diff options
Diffstat (limited to 'scene/3d')
29 files changed, 2990 insertions, 81 deletions
diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index 001c58ea76..4bff26a200 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -73,7 +73,10 @@ Vector3 ARVRCamera::project_local_ray_normal(const Point2 &p_pos) const { ERR_FAIL_NULL_V(arvr_server, Vector3()); Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface(); - ERR_FAIL_COND_V(arvr_interface.is_null(), Vector3()); + if (arvr_interface.is_null()) { + // we might be in the editor or have VR turned off, just call superclass + return Camera::project_local_ray_normal(p_pos); + } if (!is_inside_tree()) { ERR_EXPLAIN("Camera is not inside scene."); @@ -98,7 +101,10 @@ Point2 ARVRCamera::unproject_position(const Vector3 &p_pos) const { ERR_FAIL_NULL_V(arvr_server, Vector2()); Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface(); - ERR_FAIL_COND_V(arvr_interface.is_null(), Vector2()); + if (arvr_interface.is_null()) { + // we might be in the editor or have VR turned off, just call superclass + return Camera::unproject_position(p_pos); + } if (!is_inside_tree()) { ERR_EXPLAIN("Camera is not inside scene."); @@ -127,7 +133,10 @@ Vector3 ARVRCamera::project_position(const Point2 &p_point) const { ERR_FAIL_NULL_V(arvr_server, Vector3()); Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface(); - ERR_FAIL_COND_V(arvr_interface.is_null(), Vector3()); + if (arvr_interface.is_null()) { + // we might be in the editor or have VR turned off, just call superclass + return Camera::project_position(p_point); + } if (!is_inside_tree()) { ERR_EXPLAIN("Camera is not inside scene."); @@ -157,7 +166,10 @@ Vector<Plane> ARVRCamera::get_frustum() const { ERR_FAIL_NULL_V(arvr_server, Vector<Plane>()); Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface(); - ERR_FAIL_COND_V(arvr_interface.is_null(), Vector<Plane>()); + if (arvr_interface.is_null()) { + // we might be in the editor or have VR turned off, just call superclass + return Camera::get_frustum(); + } ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index e7b3645001..5f0ac3dd80 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -35,11 +35,8 @@ #include "scene/main/viewport.h" void AudioStreamPlayer3D::_mix_audio() { - if (!stream_playback.is_valid()) { - return; - } - - if (!active) { + if (!stream_playback.is_valid() || !active || + (stream_paused && !stream_paused_fade_out)) { return; } @@ -54,8 +51,13 @@ void AudioStreamPlayer3D::_mix_audio() { AudioFrame *buffer = mix_buffer.ptrw(); int buffer_size = mix_buffer.size(); - //mix - if (output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX) { + if (stream_paused_fade_out) { + // Short fadeout ramp + buffer_size = MIN(buffer_size, 128); + } + + // Mix if we're not paused or we're fading out + if ((output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) { float output_pitch_scale = 0.0; if (output_count) { @@ -105,8 +107,10 @@ void AudioStreamPlayer3D::_mix_audio() { int buffers = AudioServer::get_singleton()->get_channel_count(); for (int k = 0; k < buffers; k++) { - AudioFrame vol_inc = (current.vol[k] - prev_outputs[i].vol[k]) / float(buffer_size); - AudioFrame vol = current.vol[k]; + AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k]; + AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k]; + AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size); + AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k]; AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k); @@ -188,6 +192,8 @@ void AudioStreamPlayer3D::_mix_audio() { } output_ready = false; + stream_paused_fade_in = false; + stream_paused_fade_out = false; } float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { @@ -237,6 +243,18 @@ void AudioStreamPlayer3D::_notification(int p_what) { AudioServer::get_singleton()->remove_callback(_mix_audios, this); } + + if (p_what == NOTIFICATION_PAUSED) { + if (!can_process()) { + // Node can't process so we start fading out to silence + set_stream_paused(true); + } + } + + if (p_what == NOTIFICATION_UNPAUSED) { + set_stream_paused(false); + } + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { @@ -552,7 +570,6 @@ void AudioStreamPlayer3D::_notification(int p_what) { void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) { - ERR_FAIL_COND(!p_stream.is_valid()); AudioServer::get_singleton()->lock(); mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); @@ -564,14 +581,15 @@ void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) { setseek = -1; } - stream = p_stream; - stream_playback = p_stream->instance_playback(); + if (p_stream.is_valid()) { + stream = p_stream; + stream_playback = p_stream->instance_playback(); + } AudioServer::get_singleton()->unlock(); - if (stream_playback.is_null()) { + if (p_stream.is_valid() && stream_playback.is_null()) { stream.unref(); - ERR_FAIL_COND(stream_playback.is_null()); } } @@ -825,6 +843,20 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking() return doppler_tracking; } +void AudioStreamPlayer3D::set_stream_paused(bool p_pause) { + + if (p_pause != stream_paused) { + stream_paused = p_pause; + stream_paused_fade_in = stream_paused ? false : true; + stream_paused_fade_out = stream_paused ? true : false; + } +} + +bool AudioStreamPlayer3D::get_stream_paused() const { + + return stream_paused; +} + void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream); @@ -888,6 +920,9 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &AudioStreamPlayer3D::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &AudioStreamPlayer3D::get_doppler_tracking); + ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused); + ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused); + ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer3D::_bus_layout_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); @@ -898,6 +933,7 @@ void AudioStreamPlayer3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,4096,1,or_greater"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); @@ -949,9 +985,13 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() { attenuation_filter_db = -24; out_of_range_mode = OUT_OF_RANGE_MIX; doppler_tracking = DOPPLER_TRACKING_DISABLED; + stream_paused = false; + stream_paused_fade_in = false; + stream_paused_fade_out = false; velocity_tracker.instance(); AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); + set_disable_scale(true); } AudioStreamPlayer3D::~AudioStreamPlayer3D() { } diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 1fcb83cf21..14413d0702 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -108,6 +108,9 @@ private: float max_db; float pitch_scale; bool autoplay; + bool stream_paused; + bool stream_paused_fade_in; + bool stream_paused_fade_out; StringName bus; void _mix_audio(); @@ -199,6 +202,9 @@ public: void set_doppler_tracking(DopplerTracking p_tracking); DopplerTracking get_doppler_tracking() const; + void set_stream_paused(bool p_pause); + bool get_stream_paused() const; + AudioStreamPlayer3D(); ~AudioStreamPlayer3D(); }; diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index 204aaef7ec..26fd5ed658 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -811,4 +811,5 @@ BakedLightmap::BakedLightmap() { propagation = 1; hdr = false; image_path = "."; + set_disable_scale(true); } diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index e11e8abe5b..9a2046991b 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -613,6 +613,7 @@ Camera::Camera() { velocity_tracker.instance(); doppler_tracking = DOPPLER_TRACKING_DISABLED; set_notify_transform(true); + set_disable_scale(true); } Camera::~Camera() { diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h index f31d65e411..f8ef04b78f 100644 --- a/scene/3d/collision_object.h +++ b/scene/3d/collision_object.h @@ -39,6 +39,7 @@ class CollisionObject : public Spatial { GDCLASS(CollisionObject, Spatial); bool area; + RID rid; struct ShapeData { diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h index c9c91a5824..6ca8e80ea1 100644 --- a/scene/3d/collision_shape.h +++ b/scene/3d/collision_shape.h @@ -49,6 +49,7 @@ class CollisionShape : public Spatial { void resource_changed(RES res); bool disabled; +protected: void _create_debug_shape(); void _update_in_shape_owner(bool p_xform_only = false); diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp new file mode 100644 index 0000000000..c3b0196316 --- /dev/null +++ b/scene/3d/cpu_particles.cpp @@ -0,0 +1,1417 @@ +#include "cpu_particles.h" + +#include "particles.h" +#include "scene/3d/camera.h" +#include "scene/main/viewport.h" +#include "scene/resources/surface_tool.h" +#include "servers/visual_server.h" + +AABB CPUParticles::get_aabb() const { + + return AABB(); +} +PoolVector<Face3> CPUParticles::get_faces(uint32_t p_usage_flags) const { + + return PoolVector<Face3>(); +} + +void CPUParticles::set_emitting(bool p_emitting) { + + emitting = p_emitting; + if (!is_processing_internal()) { + 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_REDRAW_FRAME_IF_VISIBLE, true); + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif + } + } +} + +void CPUParticles::set_amount(int p_amount) { + + ERR_FAIL_COND(p_amount < 1); + + particles.resize(p_amount); + { + PoolVector<Particle>::Write w = particles.write(); + + for (int i = 0; i < p_amount; i++) { + w[i].active = false; + } + } + + particle_data.resize((12 + 4 + 1) * p_amount); + VS::get_singleton()->multimesh_allocate(multimesh, p_amount, VS::MULTIMESH_TRANSFORM_3D, VS::MULTIMESH_COLOR_8BIT, VS::MULTIMESH_CUSTOM_DATA_FLOAT); + + particle_order.resize(p_amount); +} +void CPUParticles::set_lifetime(float p_lifetime) { + + ERR_FAIL_COND(p_lifetime <= 0); + lifetime = p_lifetime; +} + +void CPUParticles::set_one_shot(bool p_one_shot) { + + one_shot = p_one_shot; +} + +void CPUParticles::set_pre_process_time(float p_time) { + + pre_process_time = p_time; +} +void CPUParticles::set_explosiveness_ratio(float p_ratio) { + + explosiveness_ratio = p_ratio; +} +void CPUParticles::set_randomness_ratio(float p_ratio) { + + randomness_ratio = p_ratio; +} +void CPUParticles::set_use_local_coordinates(bool p_enable) { + + local_coords = p_enable; +} +void CPUParticles::set_speed_scale(float p_scale) { + + speed_scale = p_scale; +} + +bool CPUParticles::is_emitting() const { + + return emitting; +} +int CPUParticles::get_amount() const { + + return particles.size(); +} +float CPUParticles::get_lifetime() const { + + return lifetime; +} +bool CPUParticles::get_one_shot() const { + + return one_shot; +} + +float CPUParticles::get_pre_process_time() const { + + return pre_process_time; +} +float CPUParticles::get_explosiveness_ratio() const { + + return explosiveness_ratio; +} +float CPUParticles::get_randomness_ratio() const { + + return randomness_ratio; +} + +bool CPUParticles::get_use_local_coordinates() const { + + return local_coords; +} + +float CPUParticles::get_speed_scale() const { + + return speed_scale; +} + +void CPUParticles::set_draw_order(DrawOrder p_order) { + + draw_order = p_order; +} + +CPUParticles::DrawOrder CPUParticles::get_draw_order() const { + + return draw_order; +} + +void CPUParticles::set_mesh(const Ref<Mesh> &p_mesh) { + + mesh = p_mesh; + if (mesh.is_valid()) { + VS::get_singleton()->multimesh_set_mesh(multimesh, mesh->get_rid()); + } else { + VS::get_singleton()->multimesh_set_mesh(multimesh, RID()); + } +} + +Ref<Mesh> CPUParticles::get_mesh() const { + + return mesh; +} + +void CPUParticles::set_fixed_fps(int p_count) { + fixed_fps = p_count; +} + +int CPUParticles::get_fixed_fps() const { + return fixed_fps; +} + +void CPUParticles::set_fractional_delta(bool p_enable) { + fractional_delta = p_enable; +} + +bool CPUParticles::get_fractional_delta() const { + return fractional_delta; +} + +String CPUParticles::get_configuration_warning() const { + + String warnings; + + return warnings; +} + +void CPUParticles::restart() { + + time = 0; + inactive_time = 0; + frame_remainder = 0; + cycle = 0; + + { + int pc = particles.size(); + PoolVector<Particle>::Write w = particles.write(); + + for (int i = 0; i < pc; i++) { + w[i].active = false; + } + } +} + +void CPUParticles::set_spread(float p_spread) { + + spread = p_spread; +} + +float CPUParticles::get_spread() const { + + return spread; +} + +void CPUParticles::set_flatness(float p_flatness) { + + flatness = p_flatness; +} +float CPUParticles::get_flatness() const { + + return flatness; +} + +void CPUParticles::set_param(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + parameters[p_param] = p_value; +} +float CPUParticles::get_param(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); + + return parameters[p_param]; +} + +void CPUParticles::set_param_randomness(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + randomness[p_param] = p_value; +} +float CPUParticles::get_param_randomness(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); + + return randomness[p_param]; +} + +static void _adjust_curve_range(const Ref<Curve> &p_curve, float p_min, float p_max) { + + Ref<Curve> curve = p_curve; + if (!curve.is_valid()) + return; + + curve->ensure_default_setup(p_min, p_max); +} + +void CPUParticles::set_param_curve(Parameter p_param, const Ref<Curve> &p_curve) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + curve_parameters[p_param] = p_curve; + + switch (p_param) { + case PARAM_INITIAL_LINEAR_VELOCITY: { + //do none for this one + } break; + case PARAM_ANGULAR_VELOCITY: { + _adjust_curve_range(p_curve, -360, 360); + } break; + /*case PARAM_ORBIT_VELOCITY: { + _adjust_curve_range(p_curve, -500, 500); + } break;*/ + case PARAM_LINEAR_ACCEL: { + _adjust_curve_range(p_curve, -200, 200); + } break; + case PARAM_RADIAL_ACCEL: { + _adjust_curve_range(p_curve, -200, 200); + } break; + case PARAM_TANGENTIAL_ACCEL: { + _adjust_curve_range(p_curve, -200, 200); + } break; + case PARAM_DAMPING: { + _adjust_curve_range(p_curve, 0, 100); + } break; + case PARAM_ANGLE: { + _adjust_curve_range(p_curve, -360, 360); + } break; + case PARAM_SCALE: { + + } break; + case PARAM_HUE_VARIATION: { + _adjust_curve_range(p_curve, -1, 1); + } break; + case PARAM_ANIM_SPEED: { + _adjust_curve_range(p_curve, 0, 200); + } break; + case PARAM_ANIM_OFFSET: { + } break; + default: {} + } +} +Ref<Curve> CPUParticles::get_param_curve(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, Ref<Curve>()); + + return curve_parameters[p_param]; +} + +void CPUParticles::set_color(const Color &p_color) { + + color = p_color; +} + +Color CPUParticles::get_color() const { + + return color; +} + +void CPUParticles::set_color_ramp(const Ref<Gradient> &p_ramp) { + + color_ramp = p_ramp; +} + +Ref<Gradient> CPUParticles::get_color_ramp() const { + + return color_ramp; +} + +void CPUParticles::set_particle_flag(Flags p_flag, bool p_enable) { + ERR_FAIL_INDEX(p_flag, FLAG_MAX); + flags[p_flag] = p_enable; + if (p_flag == FLAG_DISABLE_Z) { + _change_notify(); + } +} + +bool CPUParticles::get_particle_flag(Flags p_flag) const { + ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); + return flags[p_flag]; +} + +void CPUParticles::set_emission_shape(EmissionShape p_shape) { + + emission_shape = p_shape; +} + +void CPUParticles::set_emission_sphere_radius(float p_radius) { + + emission_sphere_radius = p_radius; +} + +void CPUParticles::set_emission_box_extents(Vector3 p_extents) { + + emission_box_extents = p_extents; +} + +void CPUParticles::set_emission_points(const PoolVector<Vector3> &p_points) { + + emission_points = p_points; +} + +void CPUParticles::set_emission_normals(const PoolVector<Vector3> &p_normals) { + + emission_normals = p_normals; +} + +void CPUParticles::set_emission_colors(const PoolVector<Color> &p_colors) { + + emission_colors = p_colors; +} + +float CPUParticles::get_emission_sphere_radius() const { + + return emission_sphere_radius; +} +Vector3 CPUParticles::get_emission_box_extents() const { + + return emission_box_extents; +} +PoolVector<Vector3> CPUParticles::get_emission_points() const { + + return emission_points; +} +PoolVector<Vector3> CPUParticles::get_emission_normals() const { + + return emission_normals; +} + +PoolVector<Color> CPUParticles::get_emission_colors() const { + + return emission_colors; +} + +CPUParticles::EmissionShape CPUParticles::get_emission_shape() const { + return emission_shape; +} +void CPUParticles::set_gravity(const Vector3 &p_gravity) { + + gravity = p_gravity; +} + +Vector3 CPUParticles::get_gravity() const { + + return gravity; +} + +void CPUParticles::_validate_property(PropertyInfo &property) const { + + if (property.name == "color" && color_ramp.is_valid()) { + property.usage = 0; + } + + if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { + property.usage = 0; + } + + if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { + property.usage = 0; + } + + if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { + property.usage = 0; + } + + 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) { + + x = ((x >> uint32_t(16)) ^ x) * uint32_t(0x45d9f3b); + x = ((x >> uint32_t(16)) ^ x) * uint32_t(0x45d9f3b); + x = (x >> uint32_t(16)) ^ x; + return x; +} + +static float rand_from_seed(uint32_t &seed) { + int k; + int s = int(seed); + if (s == 0) + s = 305420679; + k = s / 127773; + s = 16807 * (s - k * 127773) - 2836 * k; + if (s < 0) + s += 2147483647; + seed = uint32_t(s); + return float(seed % uint32_t(65536)) / 65535.0; +} + +float rand_from_seed_m1_p1(uint32_t &seed) { + return rand_from_seed(seed) * 2.0 - 1.0; +} + +void CPUParticles::_particles_process(float p_delta) { + + p_delta *= speed_scale; + + int pcount = particles.size(); + PoolVector<Particle>::Write w = particles.write(); + + Particle *parray = w.ptr(); + + float prev_time = time; + time += p_delta; + if (time > lifetime) { + time = Math::fmod(time, lifetime); + cycle++; + if (one_shot && cycle > 0) { + emitting = false; + } + } + + Transform emission_xform; + Basis velocity_xform; + if (!local_coords) { + emission_xform = get_global_transform(); + velocity_xform = emission_xform.basis.inverse().transposed(); + } + + for (int i = 0; i < pcount; i++) { + + Particle &p = parray[i]; + + if (!emitting && !p.active) + continue; + + float restart_time = (float(i) / float(pcount)) * lifetime; + float local_delta = p_delta; + + if (randomness_ratio > 0.0) { + uint32_t seed = cycle; + if (restart_time >= time) { + 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_time *= (1.0 - explosiveness_ratio); + bool restart = false; + + if (time > prev_time) { + // restart_time >= prev_time is used so particles emit in the first frame they are processed + + if (restart_time >= prev_time && restart_time < time) { + restart = true; + if (fractional_delta) { + local_delta = (time - restart_time) * lifetime; + } + } + + } else if (local_delta > 0.0) { + if (restart_time >= prev_time) { + restart = true; + if (fractional_delta) { + local_delta = (1.0 - restart_time + time) * lifetime; + } + + } else if (restart_time < time) { + restart = true; + if (fractional_delta) { + local_delta = (time - restart_time) * lifetime; + } + } + } + + if (restart) { + + if (!emitting) { + p.active = false; + continue; + } + p.active = true; + + /*float tex_linear_velocity = 0; + if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); + }*/ + + float tex_angle = 0.0; + if (curve_parameters[PARAM_ANGLE].is_valid()) { + tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(0); + } + + float tex_anim_offset = 0.0; + if (curve_parameters[PARAM_ANGLE].is_valid()) { + tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(0); + } + + p.seed = Math::rand(); + + p.angle_rand = Math::randf(); + p.scale_rand = Math::randf(); + 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; + 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; + + 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)); + direction_yz.z = direction_yz.z / Math::sqrt(direction_yz.z); //better uniform distribution + Vector3 direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z); + direction.normalize(); + p.velocity = direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + } + + float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); + p.custom[0] = Math::deg2rad(base_angle); //angle + p.custom[1] = 0.0; //phase + p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1) + p.transform = Transform(); + p.time = 0; + p.base_color = Color(1, 1, 1, 1); + + switch (emission_shape) { + case EMISSION_SHAPE_POINT: { + //do none + } break; + case EMISSION_SHAPE_SPHERE: { + p.transform.origin = Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0).normalized() * emission_sphere_radius; + } break; + case EMISSION_SHAPE_BOX: { + p.transform.origin = Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * emission_box_extents; + } break; + case EMISSION_SHAPE_POINTS: + case EMISSION_SHAPE_DIRECTED_POINTS: { + + int pc = emission_points.size(); + if (pc == 0) + break; + + int random_idx = Math::rand() % pc; + + p.transform.origin = emission_points.get(random_idx); + + if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) { + if (flags[FLAG_DISABLE_Z]) { + /* + mat2 rotm; + "; + rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy; + rotm[1] = rotm[0].yx * vec2(1.0, -1.0); + VELOCITY.xy = rotm * VELOCITY.xy; + */ + } else { + Vector3 normal = emission_normals.get(random_idx); + Vector3 v0 = Math::abs(normal.z) < 0.999 ? Vector3(0.0, 0.0, 1.0) : Vector3(0, 1.0, 0.0); + Vector3 tangent = v0.cross(normal).normalized(); + Vector3 bitangent = tangent.cross(normal).normalized(); + Basis m3; + m3.set_axis(0, tangent); + m3.set_axis(1, bitangent); + m3.set_axis(2, normal); + p.velocity = m3.xform(p.velocity); + } + } + + if (emission_colors.size() == pc) { + p.base_color = emission_colors.get(random_idx); + } + } break; + } + + if (!local_coords) { + p.velocity = velocity_xform.xform(p.velocity); + p.transform = emission_xform * p.transform; + } + + if (flags[FLAG_DISABLE_Z]) { + p.velocity.z = 0.0; + p.velocity.z = 0.0; + } + + } else if (!p.active) { + continue; + } else { + + uint32_t alt_seed = p.seed; + + p.time += local_delta; + p.custom[1] = p.time / lifetime; + + float tex_linear_velocity = 0.0; + 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; + + 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]); + } + } +*/ + 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]); + } + + float tex_linear_accel = 0.0; + if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { + tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(p.custom[1]); + } + + float tex_tangential_accel = 0.0; + if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { + tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(p.custom[1]); + } + + float tex_radial_accel = 0.0; + if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { + tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(p.custom[1]); + } + + float tex_damping = 0.0; + if (curve_parameters[PARAM_DAMPING].is_valid()) { + tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(p.custom[1]); + } + + float tex_angle = 0.0; + if (curve_parameters[PARAM_ANGLE].is_valid()) { + tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(p.custom[1]); + } + float tex_anim_speed = 0.0; + if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { + tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(p.custom[1]); + } + + float tex_anim_offset = 0.0; + if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { + tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(p.custom[1]); + } + + Vector3 force = gravity; + Vector3 pos = p.transform.origin; + if (flags[FLAG_DISABLE_Z]) { + pos.z = 0.0; + } + //apply linear acceleration + force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector3(); + //apply radial acceleration + Vector3 org = emission_xform.origin; + Vector3 diff = pos - org; + force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3(); + //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(); + + } else { + Vector3 crossDiff = diff.normalized().cross(gravity.normalized()); + force += crossDiff.length() > 0.0 ? crossDiff.normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + } + //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); + 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; + } + } +#endif + if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { + p.velocity = p.velocity.normalized() * tex_linear_velocity; + } + if (parameters[PARAM_DAMPING] + tex_damping > 0.0) { + + float v = p.velocity.length(); + float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]); + v -= damp * local_delta; + if (v < 0.0) { + p.velocity = Vector3(); + } else { + p.velocity = p.velocity.normalized() * v; + } + } + float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); + base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); + p.custom[0] = Math::deg2rad(base_angle); //angle + p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle + if (flags[FLAG_ANIM_LOOP]) { + p.custom[2] = Math::fmod(p.custom[2], 1.0f); //loop + + } else { + p.custom[2] = CLAMP(p.custom[2], 0.0f, 1.0); //0 to 1 only + } + } + //apply color + //apply hue rotation + + float tex_scale = 1.0; + if (curve_parameters[PARAM_SCALE].is_valid()) { + tex_scale = curve_parameters[PARAM_SCALE]->interpolate(p.custom[1]); + } + + float tex_hue_variation = 0.0; + if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { + tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(p.custom[1]); + } + + float hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_PI * 2.0 * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]); + float hue_rot_c = Math::cos(hue_rot_angle); + float hue_rot_s = Math::sin(hue_rot_angle); + + Basis hue_rot_mat; + { + Basis mat1(0.299, 0.587, 0.114, 0.299, 0.587, 0.114, 0.299, 0.587, 0.114); + Basis mat2(0.701, -0.587, -0.114, -0.299, 0.413, -0.114, -0.300, -0.588, 0.886); + Basis mat3(0.168, 0.330, -0.497, -0.328, 0.035, 0.292, 1.250, -1.050, -0.203); + + for (int j = 0; j < 3; j++) { + hue_rot_mat[j] = mat1[j] + mat2[j] * hue_rot_c + mat3[j] * hue_rot_s; + } + } + + if (color_ramp.is_valid()) { + p.color = color_ramp->get_color_at_offset(p.custom[1]) * color; + } else { + p.color = color; + } + + Vector3 color_rgb = hue_rot_mat.xform_inv(Vector3(p.color.r, p.color.g, p.color.b)); + p.color.r = color_rgb.x; + p.color.g = color_rgb.y; + p.color.b = color_rgb.z; + + p.color *= p.base_color; + + if (flags[FLAG_DISABLE_Z]) { + + if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (p.velocity.length() > 0.0) { + p.transform.basis.set_axis(1, p.velocity.normalized()); + } else { + p.transform.basis.set_axis(1, p.transform.basis.get_axis(1)); + } + p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); + p.transform.basis.set_axis(2, Vector3(0, 0, 1)); + + } else { + p.transform.basis.set_axis(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0)); + p.transform.basis.set_axis(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0)); + p.transform.basis.set_axis(2, Vector3(0, 0, 1)); + } + + } else { + //orient particle Y towards velocity + if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (p.velocity.length() > 0.0) { + p.transform.basis.set_axis(1, p.velocity.normalized()); + } else { + p.transform.basis.set_axis(1, p.transform.basis.get_axis(1).normalized()); + } + if (p.transform.basis.get_axis(1) == p.transform.basis.get_axis(0)) { + p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); + p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized()); + } else { + p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized()); + p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); + } + } else { + p.transform.basis.orthonormalize(); + } + + //turn particle by rotation in Y + if (flags[FLAG_ROTATE_Y]) { + Basis rot_y(Vector3(0, 1, 0), p.custom[0]); + p.transform.basis = p.transform.basis * rot_y; + } + } + + //scale by scale + float base_scale = Math::lerp(parameters[PARAM_SCALE] * tex_scale, 1.0f, p.scale_rand * randomness[PARAM_SCALE]); + if (base_scale == 0.0) base_scale = 0.000001; + + p.transform.basis.scale(Vector3(1, 1, 1) * base_scale); + + if (flags[FLAG_DISABLE_Z]) { + p.velocity.z = 0.0; + p.transform.origin.z = 0.0; + } + + p.transform.origin += p.velocity * local_delta; + } +} + +void CPUParticles::_update_particle_data_buffer() { +#ifndef NO_THREADS + update_mutex->lock(); +#endif + + { + + int pc = particles.size(); + + PoolVector<int>::Write ow; + int *order = NULL; + + PoolVector<float>::Write w = particle_data.write(); + 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(); + + for (int i = 0; i < pc; i++) { + order[i] = i; + } + if (draw_order == DRAW_ORDER_LIFETIME) { + SortArray<int, SortLifetime> sorter; + sorter.compare.particles = r.ptr(); + sorter.sort(order, pc); + } else if (draw_order == DRAW_ORDER_VIEW_DEPTH) { + Camera *c = get_viewport()->get_camera(); + if (c) { + Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close + + if (local_coords) { + dir = un_transform.basis.xform(dir).normalized(); + } + + SortArray<int, SortAxis> sorter; + sorter.compare.particles = r.ptr(); + sorter.compare.axis = dir; + sorter.sort(order, pc); + } + } + } + + for (int i = 0; i < pc; i++) { + + int idx = order ? order[i] : i; + + Transform t = r[idx].transform; + + if (!local_coords) { + t = un_transform * t; + } + + // print_line(" particle " + itos(i) + ": " + String(r[idx].active ? "[x]" : "[ ]") + "\n\txform " + r[idx].transform + "\n\t" + r[idx].velocity + "\n\tcolor: " + r[idx].color); + + if (r[idx].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); + } + + Color c = r[idx].color; + uint8_t *data8 = (uint8_t *)&ptr[12]; + data8[0] = CLAMP(c.r * 255.0, 0, 255); + data8[1] = CLAMP(c.g * 255.0, 0, 255); + data8[2] = CLAMP(c.b * 255.0, 0, 255); + data8[3] = CLAMP(c.a * 255.0, 0, 255); + + ptr[13] = r[idx].custom[0]; + ptr[14] = r[idx].custom[1]; + ptr[15] = r[idx].custom[2]; + ptr[16] = r[idx].custom[3]; + + ptr += 17; + } + } + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif +} + +void CPUParticles::_update_render_thread() { + +#ifndef NO_THREADS + update_mutex->lock(); +#endif + + VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data); + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif +} + +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_REDRAW_FRAME_IF_VISIBLE, true); +#ifndef NO_THREADS + update_mutex->unlock(); +#endif + } + } + + 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_REDRAW_FRAME_IF_VISIBLE, false); +#ifndef NO_THREADS + update_mutex->unlock(); +#endif + } + } + + if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) { + } + + if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + + if (particles.size() == 0) + 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_REDRAW_FRAME_IF_VISIBLE, false); + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif + //reset variables + time = 0; + inactive_time = 0; + frame_remainder = 0; + cycle = 0; + return; + } + } + + if (time == 0 && pre_process_time > 0.0) { + + float frame_time; + if (fixed_fps > 0) + frame_time = 1.0 / fixed_fps; + else + frame_time = 1.0 / 30.0; + + float todo = pre_process_time; + + while (todo >= 0) { + _particles_process(frame_time); + todo -= frame_time; + } + } + + if (fixed_fps > 0) { + float frame_time = 1.0 / fixed_fps; + float decr = frame_time; + + float ldelta = delta; + if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 + ldelta = 0.1; + } else if (ldelta <= 0.0) { //unlikely but.. + ldelta = 0.001; + } + float todo = frame_remainder + ldelta; + + while (todo >= frame_time) { + _particles_process(frame_time); + todo -= decr; + } + + frame_remainder = todo; + + } else { + _particles_process(delta); + } + + _update_particle_data_buffer(); + } +} + +void CPUParticles::convert_from_particles(Node *p_particles) { + + Particles *particles = Object::cast_to<Particles>(p_particles); + ERR_FAIL_COND(!particles); + + set_emitting(particles->is_emitting()); + set_amount(particles->get_amount()); + set_lifetime(particles->get_lifetime()); + set_one_shot(particles->get_one_shot()); + set_pre_process_time(particles->get_pre_process_time()); + set_explosiveness_ratio(particles->get_explosiveness_ratio()); + set_randomness_ratio(particles->get_randomness_ratio()); + set_use_local_coordinates(particles->get_use_local_coordinates()); + set_fixed_fps(particles->get_fixed_fps()); + set_fractional_delta(particles->get_fractional_delta()); + set_speed_scale(particles->get_speed_scale()); + set_draw_order(DrawOrder(particles->get_draw_order())); + set_mesh(particles->get_draw_pass_mesh(0)); + + Ref<ParticlesMaterial> material = particles->get_process_material(); + if (material.is_null()) + return; + + set_spread(material->get_spread()); + set_flatness(material->get_flatness()); + + set_color(material->get_color()); + + Ref<GradientTexture> gt = material->get_color_ramp(); + if (gt.is_valid()) { + set_color_ramp(gt->get_gradient()); + } + + set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY)); + set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y)); + set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z)); + set_particle_flag(FLAG_ANIM_LOOP, material->get_flag(ParticlesMaterial::FLAG_ANIM_LOOP)); + + set_emission_shape(EmissionShape(material->get_emission_shape())); + set_emission_sphere_radius(material->get_emission_sphere_radius()); + set_emission_box_extents(material->get_emission_box_extents()); + + set_gravity(material->get_gravity()); + +#define CONVERT_PARAM(m_param) \ + set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \ + { \ + Ref<CurveTexture> ctex = material->get_param_texture(ParticlesMaterial::m_param); \ + if (ctex.is_valid()) set_param_curve(m_param, ctex->get_curve()); \ + } \ + set_param_randomness(m_param, material->get_param_randomness(ParticlesMaterial::m_param)); + + CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); + CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); + // CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_LINEAR_ACCEL); + CONVERT_PARAM(PARAM_RADIAL_ACCEL); + CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); + CONVERT_PARAM(PARAM_DAMPING); + CONVERT_PARAM(PARAM_ANGLE); + CONVERT_PARAM(PARAM_SCALE); + CONVERT_PARAM(PARAM_HUE_VARIATION); + CONVERT_PARAM(PARAM_ANIM_SPEED); + CONVERT_PARAM(PARAM_ANIM_OFFSET); + +#undef CONVERT_PARAM +} + +void CPUParticles::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &CPUParticles::set_emitting); + ClassDB::bind_method(D_METHOD("set_amount", "amount"), &CPUParticles::set_amount); + ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &CPUParticles::set_lifetime); + ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &CPUParticles::set_one_shot); + ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles::set_pre_process_time); + ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &CPUParticles::set_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &CPUParticles::set_randomness_ratio); + ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &CPUParticles::set_use_local_coordinates); + ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &CPUParticles::set_fixed_fps); + ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &CPUParticles::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &CPUParticles::set_speed_scale); + + ClassDB::bind_method(D_METHOD("is_emitting"), &CPUParticles::is_emitting); + ClassDB::bind_method(D_METHOD("get_amount"), &CPUParticles::get_amount); + ClassDB::bind_method(D_METHOD("get_lifetime"), &CPUParticles::get_lifetime); + ClassDB::bind_method(D_METHOD("get_one_shot"), &CPUParticles::get_one_shot); + ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles::get_pre_process_time); + ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &CPUParticles::get_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &CPUParticles::get_randomness_ratio); + ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &CPUParticles::get_use_local_coordinates); + ClassDB::bind_method(D_METHOD("get_fixed_fps"), &CPUParticles::get_fixed_fps); + ClassDB::bind_method(D_METHOD("get_fractional_delta"), &CPUParticles::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &CPUParticles::get_speed_scale); + + ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &CPUParticles::set_draw_order); + + ClassDB::bind_method(D_METHOD("get_draw_order"), &CPUParticles::get_draw_order); + + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CPUParticles::set_mesh); + ClassDB::bind_method(D_METHOD("get_mesh"), &CPUParticles::get_mesh); + + ClassDB::bind_method(D_METHOD("restart"), &CPUParticles::restart); + + 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::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.01,64,0.01"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); + ADD_GROUP("Drawing", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + + BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX); + BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME); + BIND_ENUM_CONSTANT(DRAW_ORDER_VIEW_DEPTH); + + //////////////////////////////// + + ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &CPUParticles::set_spread); + ClassDB::bind_method(D_METHOD("get_spread"), &CPUParticles::get_spread); + + ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &CPUParticles::set_flatness); + ClassDB::bind_method(D_METHOD("get_flatness"), &CPUParticles::get_flatness); + + ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &CPUParticles::set_param); + ClassDB::bind_method(D_METHOD("get_param", "param"), &CPUParticles::get_param); + + ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &CPUParticles::set_param_randomness); + ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &CPUParticles::get_param_randomness); + + ClassDB::bind_method(D_METHOD("set_param_curve", "param", "curve"), &CPUParticles::set_param_curve); + ClassDB::bind_method(D_METHOD("get_param_curve", "param"), &CPUParticles::get_param_curve); + + ClassDB::bind_method(D_METHOD("set_color", "color"), &CPUParticles::set_color); + ClassDB::bind_method(D_METHOD("get_color"), &CPUParticles::get_color); + + ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles::set_color_ramp); + ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles::get_color_ramp); + + ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles::set_particle_flag); + ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles::get_particle_flag); + + ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &CPUParticles::set_emission_shape); + ClassDB::bind_method(D_METHOD("get_emission_shape"), &CPUParticles::get_emission_shape); + + ClassDB::bind_method(D_METHOD("set_emission_sphere_radius", "radius"), &CPUParticles::set_emission_sphere_radius); + ClassDB::bind_method(D_METHOD("get_emission_sphere_radius"), &CPUParticles::get_emission_sphere_radius); + + ClassDB::bind_method(D_METHOD("set_emission_box_extents", "extents"), &CPUParticles::set_emission_box_extents); + ClassDB::bind_method(D_METHOD("get_emission_box_extents"), &CPUParticles::get_emission_box_extents); + + ClassDB::bind_method(D_METHOD("set_emission_points", "array"), &CPUParticles::set_emission_points); + ClassDB::bind_method(D_METHOD("get_emission_points"), &CPUParticles::get_emission_points); + + ClassDB::bind_method(D_METHOD("set_emission_normals", "array"), &CPUParticles::set_emission_normals); + ClassDB::bind_method(D_METHOD("get_emission_normals"), &CPUParticles::get_emission_normals); + + ClassDB::bind_method(D_METHOD("set_emission_colors", "array"), &CPUParticles::set_emission_colors); + ClassDB::bind_method(D_METHOD("get_emission_colors"), &CPUParticles::get_emission_colors); + + ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles::get_gravity); + ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles::set_gravity); + + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles::convert_from_particles); + + ClassDB::bind_method(D_METHOD("_update_render_thread"), &CPUParticles::_update_render_thread); + + ADD_GROUP("Emission Shape", "emission_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors"); + ADD_GROUP("Flags", "flag_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_particle_flag", "get_particle_flag", FLAG_ROTATE_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_particle_flag", "get_particle_flag", FLAG_DISABLE_Z); + ADD_GROUP("Spread", ""); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness"); + ADD_GROUP("Gravity", ""); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); + ADD_GROUP("Initial Velocity", "initial_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_GROUP("Angular Velocity", "angular_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "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); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_LINEAR_ACCEL); + ADD_GROUP("Radial Accel", "radial_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_RADIAL_ACCEL); + ADD_GROUP("Tangential Accel", "tangential_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL); + ADD_GROUP("Damping", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING); + ADD_GROUP("Angle", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE); + ADD_GROUP("Scale", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_SCALE); + ADD_GROUP("Color", ""); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp"); + + ADD_GROUP("Hue Variation", "hue_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION); + ADD_GROUP("Animation", "anim_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_particle_flag", "get_particle_flag", FLAG_ANIM_LOOP); + + BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_ANGULAR_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); + BIND_ENUM_CONSTANT(PARAM_DAMPING); + BIND_ENUM_CONSTANT(PARAM_ANGLE); + BIND_ENUM_CONSTANT(PARAM_SCALE); + BIND_ENUM_CONSTANT(PARAM_HUE_VARIATION); + BIND_ENUM_CONSTANT(PARAM_ANIM_SPEED); + BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET); + BIND_ENUM_CONSTANT(PARAM_MAX); + + BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); + BIND_ENUM_CONSTANT(FLAG_MAX); + + BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); +} + +CPUParticles::CPUParticles() { + + time = 0; + inactive_time = 0; + frame_remainder = 0; + cycle = 0; + + multimesh = VisualServer::get_singleton()->multimesh_create(); + set_base(multimesh); + + set_emitting(true); + set_one_shot(false); + set_amount(8); + set_lifetime(1); + set_fixed_fps(0); + set_fractional_delta(true); + set_pre_process_time(0); + set_explosiveness_ratio(0); + set_randomness_ratio(0); + set_use_local_coordinates(true); + + set_draw_order(DRAW_ORDER_INDEX); + set_speed_scale(1); + + set_spread(45); + set_flatness(0); + set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1); + //set_param(PARAM_ORBIT_VELOCITY, 0); + set_param(PARAM_LINEAR_ACCEL, 0); + set_param(PARAM_RADIAL_ACCEL, 0); + set_param(PARAM_TANGENTIAL_ACCEL, 0); + set_param(PARAM_DAMPING, 0); + set_param(PARAM_ANGLE, 0); + set_param(PARAM_SCALE, 1); + set_param(PARAM_HUE_VARIATION, 0); + set_param(PARAM_ANIM_SPEED, 0); + set_param(PARAM_ANIM_OFFSET, 0); + set_emission_shape(EMISSION_SHAPE_POINT); + set_emission_sphere_radius(1); + set_emission_box_extents(Vector3(1, 1, 1)); + + set_gravity(Vector3(0, -9.8, 0)); + + for (int i = 0; i < PARAM_MAX; i++) { + set_param_randomness(Parameter(i), 0); + } + + for (int i = 0; i < FLAG_MAX; i++) { + flags[i] = false; + } + + set_color(Color(1, 1, 1, 1)); + +#ifndef NO_THREADS + update_mutex = Mutex::create(); +#endif +} + +CPUParticles::~CPUParticles() { + VS::get_singleton()->free(multimesh); + +#ifndef NO_THREADS + memdelete(update_mutex); +#endif +} diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h new file mode 100644 index 0000000000..1ee709719d --- /dev/null +++ b/scene/3d/cpu_particles.h @@ -0,0 +1,258 @@ +#ifndef CPU_PARTICLES_H +#define CPU_PARTICLES_H +#include "rid.h" +#include "scene/3d/visual_instance.h" +#include "scene/main/timer.h" +#include "scene/resources/material.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class CPUParticles : public GeometryInstance { +private: + GDCLASS(CPUParticles, GeometryInstance); + +public: + enum DrawOrder { + DRAW_ORDER_INDEX, + DRAW_ORDER_LIFETIME, + DRAW_ORDER_VIEW_DEPTH, + }; + + enum Parameter { + + PARAM_INITIAL_LINEAR_VELOCITY, + PARAM_ANGULAR_VELOCITY, + //PARAM_ORBIT_VELOCITY, + PARAM_LINEAR_ACCEL, + PARAM_RADIAL_ACCEL, + PARAM_TANGENTIAL_ACCEL, + PARAM_DAMPING, + PARAM_ANGLE, + PARAM_SCALE, + PARAM_HUE_VARIATION, + PARAM_ANIM_SPEED, + PARAM_ANIM_OFFSET, + PARAM_MAX + }; + + enum Flags { + FLAG_ALIGN_Y_TO_VELOCITY, + FLAG_ROTATE_Y, + FLAG_DISABLE_Z, + FLAG_ANIM_LOOP, + FLAG_MAX + }; + + enum EmissionShape { + EMISSION_SHAPE_POINT, + EMISSION_SHAPE_SPHERE, + EMISSION_SHAPE_BOX, + EMISSION_SHAPE_POINTS, + EMISSION_SHAPE_DIRECTED_POINTS, + }; + +private: + bool emitting; + + struct Particle { + Transform transform; + Color color; + float custom[4]; + Vector3 velocity; + bool active; + float angle_rand; + float scale_rand; + float hue_rot_rand; + float anim_offset_rand; + float time; + Color base_color; + + uint32_t seed; + }; + + float time; + float inactive_time; + float frame_remainder; + int cycle; + + RID multimesh; + + PoolVector<Particle> particles; + PoolVector<float> particle_data; + PoolVector<int> particle_order; + + struct SortLifetime { + const Particle *particles; + + bool operator()(int p_a, int p_b) const { + return particles[p_a].time < particles[p_b].time; + } + }; + + struct SortAxis { + const Particle *particles; + Vector3 axis; + bool operator()(int p_a, int p_b) const { + + return axis.dot(particles[p_a].transform.origin) < axis.dot(particles[p_b].transform.origin); + } + }; + + // + + bool one_shot; + + float lifetime; + float pre_process_time; + float explosiveness_ratio; + float randomness_ratio; + float speed_scale; + bool local_coords; + int fixed_fps; + bool fractional_delta; + + DrawOrder draw_order; + + Ref<Mesh> mesh; + + //////// + + float spread; + float flatness; + + float parameters[PARAM_MAX]; + float randomness[PARAM_MAX]; + + Ref<Curve> curve_parameters[PARAM_MAX]; + Color color; + Ref<Gradient> color_ramp; + + bool flags[FLAG_MAX]; + + EmissionShape emission_shape; + float emission_sphere_radius; + Vector3 emission_box_extents; + PoolVector<Vector3> emission_points; + PoolVector<Vector3> emission_normals; + PoolVector<Color> emission_colors; + int emission_point_count; + + bool anim_loop; + Vector3 gravity; + + void _particles_process(float p_delta); + void _update_particle_data_buffer(); + + Mutex *update_mutex; + + void _update_render_thread(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + virtual void _validate_property(PropertyInfo &property) const; + +public: + AABB get_aabb() const; + PoolVector<Face3> get_faces(uint32_t p_usage_flags) const; + + void set_emitting(bool p_emitting); + void set_amount(int p_amount); + void set_lifetime(float p_lifetime); + void set_one_shot(bool p_one_shot); + void set_pre_process_time(float p_time); + void set_explosiveness_ratio(float p_ratio); + void set_randomness_ratio(float p_ratio); + void set_visibility_aabb(const AABB &p_aabb); + void set_use_local_coordinates(bool p_enable); + void set_speed_scale(float p_scale); + + bool is_emitting() const; + int get_amount() const; + float get_lifetime() const; + bool get_one_shot() const; + float get_pre_process_time() const; + float get_explosiveness_ratio() const; + float get_randomness_ratio() const; + AABB get_visibility_aabb() const; + bool get_use_local_coordinates() const; + float get_speed_scale() const; + + void set_fixed_fps(int p_count); + int get_fixed_fps() const; + + void set_fractional_delta(bool p_enable); + bool get_fractional_delta() const; + + void set_draw_order(DrawOrder p_order); + DrawOrder get_draw_order() const; + + void set_draw_passes(int p_count); + int get_draw_passes() const; + + void set_mesh(const Ref<Mesh> &p_mesh); + Ref<Mesh> get_mesh() const; + + /////////////////// + + void set_spread(float p_spread); + float get_spread() const; + + void set_flatness(float p_flatness); + float get_flatness() const; + + void set_param(Parameter p_param, float p_value); + float get_param(Parameter p_param) const; + + void set_param_randomness(Parameter p_param, float p_value); + float get_param_randomness(Parameter p_param) const; + + void set_param_curve(Parameter p_param, const Ref<Curve> &p_curve); + Ref<Curve> get_param_curve(Parameter p_param) const; + + void set_color(const Color &p_color); + Color get_color() const; + + void set_color_ramp(const Ref<Gradient> &p_texture); + Ref<Gradient> get_color_ramp() const; + + void set_particle_flag(Flags p_flag, bool p_enable); + bool get_particle_flag(Flags p_flag) const; + + void set_emission_shape(EmissionShape p_shape); + void set_emission_sphere_radius(float p_radius); + void set_emission_box_extents(Vector3 p_extents); + void set_emission_points(const PoolVector<Vector3> &p_points); + void set_emission_normals(const PoolVector<Vector3> &p_normals); + void set_emission_colors(const PoolVector<Color> &p_colors); + void set_emission_point_count(int p_count); + + EmissionShape get_emission_shape() const; + float get_emission_sphere_radius() const; + Vector3 get_emission_box_extents() const; + PoolVector<Vector3> get_emission_points() const; + PoolVector<Vector3> get_emission_normals() const; + PoolVector<Color> get_emission_colors() const; + int get_emission_point_count() const; + + void set_gravity(const Vector3 &p_gravity); + Vector3 get_gravity() const; + + virtual String get_configuration_warning() const; + + void restart(); + + void convert_from_particles(Node *p_particles); + + CPUParticles(); + ~CPUParticles(); +}; + +VARIANT_ENUM_CAST(CPUParticles::DrawOrder) +VARIANT_ENUM_CAST(CPUParticles::Parameter) +VARIANT_ENUM_CAST(CPUParticles::Flags) +VARIANT_ENUM_CAST(CPUParticles::EmissionShape) + +#endif // CPU_PARTICLES_H diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp index 4ad2eb60ee..6276d02eff 100644 --- a/scene/3d/gi_probe.cpp +++ b/scene/3d/gi_probe.cpp @@ -557,6 +557,7 @@ GIProbe::GIProbe() { compress = false; gi_probe = VS::get_singleton()->gi_probe_create(); + set_disable_scale(true); } GIProbe::~GIProbe() { diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index 7c42638107..16164cf3bf 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -306,6 +306,7 @@ Light::Light(VisualServer::LightType p_type) { set_param(PARAM_SHADOW_SPLIT_3_OFFSET, 0.5); set_param(PARAM_SHADOW_NORMAL_BIAS, 0.0); set_param(PARAM_SHADOW_BIAS, 0.15); + set_disable_scale(true); } Light::Light() { diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index 80bae911d4..722eadb9ca 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -36,6 +36,7 @@ #include "scene/resources/material.h" #include "scene/scene_string_names.h" #include "skeleton.h" + bool MeshInstance::_set(const StringName &p_name, const Variant &p_value) { //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else. @@ -371,7 +372,7 @@ void MeshInstance::_bind_methods() { ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton_path", "get_skeleton_path"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path"); } MeshInstance::MeshInstance() { diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 5d359cd4d5..0dfec538f9 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -41,6 +41,7 @@ class MeshInstance : public GeometryInstance { GDCLASS(MeshInstance, GeometryInstance); +protected: Ref<Mesh> mesh; NodePath skeleton_path; diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 7b5eb8ebc3..2b3a62fcdc 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -1444,7 +1444,7 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_color_modifier", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_trail_color_modifier", "get_trail_color_modifier"); ADD_GROUP("Emission Shape", "emission_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture"); @@ -1483,7 +1483,7 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL); ADD_GROUP("Damping", ""); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param", "get_param", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING); ADD_GROUP("Angle", ""); diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 5056fb2fe4..7529a0e524 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -30,6 +30,7 @@ #include "physics_body.h" +#include "core/core_string_names.h" #include "engine.h" #include "method_bind_ext.gen.inc" #include "scene/scene_string_names.h" @@ -121,23 +122,23 @@ bool PhysicsBody::get_collision_layer_bit(int p_bit) const { void PhysicsBody::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); - PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node); - if (!physics_body) { - ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type"); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); } - ERR_FAIL_COND(!physics_body); - PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid()); + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), collision_object->get_rid()); } void PhysicsBody::remove_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); - PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node); - if (!physics_body) { - ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type"); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); } - ERR_FAIL_COND(!physics_body); - PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid()); + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } void PhysicsBody::_set_layers(uint32_t p_mask) { @@ -178,28 +179,75 @@ PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : collision_mask = 1; } +#ifndef DISABLE_DEPRECATED void StaticBody::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + } + physics_material_override->set_friction(p_friction); + _reload_physics_characteristics(); } + real_t StaticBody::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void StaticBody::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + } + physics_material_override->set_bounce(p_bounce); + _reload_physics_characteristics(); } + real_t StaticBody::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif + +void StaticBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> StaticBody::get_physics_material_override() const { + return physics_material_override; } void StaticBody::set_constant_linear_velocity(const Vector3 &p_vel) { @@ -236,24 +284,39 @@ void StaticBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody::get_bounce); + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody::_reload_physics_characteristics); + ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody::add_collision_exception_with); ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody::remove_collision_exception_with); ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); - + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); } StaticBody::StaticBody() : PhysicsBody(PhysicsServer::BODY_MODE_STATIC) { - - bounce = 0; - friction = 1; } -StaticBody::~StaticBody() { +StaticBody::~StaticBody() {} + +void StaticBody::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1); + PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, PhysicsServer::COMBINE_MODE_INHERIT); + PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, PhysicsServer::COMBINE_MODE_INHERIT); + } else { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce()); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction()); + PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce_combine_mode()); + PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction_combine_mode()); + } } void RigidBody::_body_enter_tree(ObjectID p_id) { @@ -550,28 +613,67 @@ real_t RigidBody::get_weight() const { return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)); } +#ifndef DISABLE_DEPRECATED void RigidBody::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + } + physics_material_override->set_friction(p_friction); + _reload_physics_characteristics(); } real_t RigidBody::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void RigidBody::set_bounce(real_t p_bounce) { - + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + } + physics_material_override->set_bounce(p_bounce); + _reload_physics_characteristics(); } real_t RigidBody::get_bounce() const { + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + if (physics_material_override.is_null()) { + return 0; + } - return bounce; + return physics_material_override->get_bounce(); +} +#endif + +void RigidBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> RigidBody::get_physics_material_override() const { + return physics_material_override; } void RigidBody::set_gravity_scale(real_t p_gravity_scale) { @@ -812,6 +914,11 @@ void RigidBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody::get_bounce); + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody::_reload_physics_characteristics); + ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody::set_linear_velocity); ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody::get_linear_velocity); @@ -865,6 +972,7 @@ void RigidBody::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "continuous_cd"), "set_use_continuous_collision_detection", "is_using_continuous_collision_detection"); @@ -903,9 +1011,7 @@ RigidBody::RigidBody() : mode = MODE_RIGID; - bounce = 0; mass = 1; - friction = 1; max_contacts_reported = 0; state = NULL; @@ -929,6 +1035,21 @@ RigidBody::~RigidBody() { if (contact_monitor) memdelete(contact_monitor); } + +void RigidBody::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1); + PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, PhysicsServer::COMBINE_MODE_INHERIT); + PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, PhysicsServer::COMBINE_MODE_INHERIT); + } else { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce()); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction()); + PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->get_bounce_combine_mode()); + PhysicsServer::get_singleton()->body_set_combine_mode(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->get_friction_combine_mode()); + } +} + ////////////////////////////////////////////////////// ////////////////////////// @@ -979,7 +1100,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in return colliding; } -Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { +Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { Vector3 lv = p_linear_velocity; @@ -1128,7 +1249,7 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { void KinematicBody::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody::_move, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_slides", "floor_max_angle"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(true), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true)); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move); @@ -2228,6 +2349,7 @@ void PhysicalBone::set_bounce(real_t p_bounce) { bounce = p_bounce; PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); } + real_t PhysicalBone::get_bounce() const { return bounce; diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 17d2769c79..44d6502be1 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -32,6 +32,7 @@ #define PHYSICS_BODY__H #include "scene/3d/collision_object.h" +#include "scene/resources/physics_material.h" #include "servers/physics_server.h" #include "skeleton.h" #include "vset.h" @@ -81,18 +82,22 @@ class StaticBody : public PhysicsBody { Vector3 constant_linear_velocity; Vector3 constant_angular_velocity; - real_t bounce; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; protected: static void _bind_methods(); public: +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_constant_linear_velocity(const Vector3 &p_vel); void set_constant_angular_velocity(const Vector3 &p_vel); @@ -102,6 +107,9 @@ public: StaticBody(); ~StaticBody(); + +private: + void _reload_physics_characteristics(); }; class RigidBody : public PhysicsBody { @@ -121,9 +129,8 @@ protected: PhysicsDirectBodyState *state; Mode mode; - real_t bounce; real_t mass; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; Vector3 linear_velocity; Vector3 angular_velocity; @@ -196,11 +203,16 @@ public: void set_weight(real_t p_weight); real_t get_weight() const; +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_linear_velocity(const Vector3 &p_velocity); Vector3 get_linear_velocity() const; @@ -249,6 +261,9 @@ public: RigidBody(); ~RigidBody(); + +private: + void _reload_physics_characteristics(); }; VARIANT_ENUM_CAST(RigidBody::Mode); @@ -294,7 +309,7 @@ protected: static void _bind_methods(); public: - bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision); + bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz); bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia); void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock); @@ -303,7 +318,7 @@ public: void set_safe_margin(float p_margin); float get_safe_margin() const; - Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); + Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true); bool is_on_floor() const; bool is_on_wall() const; bool is_on_ceiling() const; diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp index b2d10006f7..7988c43eab 100644 --- a/scene/3d/physics_joint.cpp +++ b/scene/3d/physics_joint.cpp @@ -154,8 +154,8 @@ void Joint::_bind_methods() { ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint::set_exclude_nodes_from_collision); ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint::get_exclude_nodes_from_collision); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a"), "set_node_a", "get_node_a"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b"), "set_node_b", "get_node_b"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject"), "set_node_a", "get_node_a"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject"), "set_node_b", "get_node_b"); ADD_PROPERTY(PropertyInfo(Variant::INT, "solver/priority", PROPERTY_HINT_RANGE, "1,8,1"), "set_solver_priority", "get_solver_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision/exclude_nodes"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision"); @@ -260,7 +260,7 @@ void HingeJoint::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_lower_limit", "lower_limit"), &HingeJoint::_set_lower_limit); ClassDB::bind_method(D_METHOD("_get_lower_limit"), &HingeJoint::_get_lower_limit); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "params/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"), "set_param", "get_param", PARAM_BIAS); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "params/bias", PROPERTY_HINT_RANGE, "0.00,0.99,0.01"), "set_param", "get_param", PARAM_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit/enable"), "set_flag", "get_flag", FLAG_USE_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit/upper", PROPERTY_HINT_RANGE, "-180,180,0.1"), "_set_upper_limit", "_get_upper_limit"); @@ -270,7 +270,7 @@ void HingeJoint::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_limit/relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param", "get_param", PARAM_LIMIT_RELAXATION); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "motor/enable"), "set_flag", "get_flag", FLAG_ENABLE_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/target_velocity", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/target_velocity", PROPERTY_HINT_RANGE, "-200,200,0.01,or_greater,or_lesser"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "motor/max_impulse", PROPERTY_HINT_RANGE, "0.01,1024,0.01"), "set_param", "get_param", PARAM_MOTOR_MAX_IMPULSE); BIND_ENUM_CONSTANT(PARAM_BIAS); diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index 7e3a87cbd4..fe522bbe97 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -241,8 +241,8 @@ void ReflectionProbe::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once,Always"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,16384,0.1,or_greater"), "set_max_distance", "get_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "extents"), "set_extents", "get_extents"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "origin_offset"), "set_origin_offset", "get_origin_offset"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "box_projection"), "set_enable_box_projection", "is_box_projection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); @@ -274,6 +274,7 @@ ReflectionProbe::ReflectionProbe() { probe = VisualServer::get_singleton()->reflection_probe_create(); VS::get_singleton()->instance_set_base(get_instance(), probe); + set_disable_scale(true); } ReflectionProbe::~ReflectionProbe() { diff --git a/scene/3d/remote_transform.cpp b/scene/3d/remote_transform.cpp index afb85f7314..2156e24cd0 100644 --- a/scene/3d/remote_transform.cpp +++ b/scene/3d/remote_transform.cpp @@ -194,7 +194,7 @@ void RemoteTransform::_bind_methods() { ClassDB::bind_method(D_METHOD("set_update_scale", "update_remote_scale"), &RemoteTransform::set_update_scale); ClassDB::bind_method(D_METHOD("get_update_scale"), &RemoteTransform::get_update_scale); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path"), "set_remote_node", "get_remote_node"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "remote_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Spatial"), "set_remote_node", "get_remote_node"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_global_coordinates"), "set_use_global_coordinates", "get_use_global_coordinates"); ADD_GROUP("Update", "update_"); diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 76d90dc6ff..8d91b6f09f 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -547,6 +547,8 @@ void Skeleton::localize_rests() { } } +#ifndef _3D_DISABLED + void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) { ERR_FAIL_INDEX(p_bone, bones.size()); ERR_FAIL_COND(bones[p_bone].physical_bone); @@ -691,6 +693,8 @@ void Skeleton::physical_bones_remove_collision_exception(RID p_exception) { _physical_bones_add_remove_collision_exception(false, this, p_exception); } +#endif // _3D_DISABLED + void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone); @@ -727,11 +731,15 @@ void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform); +#ifndef _3D_DISABLED + ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation); ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton::physical_bones_start_simulation_on, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton::physical_bones_add_collision_exception); ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton::physical_bones_remove_collision_exception); +#endif // _3D_DISABLED + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index dad11960a5..9672acb57a 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -38,7 +38,10 @@ @author Juan Linietsky <reduzio@gmail.com> */ +#ifndef _3D_DISABLED class PhysicalBone; +#endif // _3D_DISABLED + class Skeleton : public Spatial { GDCLASS(Skeleton, Spatial); @@ -64,8 +67,10 @@ class Skeleton : public Spatial { Transform transform_final; +#ifndef _3D_DISABLED PhysicalBone *physical_bone; PhysicalBone *cache_parent_physical_bone; +#endif // _3D_DISABLED List<uint32_t> nodes_bound; @@ -75,8 +80,10 @@ class Skeleton : public Spatial { ignore_animation = false; custom_pose_enable = false; disable_rest = false; +#ifndef _3D_DISABLED physical_bone = NULL; cache_parent_physical_bone = NULL; +#endif // _3D_DISABLED } }; @@ -164,6 +171,7 @@ public: void localize_rests(); // used for loaders and tools +#ifndef _3D_DISABLED // Physical bone API void bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone); @@ -182,6 +190,7 @@ public: void physical_bones_start_simulation_on(const Array &p_bones); void physical_bones_add_collision_exception(RID p_exception); void physical_bones_remove_collision_exception(RID p_exception); +#endif // _3D_DISABLED public: Skeleton(); diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp new file mode 100644 index 0000000000..8498dc34c0 --- /dev/null +++ b/scene/3d/soft_body.cpp @@ -0,0 +1,740 @@ +/*************************************************************************/ +/* soft_physics_body.cpp */ +/* Author: AndreaCatania */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "soft_body.h" +#include "os/os.h" +#include "scene/3d/collision_object.h" +#include "scene/3d/skeleton.h" +#include "servers/physics_server.h" + +SoftBodyVisualServerHandler::SoftBodyVisualServerHandler() {} + +void SoftBodyVisualServerHandler::prepare(RID p_mesh, int p_surface) { + clear(); + + ERR_FAIL_COND(!p_mesh.is_valid()); + + mesh = p_mesh; + surface = p_surface; + + const uint32_t surface_format = VS::get_singleton()->mesh_surface_get_format(mesh, surface); + const int surface_vertex_len = VS::get_singleton()->mesh_surface_get_array_len(mesh, p_surface); + const int surface_index_len = VS::get_singleton()->mesh_surface_get_array_index_len(mesh, p_surface); + uint32_t surface_offsets[VS::ARRAY_MAX]; + + buffer = VS::get_singleton()->mesh_surface_get_array(mesh, surface); + stride = VS::get_singleton()->mesh_surface_make_offsets_from_format(surface_format, surface_vertex_len, surface_index_len, surface_offsets); + offset_vertices = surface_offsets[VS::ARRAY_VERTEX]; + offset_normal = surface_offsets[VS::ARRAY_NORMAL]; +} + +void SoftBodyVisualServerHandler::clear() { + + if (mesh.is_valid()) { + buffer.resize(0); + } + + mesh = RID(); +} + +void SoftBodyVisualServerHandler::open() { + write_buffer = buffer.write(); +} + +void SoftBodyVisualServerHandler::close() { + write_buffer = PoolVector<uint8_t>::Write(); +} + +void SoftBodyVisualServerHandler::commit_changes() { + VS::get_singleton()->mesh_surface_update_region(mesh, surface, 0, buffer); +} + +void SoftBodyVisualServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { + copymem(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); +} + +void SoftBodyVisualServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { + copymem(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); +} + +void SoftBodyVisualServerHandler::set_aabb(const AABB &p_aabb) { + VS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb); +} + +SoftBody::PinnedPoint::PinnedPoint() : + point_index(-1), + spatial_attachment(NULL) { +} + +SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { + point_index = obj_tocopy.point_index; + spatial_attachment_path = obj_tocopy.spatial_attachment_path; + spatial_attachment = obj_tocopy.spatial_attachment; + vertex_offset_transform = obj_tocopy.vertex_offset_transform; +} + +void SoftBody::_update_pickable() { + if (!is_inside_tree()) + return; + bool pickable = ray_pickable && is_inside_tree() && is_visible_in_tree(); + PhysicsServer::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable); +} + +bool SoftBody::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + String which = name.get_slicec('/', 0); + + if ("pinned_points" == which) { + + return _set_property_pinned_points_indices(p_value); + + } else if ("attachments" == which) { + + int idx = name.get_slicec('/', 1).to_int(); + String what = name.get_slicec('/', 2); + + return _set_property_pinned_points_attachment(idx, what, p_value); + } + + return false; +} + +bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + String which = name.get_slicec('/', 0); + + if ("pinned_points" == which) { + Array arr_ret; + const int pinned_points_indices_size = pinned_points_indices.size(); + PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + arr_ret.resize(pinned_points_indices_size); + + for (int i = 0; i < pinned_points_indices_size; ++i) { + arr_ret[i] = r[i].point_index; + } + + r_ret = arr_ret; + return true; + + } else if ("attachments" == which) { + + int idx = name.get_slicec('/', 1).to_int(); + String what = name.get_slicec('/', 2); + + return _get_property_pinned_points(idx, what, r_ret); + } + + return false; +} + +void SoftBody::_get_property_list(List<PropertyInfo> *p_list) const { + + const int pinned_points_indices_size = pinned_points_indices.size(); + + p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "pinned_points")); + + for (int i = 0; i < pinned_points_indices_size; ++i) { + p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index")); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path")); + } +} + +bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) { + + const int p_indices_size = p_indices.size(); + + { // Remove the pined points on physics server that will be removed by resize + PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + if (p_indices_size < pinned_points_indices.size()) { + for (int i = pinned_points_indices.size() - 1; i >= p_indices_size; --i) { + pin_point(r[i].point_index, false); + } + } + } + + pinned_points_indices.resize(p_indices_size); + + PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); + int point_index; + for (int i = 0; i < p_indices_size; ++i) { + point_index = p_indices.get(i); + if (w[i].point_index != point_index) { + if (-1 != w[i].point_index) + pin_point(w[i].point_index, false); + w[i].point_index = point_index; + pin_point(w[i].point_index, true); + } + } + return true; +} + +bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { + if (pinned_points_indices.size() <= p_item) { + return false; + } + + if ("spatial_attachment_path" == p_what) { + PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); + pin_point(w[p_item].point_index, true, p_value); + } else { + return false; + } + + return true; +} + +bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { + if (pinned_points_indices.size() <= p_item) { + return false; + } + PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + + if ("point_index" == p_what) { + r_ret = r[p_item].point_index; + } else if ("spatial_attachment_path" == p_what) { + r_ret = r[p_item].spatial_attachment_path; + } else { + return false; + } + + return true; +} + +void SoftBody::_changed_callback(Object *p_changed, const char *p_prop) { +#ifdef TOOLS_ENABLED + if (p_changed == this) { + update_configuration_warning(); + } +#endif +} + +void SoftBody::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_WORLD: { + + if (Engine::get_singleton()->is_editor_hint()) + add_change_receptor(this); + + RID space = get_world()->get_space(); + PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, space); + PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); + update_physics_server(); + } break; + case NOTIFICATION_READY: { + if (!parent_collision_ignore.is_empty()) + add_collision_exception_with(get_node(parent_collision_ignore)); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (!simulation_started) { + PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); + + _update_cache_pin_points_datas(); + // Submit bone attachment + const int pinned_points_indices_size = pinned_points_indices.size(); + PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + for (int i = 0; i < pinned_points_indices_size; ++i) { + if (!r[i].spatial_attachment) { + // Use soft body position to update the point position + PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (get_global_transform() * r[i].vertex_offset_transform).origin); + } else { + PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (r[i].spatial_attachment->get_global_transform() * r[i].vertex_offset_transform).origin); + } + } + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + + _update_pickable(); + + } break; + case NOTIFICATION_EXIT_WORLD: { + + PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, RID()); + + } break; + } + +#ifdef TOOLS_ENABLED + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warning(); + } + } + +#endif +} + +void SoftBody::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_draw_soft_mesh"), &SoftBody::_draw_soft_mesh); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody::get_collision_mask); + + ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody::get_collision_layer); + + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody::get_collision_mask_bit); + + ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody::set_collision_layer_bit); + ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody::get_collision_layer_bit); + + ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody::set_parent_collision_ignore); + ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody::get_parent_collision_ignore); + + ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody::add_collision_exception_with); + ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody::remove_collision_exception_with); + + ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody::set_simulation_precision); + ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody::get_simulation_precision); + + ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody::set_total_mass); + ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody::get_total_mass); + + ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody::set_linear_stiffness); + ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody::get_linear_stiffness); + + ClassDB::bind_method(D_METHOD("set_areaAngular_stiffness", "areaAngular_stiffness"), &SoftBody::set_areaAngular_stiffness); + ClassDB::bind_method(D_METHOD("get_areaAngular_stiffness"), &SoftBody::get_areaAngular_stiffness); + + ClassDB::bind_method(D_METHOD("set_volume_stiffness", "volume_stiffness"), &SoftBody::set_volume_stiffness); + ClassDB::bind_method(D_METHOD("get_volume_stiffness"), &SoftBody::get_volume_stiffness); + + ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody::set_pressure_coefficient); + ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody::get_pressure_coefficient); + + ClassDB::bind_method(D_METHOD("set_pose_matching_coefficient", "pose_matching_coefficient"), &SoftBody::set_pose_matching_coefficient); + ClassDB::bind_method(D_METHOD("get_pose_matching_coefficient"), &SoftBody::get_pose_matching_coefficient); + + ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody::set_damping_coefficient); + ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody::get_damping_coefficient); + + ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody::set_drag_coefficient); + ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody::get_drag_coefficient); + + ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody::set_ray_pickable); + ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody::is_ray_pickable); + + ADD_GROUP("Collision", "collision_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, "Parent collision object"), "set_parent_collision_ignore", "get_parent_collision_ignore"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "areaAngular_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_areaAngular_stiffness", "get_areaAngular_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_volume_stiffness", "get_volume_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_coefficient"); +} + +String SoftBody::get_configuration_warning() const { + + String warning = MeshInstance::get_configuration_warning(); + + if (get_mesh().is_null()) { + if (!warning.empty()) + warning += "\n\n"; + + warning += TTR("This body will be ignored until you set a mesh"); + } + + Transform t = get_transform(); + if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) { + if (!warning.empty()) + warning += "\n\n"; + + warning += TTR("Size changes to SoftBody will be overriden by the physics engine when running.\nChange the size in children collision shapes instead."); + } + + return warning; +} + +void SoftBody::_draw_soft_mesh() { + if (get_mesh().is_null()) + return; + + if (!visual_server_handler.is_ready()) { + + visual_server_handler.prepare(get_mesh()->get_rid(), 0); + + /// Necessary in order to render the mesh correctly (Soft body nodes are in global space) + simulation_started = true; + call_deferred("set_as_toplevel", true); + call_deferred("set_transform", Transform()); + } + + visual_server_handler.open(); + PhysicsServer::get_singleton()->soft_body_update_visual_server(physics_rid, &visual_server_handler); + visual_server_handler.close(); + + visual_server_handler.commit_changes(); +} + +void SoftBody::update_physics_server() { + + if (Engine::get_singleton()->is_editor_hint()) + return; + + if (get_mesh().is_valid()) { + + become_mesh_owner(); + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + VS::get_singleton()->connect("frame_pre_draw", this, "_draw_soft_mesh"); + } else { + + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL); + VS::get_singleton()->disconnect("frame_pre_draw", this, "_draw_soft_mesh"); + } +} + +void SoftBody::become_mesh_owner() { + if (mesh.is_null()) + return; + + if (!mesh_owner) { + mesh_owner = true; + + ERR_FAIL_COND(!mesh->get_surface_count()); + + // Get current mesh array and create new mesh array with necessary flag for softbody + Array surface_arrays = mesh->surface_get_arrays(0); + Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0); + uint32_t surface_format = mesh->surface_get_format(0); + + surface_format &= ~(Mesh::ARRAY_COMPRESS_VERTEX | Mesh::ARRAY_COMPRESS_NORMAL); + surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE; + + Ref<ArrayMesh> soft_mesh; + soft_mesh.instance(); + soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_format); + + set_mesh(soft_mesh); + + Vector<Ref<Material> > copy_materials; + copy_materials.append_array(materials); + for (int i = copy_materials.size() - 1; 0 <= i; --i) { + set_surface_material(i, copy_materials[i]); + } + } +} + +void SoftBody::set_collision_mask(uint32_t p_mask) { + collision_mask = p_mask; + PhysicsServer::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask); +} + +uint32_t SoftBody::get_collision_mask() const { + return collision_mask; +} +void SoftBody::set_collision_layer(uint32_t p_layer) { + collision_layer = p_layer; + PhysicsServer::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer); +} + +uint32_t SoftBody::get_collision_layer() const { + return collision_layer; +} + +void SoftBody::set_collision_mask_bit(int p_bit, bool p_value) { + uint32_t mask = get_collision_mask(); + if (p_value) + mask |= 1 << p_bit; + else + mask &= ~(1 << p_bit); + set_collision_mask(mask); +} + +bool SoftBody::get_collision_mask_bit(int p_bit) const { + return get_collision_mask() & (1 << p_bit); +} + +void SoftBody::set_collision_layer_bit(int p_bit, bool p_value) { + uint32_t layer = get_collision_layer(); + if (p_value) + layer |= 1 << p_bit; + else + layer &= ~(1 << p_bit); + set_collision_layer(layer); +} + +bool SoftBody::get_collision_layer_bit(int p_bit) const { + return get_collision_layer() & (1 << p_bit); +} + +void SoftBody::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { + parent_collision_ignore = p_parent_collision_ignore; +} + +const NodePath &SoftBody::get_parent_collision_ignore() const { + return parent_collision_ignore; +} + +void SoftBody::set_pinned_points_indices(PoolVector<SoftBody::PinnedPoint> p_pinned_points_indices) { + pinned_points_indices = p_pinned_points_indices; + PoolVector<PinnedPoint>::Read w = pinned_points_indices.read(); + for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { + pin_point(p_pinned_points_indices[i].point_index, true); + } +} + +PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() { + return pinned_points_indices; +} + +void SoftBody::add_collision_exception_with(Node *p_node) { + ERR_FAIL_NULL(p_node); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); + } + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid()); +} + +void SoftBody::remove_collision_exception_with(Node *p_node) { + ERR_FAIL_NULL(p_node); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); + } + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid()); +} + +int SoftBody::get_simulation_precision() { + return PhysicsServer::get_singleton()->soft_body_get_simulation_precision(physics_rid); +} + +void SoftBody::set_simulation_precision(int p_simulation_precision) { + PhysicsServer::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision); +} + +real_t SoftBody::get_total_mass() { + return PhysicsServer::get_singleton()->soft_body_get_total_mass(physics_rid); +} + +void SoftBody::set_total_mass(real_t p_total_mass) { + PhysicsServer::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass); +} + +void SoftBody::set_linear_stiffness(real_t p_linear_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness); +} + +real_t SoftBody::get_linear_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_linear_stiffness(physics_rid); +} + +void SoftBody::set_areaAngular_stiffness(real_t p_areaAngular_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_areaAngular_stiffness(physics_rid, p_areaAngular_stiffness); +} + +real_t SoftBody::get_areaAngular_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_areaAngular_stiffness(physics_rid); +} + +void SoftBody::set_volume_stiffness(real_t p_volume_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_volume_stiffness(physics_rid, p_volume_stiffness); +} + +real_t SoftBody::get_volume_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_volume_stiffness(physics_rid); +} + +real_t SoftBody::get_pressure_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_pressure_coefficient(physics_rid); +} + +void SoftBody::set_pose_matching_coefficient(real_t p_pose_matching_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_pose_matching_coefficient(physics_rid, p_pose_matching_coefficient); +} + +real_t SoftBody::get_pose_matching_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_pose_matching_coefficient(physics_rid); +} + +void SoftBody::set_pressure_coefficient(real_t p_pressure_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient); +} + +real_t SoftBody::get_damping_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_damping_coefficient(physics_rid); +} + +void SoftBody::set_damping_coefficient(real_t p_damping_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient); +} + +real_t SoftBody::get_drag_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_drag_coefficient(physics_rid); +} + +void SoftBody::set_drag_coefficient(real_t p_drag_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient); +} + +Vector3 SoftBody::get_point_transform(int p_point_index) { + return PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index); +} + +void SoftBody::pin_point_toggle(int p_point_index) { + pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index))); +} + +void SoftBody::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { + _pin_point_on_physics_server(p_point_index, pin); + if (pin) { + _add_pinned_point(p_point_index, p_spatial_attachment_path); + } else { + _remove_pinned_point(p_point_index); + } +} + +bool SoftBody::is_point_pinned(int p_point_index) const { + return -1 != _has_pinned_point(p_point_index); +} + +void SoftBody::set_ray_pickable(bool p_ray_pickable) { + + ray_pickable = p_ray_pickable; + _update_pickable(); +} + +bool SoftBody::is_ray_pickable() const { + + return ray_pickable; +} + +SoftBody::SoftBody() : + MeshInstance(), + physics_rid(PhysicsServer::get_singleton()->soft_body_create()), + mesh_owner(false), + collision_mask(1), + collision_layer(1), + simulation_started(false), + pinned_points_cache_dirty(true) { + + PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id()); +} + +SoftBody::~SoftBody() { +} + +void SoftBody::reset_softbody_pin() { + PhysicsServer::get_singleton()->soft_body_remove_all_pinned_points(physics_rid); + PoolVector<PinnedPoint>::Read pps = pinned_points_indices.read(); + for (int i = pinned_points_indices.size() - 1; 0 < i; --i) { + PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true); + } +} + +void SoftBody::_update_cache_pin_points_datas() { + if (pinned_points_cache_dirty) { + pinned_points_cache_dirty = false; + + PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); + for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { + + if (!w[i].spatial_attachment_path.is_empty()) { + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path)); + if (w[i].spatial_attachment) { + + Transform point_global_transform(get_global_transform()); + point_global_transform.translate(PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index)); + + // Local transform relative to spatial attachment node + w[i].vertex_offset_transform = w[i].spatial_attachment->get_global_transform().affine_inverse() * point_global_transform; + continue; + } else { + ERR_PRINTS("The node with path: " + String(w[i].spatial_attachment_path) + " was not found or is not a spatial node."); + } + } + // Local transform relative to Soft body + w[i].vertex_offset_transform.origin = PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index); + w[i].vertex_offset_transform.basis = Basis(); + } + } +} + +void SoftBody::_pin_point_on_physics_server(int p_point_index, bool pin) { + PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin); +} + +void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { + SoftBody::PinnedPoint *pinned_point; + if (-1 == _get_pinned_point(p_point_index, pinned_point)) { + // Create new + PinnedPoint pp; + pp.point_index = p_point_index; + pp.spatial_attachment_path = p_spatial_attachment_path; + pinned_points_indices.push_back(pp); + } else { + // Update + pinned_point->point_index = p_point_index; + pinned_point->spatial_attachment_path = p_spatial_attachment_path; + } +} + +void SoftBody::_remove_pinned_point(int p_point_index) { + const int id(_has_pinned_point(p_point_index)); + if (-1 != id) { + pinned_points_indices.remove(id); + } +} + +int SoftBody::_get_pinned_point(int p_point_index, SoftBody::PinnedPoint *&r_point) const { + const int id = _has_pinned_point(p_point_index); + if (-1 == id) { + r_point = NULL; + return -1; + } else { + r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points_indices.read()[id]); + return id; + } +} + +int SoftBody::_has_pinned_point(int p_point_index) const { + PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { + if (p_point_index == r[i].point_index) { + return i; + } + } + return -1; +} diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h new file mode 100644 index 0000000000..f44f337698 --- /dev/null +++ b/scene/3d/soft_body.h @@ -0,0 +1,197 @@ +/*************************************************************************/ +/* soft_physics_body.h */ +/* Author: AndreaCatania */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SOFT_PHYSICS_BODY_H +#define SOFT_PHYSICS_BODY_H + +#include "scene/3d/mesh_instance.h" + +class SoftBody; + +class SoftBodyVisualServerHandler { + + friend class SoftBody; + + RID mesh; + int surface; + PoolVector<uint8_t> buffer; + uint32_t stride; + uint32_t offset_vertices; + uint32_t offset_normal; + + PoolVector<uint8_t>::Write write_buffer; + +private: + SoftBodyVisualServerHandler(); + bool is_ready() { return mesh.is_valid(); } + void prepare(RID p_mesh_rid, int p_surface); + void clear(); + void open(); + void close(); + void commit_changes(); + +public: + void set_vertex(int p_vertex_id, const void *p_vector3); + void set_normal(int p_vertex_id, const void *p_vector3); + void set_aabb(const AABB &p_aabb); +}; + +class SoftBody : public MeshInstance { + GDCLASS(SoftBody, MeshInstance); + +public: + struct PinnedPoint { + int point_index; + NodePath spatial_attachment_path; + Spatial *spatial_attachment; // Cache + /// This is the offset from the soft body to point or attachment to point + /// Depend if the spatial_attachment_node is NULL or not + Transform vertex_offset_transform; // Cache + + PinnedPoint(); + PinnedPoint(const PinnedPoint &obj_tocopy); + }; + +private: + SoftBodyVisualServerHandler visual_server_handler; + + RID physics_rid; + + bool mesh_owner; + uint32_t collision_mask; + uint32_t collision_layer; + NodePath parent_collision_ignore; + PoolVector<PinnedPoint> pinned_points_indices; + bool simulation_started; + bool pinned_points_cache_dirty; + + Ref<ArrayMesh> debug_mesh_cache; + class MeshInstance *debug_mesh; + + bool capture_input_on_drag; + bool ray_pickable; + + void _update_pickable(); + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + bool _set_property_pinned_points_indices(const Array &p_indices); + bool _set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value); + bool _get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const; + + virtual void _changed_callback(Object *p_changed, const char *p_prop); + + void _notification(int p_what); + static void _bind_methods(); + + virtual String get_configuration_warning() const; + +protected: + void _draw_soft_mesh(); + +public: + void update_physics_server(); + void become_mesh_owner(); + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_layer(uint32_t p_layer); + uint32_t get_collision_layer() const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + + void set_collision_layer_bit(int p_bit, bool p_value); + bool get_collision_layer_bit(int p_bit) const; + + void set_parent_collision_ignore(const NodePath &p_parent_collision_ignore); + const NodePath &get_parent_collision_ignore() const; + + void set_pinned_points_indices(PoolVector<PinnedPoint> p_pinned_points_indices); + PoolVector<PinnedPoint> get_pinned_points_indices(); + + void set_simulation_precision(int p_simulation_precision); + int get_simulation_precision(); + + void set_total_mass(real_t p_total_mass); + real_t get_total_mass(); + + void set_linear_stiffness(real_t p_linear_stiffness); + real_t get_linear_stiffness(); + + void set_areaAngular_stiffness(real_t p_areaAngular_stiffness); + real_t get_areaAngular_stiffness(); + + void set_volume_stiffness(real_t p_volume_stiffness); + real_t get_volume_stiffness(); + + void set_pressure_coefficient(real_t p_pressure_coefficient); + real_t get_pressure_coefficient(); + + void set_pose_matching_coefficient(real_t p_pose_matching_coefficient); + real_t get_pose_matching_coefficient(); + + void set_damping_coefficient(real_t p_damping_coefficient); + real_t get_damping_coefficient(); + + void set_drag_coefficient(real_t p_drag_coefficient); + real_t get_drag_coefficient(); + + void add_collision_exception_with(Node *p_node); + void remove_collision_exception_with(Node *p_node); + + Vector3 get_point_transform(int p_point_index); + + void pin_point_toggle(int p_point_index); + void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath()); + bool is_point_pinned(int p_point_index) const; + + void set_ray_pickable(bool p_ray_pickable); + bool is_ray_pickable() const; + + SoftBody(); + ~SoftBody(); + +private: + void reset_softbody_pin(); + void _update_cache_pin_points_datas(); + void _pin_point_on_physics_server(int p_point_index, bool pin); + void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path); + void _remove_pinned_point(int p_point_index); + int _get_pinned_point(int p_point_index, PinnedPoint *&r_point) const; + int _has_pinned_point(int p_point_index) const; +}; + +#endif // SOFT_PHYSICS_BODY_H diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 748aa8aad4..9b27faed6a 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -280,6 +280,10 @@ Transform Spatial::get_global_transform() const { data.global_transform = data.local_transform; } + if (data.disable_scale) { + data.global_transform.basis.orthonormalize(); + } + data.dirty &= ~DIRTY_GLOBAL; } @@ -467,6 +471,15 @@ void Spatial::set_disable_gizmo(bool p_enabled) { #endif +void Spatial::set_disable_scale(bool p_enabled) { + + data.disable_scale = p_enabled; +} + +bool Spatial::is_scale_disabled() const { + return data.disable_scale; +} + void Spatial::set_as_toplevel(bool p_enabled) { if (data.toplevel == p_enabled) @@ -735,6 +748,8 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Spatial::set_ignore_transform_notification); ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &Spatial::set_as_toplevel); ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel); + ClassDB::bind_method(D_METHOD("set_disable_scale", "disable"), &Spatial::set_disable_scale); + ClassDB::bind_method(D_METHOD("is_scale_disabled"), &Spatial::is_scale_disabled); ClassDB::bind_method(D_METHOD("get_world"), &Spatial::get_world); ClassDB::bind_method(D_METHOD("_update_gizmo"), &Spatial::_update_gizmo); @@ -755,15 +770,6 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_notify_transform", "enable"), &Spatial::set_notify_transform); ClassDB::bind_method(D_METHOD("is_transform_notification_enabled"), &Spatial::is_transform_notification_enabled); - void rotate(const Vector3 &p_axis, float p_angle); - void rotate_x(float p_angle); - void rotate_y(float p_angle); - void rotate_z(float p_angle); - void translate(const Vector3 &p_offset); - void scale(const Vector3 &p_ratio); - void global_rotate(const Vector3 &p_axis, float p_angle); - void global_translate(const Vector3 &p_offset); - ClassDB::bind_method(D_METHOD("rotate", "axis", "angle"), &Spatial::rotate); ClassDB::bind_method(D_METHOD("global_rotate", "axis", "angle"), &Spatial::global_rotate); ClassDB::bind_method(D_METHOD("global_scale", "scale"), &Spatial::global_scale); @@ -797,6 +803,7 @@ void Spatial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", 0), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); + ADD_GROUP("Visibility", ""); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo"); @@ -817,6 +824,7 @@ Spatial::Spatial() : data.viewport = NULL; data.inside_world = false; data.visible = true; + data.disable_scale = false; #ifdef TOOLS_ENABLED data.gizmo_disabled = false; diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h index a43bed3e4a..653714dc98 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -92,6 +92,7 @@ class Spatial : public Node { bool notify_transform; bool visible; + bool disable_scale; #ifdef TOOLS_ENABLED Ref<SpatialGizmo> gizmo; @@ -153,6 +154,9 @@ public: void set_as_toplevel(bool p_enabled); bool is_set_as_toplevel() const; + void set_disable_scale(bool p_enabled); + bool is_scale_disabled() const; + void set_disable_gizmo(bool p_enabled); void update_gizmo(); void set_gizmo(const Ref<SpatialGizmo> &p_gizmo); diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index d833e6a8bb..036a748c83 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -185,6 +185,9 @@ void SpriteBase3D::_queue_update() { if (pending_update) return; + triangle_mesh.unref(); + update_gizmo(); + pending_update = true; call_deferred(SceneStringNames::get_singleton()->_im_update); } @@ -198,6 +201,66 @@ PoolVector<Face3> SpriteBase3D::get_faces(uint32_t p_usage_flags) const { return PoolVector<Face3>(); } +Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { + if (triangle_mesh.is_valid()) + return triangle_mesh; + + PoolVector<Vector3> faces; + faces.resize(6); + PoolVector<Vector3>::Write facesw = faces.write(); + + Rect2 final_rect = get_item_rect(); + + if (final_rect.size.x == 0 || final_rect.size.y == 0) + return Ref<TriangleMesh>(); + + float pixel_size = get_pixel_size(); + + Vector2 vertices[4] = { + + (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size, + (final_rect.position + final_rect.size) * pixel_size, + (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, + final_rect.position * pixel_size, + + }; + + int x_axis = ((axis + 1) % 3); + int y_axis = ((axis + 2) % 3); + + if (axis != Vector3::AXIS_Z) { + SWAP(x_axis, y_axis); + + for (int i = 0; i < 4; i++) { + if (axis == Vector3::AXIS_Y) { + vertices[i].y = -vertices[i].y; + } else if (axis == Vector3::AXIS_X) { + vertices[i].x = -vertices[i].x; + } + } + } + + static const int indices[6] = { + 0, 1, 2, + 0, 2, 3 + }; + + for (int j = 0; j < 6; j++) { + int i = indices[j]; + Vector3 vtx; + vtx[x_axis] = vertices[i][0]; + vtx[y_axis] = vertices[i][1]; + facesw[j] = vtx; + } + + facesw = PoolVector<Vector3>::Write(); + + triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh->create(faces); + + return triangle_mesh; +} + void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); @@ -255,6 +318,7 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &SpriteBase3D::get_alpha_cut_mode); ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect); + ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh); ClassDB::bind_method(D_METHOD("_queue_update"), &SpriteBase3D::_queue_update); ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update); @@ -901,7 +965,7 @@ Rect2 AnimatedSprite3D::get_item_rect() const { return Rect2(0, 0, 1, 1); Size2i s = t->get_size(); - Point2 ofs = offset; + Point2 ofs = get_offset(); if (centered) ofs -= s / 2; diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 23e1d96b4b..a4705a8970 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -38,6 +38,8 @@ class SpriteBase3D : public GeometryInstance { GDCLASS(SpriteBase3D, GeometryInstance); + mutable Ref<TriangleMesh> triangle_mesh; //cached + public: enum DrawFlags { FLAG_TRANSPARENT, @@ -133,6 +135,7 @@ public: virtual AABB get_aabb() const; virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const; + Ref<TriangleMesh> generate_triangle_mesh() const; SpriteBase3D(); ~SpriteBase3D(); @@ -192,7 +195,6 @@ class AnimatedSprite3D : public SpriteBase3D { int frame; bool centered; - Point2 offset; float timeout; diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index 385956dc16..4b870ca54c 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -942,8 +942,6 @@ VehicleBody::VehicleBody() : engine_force = 0; brake = 0; - friction = 1; - state = NULL; ccd = false; diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index 670df5cc7f..ba2807d4e8 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -113,7 +113,7 @@ static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) { rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ if (min > rad || max < -rad) return false; - /*======================== Z-tests ========================*/ +/*======================== Z-tests ========================*/ #define AXISTEST_Z12(a, b, fa, fb) \ p1 = a * v1.x - b * v1.y; \ @@ -1961,7 +1961,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh #endif for (int i = 0; i < height; i++) { - //print_line("bake line " + itos(i) + " / " + itos(height)); + //print_line("bake line " + itos(i) + " / " + itos(height)); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 1) #endif |