diff options
Diffstat (limited to 'scene/3d')
65 files changed, 1744 insertions, 1082 deletions
diff --git a/scene/3d/SCsub b/scene/3d/SCsub index 40bdaee47d..fc61250247 100644 --- a/scene/3d/SCsub +++ b/scene/3d/SCsub @@ -2,7 +2,4 @@ Import("env") -if env["disable_3d"]: - env.add_source_files(env.scene_sources, "node_3d.cpp") -else: - env.add_source_files(env.scene_sources, "*.cpp") +env.add_source_files(env.scene_sources, "*.cpp") diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index cd64a813dd..943586f43c 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -32,7 +32,6 @@ #include "scene/scene_string_names.h" #include "servers/audio_server.h" -#include "servers/physics_server_3d.h" void Area3D::set_space_override_mode(SpaceOverride p_mode) { space_override = p_mode; @@ -106,6 +105,61 @@ real_t Area3D::get_priority() const { return priority; } +void Area3D::set_wind_force_magnitude(real_t p_wind_force_magnitude) { + wind_force_magnitude = p_wind_force_magnitude; + if (is_inside_tree()) { + _initialize_wind(); + } +} + +real_t Area3D::get_wind_force_magnitude() const { + return wind_force_magnitude; +} + +void Area3D::set_wind_attenuation_factor(real_t p_wind_force_attenuation_factor) { + wind_attenuation_factor = p_wind_force_attenuation_factor; + if (is_inside_tree()) { + _initialize_wind(); + } +} + +real_t Area3D::get_wind_attenuation_factor() const { + return wind_attenuation_factor; +} + +void Area3D::set_wind_source_path(const NodePath &p_wind_source_path) { + wind_source_path = p_wind_source_path; + if (is_inside_tree()) { + _initialize_wind(); + } +} + +const NodePath &Area3D::get_wind_source_path() const { + return wind_source_path; +} + +void Area3D::_initialize_wind() { + real_t temp_magnitude = 0.0; + Vector3 wind_direction(0., 0., 0.); + Vector3 wind_source(0., 0., 0.); + + // Overwrite with area-specified info if available + if (!wind_source_path.is_empty()) { + Node3D *p_wind_source = Object::cast_to<Node3D>(get_node(wind_source_path)); + ERR_FAIL_NULL(p_wind_source); + Transform3D global_transform = p_wind_source->get_transform(); + wind_direction = -global_transform.basis.get_axis(Vector3::AXIS_Z).normalized(); + wind_source = global_transform.origin; + temp_magnitude = wind_force_magnitude; + } + + // Set force, source and direction in the physics server. + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_WIND_ATTENUATION_FACTOR, wind_attenuation_factor); + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_WIND_SOURCE, wind_source); + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_WIND_DIRECTION, wind_direction); + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_WIND_FORCE_MAGNITUDE, temp_magnitude); +} + void Area3D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); @@ -265,6 +319,8 @@ void Area3D::_clear_monitoring() { void Area3D::_notification(int p_what) { if (p_what == NOTIFICATION_EXIT_TREE) { _clear_monitoring(); + } else if (p_what == NOTIFICATION_ENTER_TREE) { + _initialize_wind(); } } @@ -551,6 +607,15 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_priority", "priority"), &Area3D::set_priority); ClassDB::bind_method(D_METHOD("get_priority"), &Area3D::get_priority); + ClassDB::bind_method(D_METHOD("set_wind_force_magnitude", "wind_force_magnitude"), &Area3D::set_wind_force_magnitude); + ClassDB::bind_method(D_METHOD("get_wind_force_magnitude"), &Area3D::get_wind_force_magnitude); + + ClassDB::bind_method(D_METHOD("set_wind_attenuation_factor", "wind_attenuation_factor"), &Area3D::set_wind_attenuation_factor); + ClassDB::bind_method(D_METHOD("get_wind_attenuation_factor"), &Area3D::get_wind_attenuation_factor); + + ClassDB::bind_method(D_METHOD("set_wind_source_path", "wind_source_path"), &Area3D::set_wind_source_path); + ClassDB::bind_method(D_METHOD("get_wind_source_path"), &Area3D::get_wind_source_path); + ClassDB::bind_method(D_METHOD("set_monitorable", "enable"), &Area3D::set_monitorable); ClassDB::bind_method(D_METHOD("is_monitorable"), &Area3D::is_monitorable); @@ -606,6 +671,9 @@ void Area3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-32,32,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wind_force_magnitude", PROPERTY_HINT_RANGE, "0,10,0.001,or_greater"), "set_wind_force_magnitude", "get_wind_force_magnitude"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wind_attenuation_factor", PROPERTY_HINT_RANGE, "0.0,3.0,0.001,or_greater"), "set_wind_attenuation_factor", "get_wind_attenuation_factor"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "wind_source_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_wind_source_path", "get_wind_source_path"); ADD_GROUP("Audio Bus", "audio_bus_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus"); diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index 5b8d612717..847d1c5966 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -55,6 +55,9 @@ private: real_t angular_damp = 0.1; real_t linear_damp = 0.1; int priority = 0; + real_t wind_force_magnitude = 0.0; + real_t wind_attenuation_factor = 0.0; + NodePath wind_source_path; bool monitoring = false; bool monitorable = false; bool locked = false; @@ -134,6 +137,8 @@ private: void _validate_property(PropertyInfo &property) const override; + void _initialize_wind(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -163,6 +168,15 @@ public: void set_priority(real_t p_priority); real_t get_priority() const; + void set_wind_force_magnitude(real_t p_wind_force_magnitude); + real_t get_wind_force_magnitude() const; + + void set_wind_attenuation_factor(real_t p_wind_attenuation_factor); + real_t get_wind_attenuation_factor() const; + + void set_wind_source_path(const NodePath &p_wind_source_path); + const NodePath &get_wind_source_path() const; + void set_monitoring(bool p_enable); bool is_monitoring() const; diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 6318aad08e..16d5b9da3e 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -30,11 +30,10 @@ #include "audio_stream_player_3d.h" -#include "core/config/engine.h" #include "scene/3d/area_3d.h" #include "scene/3d/camera_3d.h" #include "scene/3d/listener_3d.h" -#include "scene/main/window.h" +#include "scene/main/viewport.h" // Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004) // Speaker-Placement Correction Amplitude Panning (SPCAP) @@ -96,7 +95,7 @@ static const Vector3 speaker_directions[7] = { Vector3(1.0, 0.0, 0.0).normalized(), // side-right }; -void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tightness, AudioStreamPlayer3D::Output &output) { +void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tightness, Vector<AudioFrame> &output) { unsigned int speaker_count = 0; // only main speakers (no LFE) switch (AudioServer::get_singleton()->get_speaker_mode()) { case AudioServer::SPEAKER_MODE_STEREO: @@ -119,182 +118,94 @@ void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tig switch (AudioServer::get_singleton()->get_speaker_mode()) { case AudioServer::SPEAKER_SURROUND_71: - output.vol[3].l = volumes[5]; // side-left - output.vol[3].r = volumes[6]; // side-right + output.write[3].l = volumes[5]; // side-left + output.write[3].r = volumes[6]; // side-right [[fallthrough]]; case AudioServer::SPEAKER_SURROUND_51: - output.vol[2].l = volumes[3]; // rear-left - output.vol[2].r = volumes[4]; // rear-right + output.write[2].l = volumes[3]; // rear-left + output.write[2].r = volumes[4]; // rear-right [[fallthrough]]; case AudioServer::SPEAKER_SURROUND_31: - output.vol[1].r = 1.0; // LFE - always full power - output.vol[1].l = volumes[2]; // center + output.write[1].r = 1.0; // LFE - always full power + output.write[1].l = volumes[2]; // center [[fallthrough]]; case AudioServer::SPEAKER_MODE_STEREO: - output.vol[0].r = volumes[1]; // front-right - output.vol[0].l = volumes[0]; // front-left + output.write[0].r = volumes[1]; // front-right + output.write[0].l = volumes[0]; // front-left break; } } -void AudioStreamPlayer3D::_mix_audio() { - if (!stream_playback.is_valid() || !active.is_set() || - (stream_paused && !stream_paused_fade_out)) { - return; - } +void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_pos, Vector<AudioFrame> direct_path_vol, Vector<AudioFrame> &reverb_vol) { + reverb_vol.resize(4); + reverb_vol.write[0] = AudioFrame(0, 0); + reverb_vol.write[1] = AudioFrame(0, 0); + reverb_vol.write[2] = AudioFrame(0, 0); + reverb_vol.write[3] = AudioFrame(0, 0); - bool started = false; - if (setseek.get() >= 0.0) { - stream_playback->start(setseek.get()); - setseek.set(-1.0); //reset seek - started = true; - } + float uniformity = area->get_reverb_uniformity(); + float area_send = area->get_reverb_amount(); - //get data - AudioFrame *buffer = mix_buffer.ptrw(); - int buffer_size = mix_buffer.size(); + if (uniformity > 0.0) { + float distance = listener_area_pos.length(); + float attenuation = Math::db2linear(_get_attenuation_db(distance)); - if (stream_paused_fade_out) { - // Short fadeout ramp - buffer_size = MIN(buffer_size, 128); - } + // Determine the fraction of sound that would come from each speaker if they were all driven uniformly. + float center_val[3] = { 0.5f, 0.25f, 0.16666f }; + int channel_count = AudioServer::get_singleton()->get_channel_count(); + AudioFrame center_frame(center_val[channel_count - 1], center_val[channel_count - 1]); - // Mix if we're not paused or we're fading out - if ((output_count.get() > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) { - float output_pitch_scale = 0.0; - if (output_count.get()) { - //used for doppler, not realistic but good enough - for (int i = 0; i < output_count.get(); i++) { - output_pitch_scale += outputs[i].pitch_scale; - } - output_pitch_scale /= float(output_count.get()); - } else { - output_pitch_scale = 1.0; - } + if (attenuation < 1.0) { + //pan the uniform sound + Vector3 rev_pos = listener_area_pos; + rev_pos.y = 0; + rev_pos.normalize(); - stream_playback->mix(buffer, pitch_scale * output_pitch_scale, buffer_size); - } - - //write all outputs - for (int i = 0; i < output_count.get(); i++) { - Output current = outputs[i]; - - //see if current output exists, to keep volume ramp - bool found = false; - for (int j = i; j < prev_output_count; j++) { - if (prev_outputs[j].viewport == current.viewport) { - if (j != i) { - SWAP(prev_outputs[j], prev_outputs[i]); - } - found = true; - break; + if (channel_count >= 1) { + // Stereo pair + float c = rev_pos.x * 0.5 + 0.5; + reverb_vol.write[0].l = 1.0 - c; + reverb_vol.write[0].r = c; } - } - bool interpolate_filter = !started; + if (channel_count >= 3) { + // Center pair + Side pair + float xl = Vector3(-1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; + float xr = Vector3(1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; - if (!found) { - //create new if was not used before - if (prev_output_count < MAX_OUTPUTS) { - prev_outputs[prev_output_count] = prev_outputs[i]; //may be owned by another viewport - prev_output_count++; + reverb_vol.write[1].l = xl; + reverb_vol.write[1].r = xr; + reverb_vol.write[2].l = 1.0 - xr; + reverb_vol.write[2].r = 1.0 - xl; } - prev_outputs[i] = current; - interpolate_filter = false; - } - - //mix! - - int buffers = AudioServer::get_singleton()->get_channel_count(); - - for (int k = 0; k < buffers; 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 = vol_prev; - if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k)) { - continue; //may have been deleted, will be updated on process + if (channel_count >= 4) { + // Rear pair + // FIXME: Not sure what math should be done here + float c = rev_pos.x * 0.5 + 0.5; + reverb_vol.write[3].l = 1.0 - c; + reverb_vol.write[3].r = c; } - AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k); - current.filter.set_mode(AudioFilterSW::HIGHSHELF); - current.filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate()); - current.filter.set_cutoff(attenuation_filter_cutoff_hz); - current.filter.set_resonance(1); - current.filter.set_stages(1); - current.filter.set_gain(current.filter_gain); - - if (interpolate_filter) { - current.filter_process[k * 2 + 0] = prev_outputs[i].filter_process[k * 2 + 0]; - current.filter_process[k * 2 + 1] = prev_outputs[i].filter_process[k * 2 + 1]; - - current.filter_process[k * 2 + 0].set_filter(¤t.filter, false); - current.filter_process[k * 2 + 1].set_filter(¤t.filter, false); - - current.filter_process[k * 2 + 0].update_coeffs(buffer_size); - current.filter_process[k * 2 + 1].update_coeffs(buffer_size); - for (int j = 0; j < buffer_size; j++) { - AudioFrame f = buffer[j] * vol; - current.filter_process[k * 2 + 0].process_one_interp(f.l); - current.filter_process[k * 2 + 1].process_one_interp(f.r); - - target[j] += f; - vol += vol_inc; - } - } else { - current.filter_process[k * 2 + 0].set_filter(¤t.filter); - current.filter_process[k * 2 + 1].set_filter(¤t.filter); - - current.filter_process[k * 2 + 0].update_coeffs(); - current.filter_process[k * 2 + 1].update_coeffs(); - for (int j = 0; j < buffer_size; j++) { - AudioFrame f = buffer[j] * vol; - current.filter_process[k * 2 + 0].process_one(f.l); - current.filter_process[k * 2 + 1].process_one(f.r); - - target[j] += f; - vol += vol_inc; - } + for (int i = 0; i < channel_count; i++) { + reverb_vol.write[i] = reverb_vol[i].lerp(center_frame, attenuation); } - - if (current.reverb_bus_index >= 0) { - if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.reverb_bus_index, k)) { - continue; //may have been deleted, will be updated on process - } - - AudioFrame *rtarget = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.reverb_bus_index, k); - - if (current.reverb_bus_index == prev_outputs[i].reverb_bus_index) { - AudioFrame rvol_inc = (current.reverb_vol[k] - prev_outputs[i].reverb_vol[k]) / float(buffer_size); - AudioFrame rvol = prev_outputs[i].reverb_vol[k]; - - for (int j = 0; j < buffer_size; j++) { - rtarget[j] += buffer[j] * rvol; - rvol += rvol_inc; - } - } else { - AudioFrame rvol = current.reverb_vol[k]; - for (int j = 0; j < buffer_size; j++) { - rtarget[j] += buffer[j] * rvol; - } - } + } else { + for (int i = 0; i < channel_count; i++) { + reverb_vol.write[i] = center_frame; } } - prev_outputs[i] = current; - } - - prev_output_count = output_count.get(); + for (int i = 0; i < channel_count; i++) { + reverb_vol.write[i] = direct_path_vol[i].lerp(reverb_vol[i] * attenuation, uniformity); + reverb_vol.write[i] *= area_send; + } - //stream is no longer active, disable this. - if (!stream_playback->is_playing()) { - active.clear(); + } else { + for (int i = 0; i < 4; i++) { + reverb_vol.write[i] = direct_path_vol[i] * area_send; + } } - - output_ready.clear(); - stream_paused_fade_in = false; - stream_paused_fade_out = false; } float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { @@ -330,14 +241,15 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { void AudioStreamPlayer3D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { velocity_tracker->reset(get_global_transform().origin); - AudioServer::get_singleton()->add_callback(_mix_audios, this); + AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this); if (autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } } if (p_what == NOTIFICATION_EXIT_TREE) { - AudioServer::get_singleton()->remove_callback(_mix_audios, this); + stop(); + AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this); } if (p_what == NOTIFICATION_PAUSED) { @@ -360,277 +272,227 @@ void AudioStreamPlayer3D::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { //update anything related to position first, if possible of course - if (!output_ready.is_set()) { - Vector3 linear_velocity; + if (!stream_playback.is_valid()) { + return; + } + //start playing if requested - //compute linear velocity for doppler - if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { - linear_velocity = velocity_tracker->get_tracked_linear_velocity(); - } + if (setplay.get() >= 0) { + Vector<AudioFrame> volume_vector = _update_panning(); + AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get()); + active.set(); + setplay.set(-1); + } - Ref<World3D> world_3d = get_world_3d(); - ERR_FAIL_COND(world_3d.is_null()); + if (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) { + _update_panning(); + last_mix_count = AudioServer::get_singleton()->get_mix_count(); + } - int new_output_count = 0; + // Stop playing if no longer active. + if (!active.is_set()) { + set_physics_process_internal(false); + emit_signal(SNAME("finished")); + } + } +} - Vector3 global_pos = get_global_transform().origin; +Area3D *AudioStreamPlayer3D::_get_overriding_area() { + //check if any area is diverting sound into a bus + Ref<World3D> world_3d = get_world_3d(); + ERR_FAIL_COND_V(world_3d.is_null(), nullptr); - int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); + Vector3 global_pos = get_global_transform().origin; - //check if any area is diverting sound into a bus + PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); - PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); + PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS]; - PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS]; + int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); + + for (int i = 0; i < areas; i++) { + if (!sr[i].collider) { + continue; + } - int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); - Area3D *area = nullptr; + Area3D *tarea = Object::cast_to<Area3D>(sr[i].collider); + if (!tarea) { + continue; + } + + if (!tarea->is_overriding_audio_bus() && !tarea->is_using_reverb_bus()) { + continue; + } + + return tarea; + } + return nullptr; +} + +StringName AudioStreamPlayer3D::_get_actual_bus() { + if (!stream_playback.is_valid()) { + return SNAME("Master"); + } + Area3D *overriding_area = _get_overriding_area(); + if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) { + return overriding_area->get_audio_bus_name(); + } + return bus; +} + +Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { + Vector<AudioFrame> output_volume_vector; + output_volume_vector.resize(4); + for (AudioFrame &frame : output_volume_vector) { + frame = AudioFrame(0, 0); + } + + ERR_FAIL_COND_V(stream_playback.is_null(), output_volume_vector); + + Vector3 linear_velocity; + + //compute linear velocity for doppler + if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { + linear_velocity = velocity_tracker->get_tracked_linear_velocity(); + } - for (int i = 0; i < areas; i++) { - if (!sr[i].collider) { - continue; - } + Vector3 global_pos = get_global_transform().origin; - Area3D *tarea = Object::cast_to<Area3D>(sr[i].collider); - if (!tarea) { - continue; - } + Ref<World3D> world_3d = get_world_3d(); + ERR_FAIL_COND_V(world_3d.is_null(), output_volume_vector); - if (!tarea->is_overriding_audio_bus() && !tarea->is_using_reverb_bus()) { - continue; - } + Set<Camera3D *> cameras = world_3d->get_cameras(); + cameras.insert(get_viewport()->get_camera_3d()); - area = tarea; - break; + PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); + + for (Camera3D *camera : cameras) { + Viewport *vp = camera->get_viewport(); + if (!vp->is_audio_listener_3d()) { + continue; + } + + bool listener_is_camera = true; + Node3D *listener_node = camera; + + Listener3D *listener = vp->get_listener_3d(); + if (listener) { + listener_node = listener; + listener_is_camera = false; + } + + Vector3 local_pos = listener_node->get_global_transform().orthonormalized().affine_inverse().xform(global_pos); + + float dist = local_pos.length(); + + Vector3 area_sound_pos; + Vector3 listener_area_pos; + + Area3D *area = _get_overriding_area(); + + if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { + area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), listener_node->get_global_transform().origin); + listener_area_pos = listener_node->get_global_transform().affine_inverse().xform(area_sound_pos); + } + + if (max_distance > 0) { + float total_max = max_distance; + + if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { + total_max = MAX(total_max, listener_area_pos.length()); + } + if (total_max > max_distance) { + continue; //can't hear this sound in this listener } + } - for (const Set<Camera3D *>::Element *E = world_3d->get_cameras().front(); E; E = E->next()) { - Camera3D *camera = E->get(); - Viewport *vp = camera->get_viewport(); - if (!vp->is_audio_listener_3d()) { - continue; - } - - bool listener_is_camera = true; - Node3D *listener_node = camera; - - Listener3D *listener = vp->get_listener_3d(); - if (listener) { - listener_node = listener; - listener_is_camera = false; - } - - Vector3 local_pos = listener_node->get_global_transform().orthonormalized().affine_inverse().xform(global_pos); - - float dist = local_pos.length(); - - Vector3 area_sound_pos; - Vector3 listener_area_pos; - - if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { - area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), listener_node->get_global_transform().origin); - listener_area_pos = listener_node->get_global_transform().affine_inverse().xform(area_sound_pos); - } - - if (max_distance > 0) { - float total_max = max_distance; - - if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { - total_max = MAX(total_max, listener_area_pos.length()); - } - if (total_max > max_distance) { - continue; //can't hear this sound in this listener - } - } - - float multiplier = Math::db2linear(_get_attenuation_db(dist)); - if (max_distance > 0) { - multiplier *= MAX(0, 1.0 - (dist / max_distance)); - } - - Output output; - output.bus_index = bus_index; - output.reverb_bus_index = -1; //no reverb by default - output.viewport = vp; - - float db_att = (1.0 - MIN(1.0, multiplier)) * attenuation_filter_db; - - if (emission_angle_enabled) { - Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin; - float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative - float angle = Math::rad2deg(Math::acos(c)); - if (angle > emission_angle) { - db_att -= -emission_angle_filter_attenuation_db; - } - } - - output.filter_gain = Math::db2linear(db_att); - - //TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from - // speakers not facing the source) - this could be made distance dependent. - _calc_output_vol(local_pos.normalized(), 4.0, output); - - unsigned int cc = AudioServer::get_singleton()->get_channel_count(); - for (unsigned int k = 0; k < cc; k++) { - output.vol[k] *= multiplier; - } - - bool filled_reverb = false; - int vol_index_max = AudioServer::get_singleton()->get_speaker_mode() + 1; - - if (area) { - if (area->is_overriding_audio_bus()) { - //override audio bus - StringName bus_name = area->get_audio_bus_name(); - output.bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); - } - - if (area->is_using_reverb_bus()) { - filled_reverb = true; - StringName bus_name = area->get_reverb_bus(); - output.reverb_bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); - - float uniformity = area->get_reverb_uniformity(); - float area_send = area->get_reverb_amount(); - - if (uniformity > 0.0) { - float distance = listener_area_pos.length(); - float attenuation = Math::db2linear(_get_attenuation_db(distance)); - - //float dist_att_db = -20 * Math::log(dist + 0.00001); //logarithmic attenuation, like in real life - - float center_val[3] = { 0.5f, 0.25f, 0.16666f }; - AudioFrame center_frame(center_val[vol_index_max - 1], center_val[vol_index_max - 1]); - - if (attenuation < 1.0) { - //pan the uniform sound - Vector3 rev_pos = listener_area_pos; - rev_pos.y = 0; - rev_pos.normalize(); - - if (cc >= 1) { - // Stereo pair - float c = rev_pos.x * 0.5 + 0.5; - output.reverb_vol[0].l = 1.0 - c; - output.reverb_vol[0].r = c; - } - - if (cc >= 3) { - // Center pair + Side pair - float xl = Vector3(-1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; - float xr = Vector3(1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; - - output.reverb_vol[1].l = xl; - output.reverb_vol[1].r = xr; - output.reverb_vol[2].l = 1.0 - xr; - output.reverb_vol[2].r = 1.0 - xl; - } - - if (cc >= 4) { - // Rear pair - // FIXME: Not sure what math should be done here - float c = rev_pos.x * 0.5 + 0.5; - output.reverb_vol[3].l = 1.0 - c; - output.reverb_vol[3].r = c; - } - - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = output.reverb_vol[i].lerp(center_frame, attenuation); - } - } else { - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = center_frame; - } - } - - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = output.vol[i].lerp(output.reverb_vol[i] * attenuation, uniformity); - output.reverb_vol[i] *= area_send; - } - - } else { - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = output.vol[i] * area_send; - } - } - } - } - - if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { - Vector3 listener_velocity; - - if (listener_is_camera) { - listener_velocity = camera->get_doppler_tracked_velocity(); - } - - Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity); - - if (local_velocity == Vector3()) { - output.pitch_scale = 1.0; - } else { - float approaching = local_pos.normalized().dot(local_velocity.normalized()); - float velocity = local_velocity.length(); - float speed_of_sound = 343.0; - - output.pitch_scale = speed_of_sound / (speed_of_sound + velocity * approaching); - output.pitch_scale = CLAMP(output.pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff - } - - } else { - output.pitch_scale = 1.0; - } - - if (!filled_reverb) { - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = AudioFrame(0, 0); - } - } - - outputs[new_output_count] = output; - new_output_count++; - if (new_output_count == MAX_OUTPUTS) { - break; - } + float multiplier = Math::db2linear(_get_attenuation_db(dist)); + if (max_distance > 0) { + multiplier *= MAX(0, 1.0 - (dist / max_distance)); + } + + float db_att = (1.0 - MIN(1.0, multiplier)) * attenuation_filter_db; + + if (emission_angle_enabled) { + Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin; + float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative + float angle = Math::rad2deg(Math::acos(c)); + if (angle > emission_angle) { + db_att -= -emission_angle_filter_attenuation_db; } + } - output_count.set(new_output_count); - output_ready.set(); + AudioServer::get_singleton()->set_playback_highshelf_params(stream_playback, Math::db2linear(db_att), attenuation_filter_cutoff_hz); + //TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from + // speakers not facing the source) - this could be made distance dependent. + _calc_output_vol(local_pos.normalized(), 4.0, output_volume_vector); + + for (unsigned int k = 0; k < 4; k++) { + output_volume_vector.write[k] = multiplier * output_volume_vector[k]; } - //start playing if requested - if (setplay.get() >= 0.0) { - setseek.set(setplay.get()); - active.set(); - setplay.set(-1); + Map<StringName, Vector<AudioFrame>> bus_volumes; + if (area) { + if (area->is_overriding_audio_bus()) { + //override audio bus + bus_volumes[area->get_audio_bus_name()] = output_volume_vector; + } + + if (area->is_using_reverb_bus()) { + StringName reverb_bus_name = area->get_reverb_bus(); + Vector<AudioFrame> reverb_vol; + _calc_reverb_vol(area, listener_area_pos, output_volume_vector, reverb_vol); + bus_volumes[reverb_bus_name] = reverb_vol; + } + } else { + bus_volumes[bus] = output_volume_vector; } + AudioServer::get_singleton()->set_playback_bus_volumes_linear(stream_playback, bus_volumes); - //stop playing if no longer active - if (!active.is_set()) { - set_physics_process_internal(false); - emit_signal(SNAME("finished")); + if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { + Vector3 listener_velocity; + + if (listener_is_camera) { + listener_velocity = camera->get_doppler_tracked_velocity(); + } + + Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity); + + if (local_velocity == Vector3()) { + AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale); + } else { + float approaching = local_pos.normalized().dot(local_velocity.normalized()); + float velocity = local_velocity.length(); + float speed_of_sound = 343.0; + + float doppler_pitch_scale = pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching); + doppler_pitch_scale = CLAMP(doppler_pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff + + AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, doppler_pitch_scale); + } + } else { + AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale); } } + return output_volume_vector; } void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) { - AudioServer::get_singleton()->lock(); - - mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); - if (stream_playback.is_valid()) { + stop(); stream_playback.unref(); stream.unref(); - active.clear(); - setseek.set(-1); } if (p_stream.is_valid()) { - stream = p_stream; stream_playback = p_stream->instance_playback(); + if (stream_playback.is_valid()) { + stream = p_stream; + } else { + stream.unref(); + } } - AudioServer::get_singleton()->unlock(); - if (p_stream.is_valid() && stream_playback.is_null()) { stream.unref(); } @@ -674,27 +536,22 @@ float AudioStreamPlayer3D::get_pitch_scale() const { } void AudioStreamPlayer3D::play(float p_from_pos) { - if (!is_playing()) { - // Reset the prev_output_count if the stream is stopped - prev_output_count = 0; - } - if (stream_playback.is_valid()) { setplay.set(p_from_pos); - output_ready.clear(); set_physics_process_internal(true); } } void AudioStreamPlayer3D::seek(float p_seconds) { - if (stream_playback.is_valid()) { - setseek.set(p_seconds); + if (stream_playback.is_valid() && active.is_set()) { + play(p_seconds); } } void AudioStreamPlayer3D::stop() { if (stream_playback.is_valid()) { active.clear(); + AudioServer::get_singleton()->stop_playback_stream(stream_playback); set_physics_process_internal(false); setplay.set(-1); } @@ -710,11 +567,7 @@ bool AudioStreamPlayer3D::is_playing() const { float AudioStreamPlayer3D::get_playback_position() { if (stream_playback.is_valid()) { - float ss = setseek.get(); - if (ss >= 0.0) { - return ss; - } - return stream_playback->get_playback_position(); + return AudioServer::get_singleton()->get_playback_position(stream_playback); } return 0; @@ -733,7 +586,7 @@ StringName AudioStreamPlayer3D::get_bus() const { return bus; } } - return "Master"; + return SNAME("Master"); } void AudioStreamPlayer3D::set_autoplay(bool p_enable) { @@ -876,15 +729,16 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_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; - stream_paused_fade_out = stream_paused; + if (stream_playback.is_valid()) { + AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause); } } bool AudioStreamPlayer3D::get_stream_paused() const { - return stream_paused; + if (stream_playback.is_valid()) { + return AudioServer::get_singleton()->is_playback_paused(stream_playback); + } + return false; } Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() { diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 8aec493602..f915abad2b 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -31,7 +31,7 @@ #ifndef AUDIO_STREAM_PLAYER_3D_H #define AUDIO_STREAM_PLAYER_3D_H -#include "core/templates/safe_refcount.h" +#include "scene/3d/area_3d.h" #include "scene/3d/node_3d.h" #include "scene/3d/velocity_tracker_3d.h" #include "servers/audio/audio_filter_sw.h" @@ -68,31 +68,9 @@ private: }; - struct Output { - AudioFilterSW filter; - AudioFilterSW::Processor filter_process[8]; - AudioFrame vol[4]; - float filter_gain = 0.0; - float pitch_scale = 0.0; - int bus_index = -1; - int reverb_bus_index = -1; - AudioFrame reverb_vol[4]; - Viewport *viewport = nullptr; //pointer only used for reference to previous mix - }; - - Output outputs[MAX_OUTPUTS]; - SafeNumeric<int> output_count; - SafeFlag output_ready; - - //these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks) - Output prev_outputs[MAX_OUTPUTS]; - int prev_output_count = 0; - Ref<AudioStreamPlayback> stream_playback; Ref<AudioStream> stream; - Vector<AudioFrame> mix_buffer; - SafeNumeric<float> setseek{ -1.0 }; SafeFlag active; SafeNumeric<float> setplay{ -1.0 }; @@ -102,17 +80,21 @@ private: float max_db = 3.0; float pitch_scale = 1.0; bool autoplay = false; - bool stream_paused = false; - bool stream_paused_fade_in = false; - bool stream_paused_fade_out = false; - StringName bus; + StringName bus = "Master"; + + uint64_t last_mix_count = -1; + + static void _calc_output_vol(const Vector3 &source_dir, real_t tightness, Vector<AudioFrame> &output); + + void _calc_reverb_vol(Area3D *area, Vector3 listener_area_pos, Vector<AudioFrame> direct_path_vol, Vector<AudioFrame> &reverb_vol); - static void _calc_output_vol(const Vector3 &source_dir, real_t tightness, Output &output); - void _mix_audio(); - static void _mix_audios(void *self) { reinterpret_cast<AudioStreamPlayer3D *>(self)->_mix_audio(); } + static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer3D *>(self)->_update_panning(); } void _set_playing(bool p_enable); bool _is_active() const; + StringName _get_actual_bus(); + Area3D *_get_overriding_area(); + Vector<AudioFrame> _update_panning(); void _bus_layout_changed(); diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 5315e685a0..70361f4787 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -32,7 +32,15 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const { if (property.name == "bone_name") { - Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent()); + // Because it is a constant function, we cannot use the _get_skeleton_3d function. + const Skeleton3D *parent = nullptr; + if (use_external_skeleton) { + if (external_skeleton_node_cache.is_valid()) { + parent = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); + } + } else { + parent = Object::cast_to<Skeleton3D>(get_parent()); + } if (parent) { String names; @@ -52,55 +60,321 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const { } } +bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) { + if (p_path == SNAME("override_pose")) { + set_override_pose(p_value); + } else if (p_path == SNAME("override_mode")) { + set_override_mode(p_value); + } else if (p_path == SNAME("use_external_skeleton")) { + set_use_external_skeleton(p_value); + } else if (p_path == SNAME("external_skeleton")) { + set_external_skeleton(p_value); + } + + return true; +} + +bool BoneAttachment3D::_get(const StringName &p_path, Variant &r_ret) const { + if (p_path == SNAME("override_pose")) { + r_ret = get_override_pose(); + } else if (p_path == SNAME("override_mode")) { + r_ret = get_override_mode(); + } else if (p_path == SNAME("use_external_skeleton")) { + r_ret = get_use_external_skeleton(); + } else if (p_path == SNAME("external_skeleton")) { + r_ret = get_external_skeleton(); + } + + return true; +} + +void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::BOOL, "override_pose", PROPERTY_HINT_NONE, "")); + if (override_pose) { + p_list->push_back(PropertyInfo(Variant::INT, "override_mode", PROPERTY_HINT_ENUM, "Global Pose Override, Local Pose Override, Custom Pose")); + } + + p_list->push_back(PropertyInfo(Variant::BOOL, "use_external_skeleton", PROPERTY_HINT_NONE, "")); + if (use_external_skeleton) { + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "external_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D")); + } +} + +TypedArray<String> BoneAttachment3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node3D::get_configuration_warnings(); + + if (use_external_skeleton) { + if (external_skeleton_node_cache.is_null()) { + warnings.append(TTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node.")); + } + } else { + Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent()); + if (!parent) { + warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an extenral Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); + } + } + + if (bone_idx == -1) { + warnings.append(TTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node.")); + } + + return warnings; +} + +void BoneAttachment3D::_update_external_skeleton_cache() { + external_skeleton_node_cache = ObjectID(); + if (has_node(external_skeleton_node)) { + Node *node = get_node(external_skeleton_node); + ERR_FAIL_COND_MSG(!node, "Cannot update external skeleton cache: Node cannot be found!"); + + // Make sure it's a skeleton3D + Skeleton3D *sk = Object::cast_to<Skeleton3D>(node); + ERR_FAIL_COND_MSG(!sk, "Cannot update external skeleton cache: Skeleton3D Nodepath does not point to a Skeleton3D node!"); + + external_skeleton_node_cache = node->get_instance_id(); + } else { + if (external_skeleton_node.is_empty()) { + BoneAttachment3D *parent_attachment = Object::cast_to<BoneAttachment3D>(get_parent()); + if (parent_attachment) { + parent_attachment->_update_external_skeleton_cache(); + if (parent_attachment->has_node(parent_attachment->external_skeleton_node)) { + Node *node = parent_attachment->get_node(parent_attachment->external_skeleton_node); + ERR_FAIL_COND_MSG(!node, "Cannot update external skeleton cache: Parent's Skeleton3D node cannot be found!"); + + // Make sure it's a skeleton3D + Skeleton3D *sk = Object::cast_to<Skeleton3D>(node); + ERR_FAIL_COND_MSG(!sk, "Cannot update external skeleton cache: Parent Skeleton3D Nodepath does not point to a Skeleton3D node!"); + + external_skeleton_node_cache = node->get_instance_id(); + external_skeleton_node = get_path_to(node); + } + } + } + } +} + void BoneAttachment3D::_check_bind() { - Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent()); - if (sk) { - int idx = sk->find_bone(bone_name); - if (idx != -1) { - sk->bind_child_node_to_bone(idx, this); - set_transform(sk->get_bone_global_pose(idx)); + Skeleton3D *sk = _get_skeleton3d(); + + if (sk && !bound) { + if (bone_idx <= -1) { + bone_idx = sk->find_bone(bone_name); + } + if (bone_idx != -1) { + sk->call_deferred("connect", "bone_pose_changed", callable_mp(this, &BoneAttachment3D::on_bone_pose_update)); bound = true; + call_deferred(SNAME("on_bone_pose_update"), bone_idx); + } + } +} + +Skeleton3D *BoneAttachment3D::_get_skeleton3d() { + if (use_external_skeleton) { + if (external_skeleton_node_cache.is_valid()) { + return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); + } else { + _update_external_skeleton_cache(); + if (external_skeleton_node_cache.is_valid()) { + return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); + } } + } else { + return Object::cast_to<Skeleton3D>(get_parent()); } + return nullptr; } void BoneAttachment3D::_check_unbind() { if (bound) { - Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent()); + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { - int idx = sk->find_bone(bone_name); - if (idx != -1) { - sk->unbind_child_node_from_bone(idx, this); - } + sk->disconnect(SNAME("bone_pose_changed"), callable_mp(this, &BoneAttachment3D::on_bone_pose_update)); } bound = false; } } +void BoneAttachment3D::_transform_changed() { + if (!is_inside_tree()) { + return; + } + + if (override_pose) { + Skeleton3D *sk = _get_skeleton3d(); + + ERR_FAIL_COND_MSG(!sk, "Cannot override pose: Skeleton not found!"); + ERR_FAIL_INDEX_MSG(bone_idx, sk->get_bone_count(), "Cannot override pose: Bone index is out of range!"); + + Transform3D our_trans = get_transform(); + if (use_external_skeleton) { + our_trans = sk->world_transform_to_global_pose(get_global_transform()); + } + + if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) { + sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true); + } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { + sk->set_bone_local_pose_override(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans), 1.0, true); + } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { + sk->set_bone_custom_pose(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans)); + } + } +} + void BoneAttachment3D::set_bone_name(const String &p_name) { + bone_name = p_name; + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + set_bone_idx(sk->find_bone(bone_name)); + } +} + +String BoneAttachment3D::get_bone_name() const { + return bone_name; +} + +void BoneAttachment3D::set_bone_idx(const int &p_idx) { if (is_inside_tree()) { _check_unbind(); } - bone_name = p_name; + bone_idx = p_idx; + + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + if (bone_idx <= -1 || bone_idx >= sk->get_bone_count()) { + WARN_PRINT("Bone index out of range! Cannot connect BoneAttachment to node!"); + bone_idx = -1; + } else { + bone_name = sk->get_bone_name(bone_idx); + } + } if (is_inside_tree()) { _check_bind(); } + + notify_property_list_changed(); } -String BoneAttachment3D::get_bone_name() const { - return bone_name; +int BoneAttachment3D::get_bone_idx() const { + return bone_idx; +} + +void BoneAttachment3D::set_override_pose(bool p_override) { + override_pose = p_override; + set_notify_local_transform(override_pose); + set_process_internal(override_pose); + + if (!override_pose) { + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) { + sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); + } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { + sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); + } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { + sk->set_bone_custom_pose(bone_idx, Transform3D()); + } + } + _transform_changed(); + } + notify_property_list_changed(); +} + +bool BoneAttachment3D::get_override_pose() const { + return override_pose; +} + +void BoneAttachment3D::set_override_mode(int p_mode) { + if (override_pose) { + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) { + sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); + } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { + sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); + } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { + sk->set_bone_custom_pose(bone_idx, Transform3D()); + } + } + override_mode = p_mode; + _transform_changed(); + return; + } + override_mode = p_mode; +} + +int BoneAttachment3D::get_override_mode() const { + return override_mode; +} + +void BoneAttachment3D::set_use_external_skeleton(bool p_use_external) { + use_external_skeleton = p_use_external; + + if (use_external_skeleton) { + _check_unbind(); + _update_external_skeleton_cache(); + _check_bind(); + _transform_changed(); + } + + notify_property_list_changed(); +} + +bool BoneAttachment3D::get_use_external_skeleton() const { + return use_external_skeleton; +} + +void BoneAttachment3D::set_external_skeleton(NodePath p_path) { + external_skeleton_node = p_path; + _update_external_skeleton_cache(); + notify_property_list_changed(); +} + +NodePath BoneAttachment3D::get_external_skeleton() const { + return external_skeleton_node; } void BoneAttachment3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { + if (use_external_skeleton) { + _update_external_skeleton_cache(); + } _check_bind(); } break; case NOTIFICATION_EXIT_TREE: { _check_unbind(); } break; + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + _transform_changed(); + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + if (_override_dirty) { + _override_dirty = false; + } + } + } +} + +void BoneAttachment3D::on_bone_pose_update(int p_bone_index) { + if (bone_idx == p_bone_index) { + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + if (!override_pose) { + if (use_external_skeleton) { + set_global_transform(sk->global_pose_to_world_transform(sk->get_bone_global_pose(bone_idx))); + } else { + set_transform(sk->get_bone_global_pose(bone_idx)); + } + } else { + if (!_override_dirty) { + _transform_changed(); + _override_dirty = true; + } + } + } } } @@ -111,5 +385,21 @@ void BoneAttachment3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_name", "bone_name"), &BoneAttachment3D::set_bone_name); ClassDB::bind_method(D_METHOD("get_bone_name"), &BoneAttachment3D::get_bone_name); + ClassDB::bind_method(D_METHOD("set_bone_idx", "bone_idx"), &BoneAttachment3D::set_bone_idx); + ClassDB::bind_method(D_METHOD("get_bone_idx"), &BoneAttachment3D::get_bone_idx); + + ClassDB::bind_method(D_METHOD("on_bone_pose_update", "bone_index"), &BoneAttachment3D::on_bone_pose_update); + + ClassDB::bind_method(D_METHOD("set_override_pose", "override_pose"), &BoneAttachment3D::set_override_pose); + ClassDB::bind_method(D_METHOD("get_override_pose"), &BoneAttachment3D::get_override_pose); + ClassDB::bind_method(D_METHOD("set_override_mode", "override_mode"), &BoneAttachment3D::set_override_mode); + ClassDB::bind_method(D_METHOD("get_override_mode"), &BoneAttachment3D::get_override_mode); + + ClassDB::bind_method(D_METHOD("set_use_external_skeleton", "use_external_skeleton"), &BoneAttachment3D::set_use_external_skeleton); + ClassDB::bind_method(D_METHOD("get_use_external_skeleton"), &BoneAttachment3D::get_use_external_skeleton); + ClassDB::bind_method(D_METHOD("set_external_skeleton", "external_skeleton"), &BoneAttachment3D::set_external_skeleton); + ClassDB::bind_method(D_METHOD("get_external_skeleton"), &BoneAttachment3D::get_external_skeleton); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_idx"), "set_bone_idx", "get_bone_idx"); } diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 0c6d5f12b1..cf681cace8 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -38,20 +38,59 @@ class BoneAttachment3D : public Node3D { bool bound = false; String bone_name; + int bone_idx = -1; + + bool override_pose = false; + int override_mode = 0; + bool _override_dirty = false; + + enum OVERRIDE_MODES { + MODE_GLOBAL_POSE, + MODE_LOCAL_POSE, + MODE_CUSTOM_POSE + }; + + bool use_external_skeleton = false; + NodePath external_skeleton_node; + ObjectID external_skeleton_node_cache; void _check_bind(); void _check_unbind(); + void _transform_changed(); + void _update_external_skeleton_cache(); + Skeleton3D *_get_skeleton3d(); + protected: virtual void _validate_property(PropertyInfo &property) const override; + bool _get(const StringName &p_path, Variant &r_ret) const; + bool _set(const StringName &p_path, const Variant &p_value); + void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); static void _bind_methods(); public: + virtual TypedArray<String> get_configuration_warnings() const override; + void set_bone_name(const String &p_name); String get_bone_name() const; + void set_bone_idx(const int &p_idx); + int get_bone_idx() const; + + void set_override_pose(bool p_override); + bool get_override_pose() const; + void set_override_mode(int p_mode); + int get_override_mode() const; + + void set_use_external_skeleton(bool p_external_skeleton); + bool get_use_external_skeleton() const; + void set_external_skeleton(NodePath p_skeleton); + NodePath get_external_skeleton() const; + + virtual void on_bone_pose_update(int p_bone_index); + BoneAttachment3D(); }; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 9dc1df202c..3ada9072c2 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,10 +31,8 @@ #include "camera_3d.h" #include "collision_object_3d.h" -#include "core/config/engine.h" #include "core/math/camera_matrix.h" -#include "scene/resources/material.h" -#include "scene/resources/surface_tool.h" +#include "scene/main/viewport.h" void Camera3D::_update_audio_listener_state() { } diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index b7bf5566e7..3b704944b0 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -33,9 +33,6 @@ #include "scene/3d/node_3d.h" #include "scene/3d/velocity_tracker_3d.h" -#include "scene/main/window.h" -#include "scene/resources/camera_effects.h" -#include "scene/resources/environment.h" class Camera3D : public Node3D { GDCLASS(Camera3D, Node3D); diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 75bb2995d3..e2f953974a 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -30,7 +30,6 @@ #include "collision_object_3d.h" -#include "core/config/engine.h" #include "scene/scene_string_names.h" void CollisionObject3D::_notification(int p_what) { @@ -255,10 +254,8 @@ void CollisionObject3D::_apply_enabled() { } } -void CollisionObject3D::_input_event(Node *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) { - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_input_event, p_camera, p_input_event, p_pos, p_normal, p_shape); - } +void CollisionObject3D::_input_event_call(Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) { + GDVIRTUAL_CALL(_input_event, p_camera, p_input_event, p_pos, p_normal, p_shape); emit_signal(SceneStringNames::get_singleton()->input_event, p_camera, p_input_event, p_pos, p_normal, p_shape); } @@ -454,7 +451,7 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject3D::shape_owner_clear_shapes); ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject3D::shape_find_owner); - BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "position"), PropertyInfo(Variant::VECTOR3, "normal"), PropertyInfo(Variant::INT, "shape_idx"))); + GDVIRTUAL_BIND(_input_event, "camera", "event", "position", "normal", "shape_idx"); ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "position"), PropertyInfo(Variant::VECTOR3, "normal"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 2e74d84465..1c7e205888 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -31,9 +31,8 @@ #ifndef COLLISION_OBJECT_3D_H #define COLLISION_OBJECT_3D_H +#include "scene/3d/camera_3d.h" #include "scene/3d/node_3d.h" -#include "scene/resources/shape_3d.h" -#include "servers/physics_server_3d.h" class CollisionObject3D : public Node3D { GDCLASS(CollisionObject3D, Node3D); @@ -103,7 +102,7 @@ protected: void _on_transform_changed(); friend class Viewport; - virtual void _input_event(Node *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape); + virtual void _input_event_call(Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape); virtual void _mouse_enter(); virtual void _mouse_exit(); @@ -112,6 +111,7 @@ protected: void set_only_update_transform_changes(bool p_enable); bool is_only_update_transform_changes_enabled() const; + GDVIRTUAL5(_input_event, Camera3D *, Ref<InputEvent>, Vector3, Vector3, int) public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index 42645f47d4..d5835586f9 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -32,7 +32,6 @@ #include "collision_object_3d.h" #include "core/math/geometry_2d.h" -#include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" void CollisionPolygon3D::_build_polygon() { diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index 36d4c8f4f0..d4668a24f2 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -30,12 +30,10 @@ #include "collision_shape_3d.h" -#include "core/math/quick_hull.h" #include "mesh_instance_3d.h" #include "physics_body_3d.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" -#include "servers/rendering_server.h" void CollisionShape3D::make_convex_from_siblings() { Node *p = get_parent(); diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index f69c1e38eb..cb7fe21eae 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -33,6 +33,7 @@ #include "scene/3d/node_3d.h" #include "scene/resources/shape_3d.h" + class CollisionObject3D; class CollisionShape3D : public Node3D { GDCLASS(CollisionShape3D, Node3D); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 5a358e1917..48ef41e015 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -32,8 +32,8 @@ #include "scene/3d/camera_3d.h" #include "scene/3d/gpu_particles_3d.h" +#include "scene/main/viewport.h" #include "scene/resources/particles_material.h" -#include "servers/rendering_server.h" AABB CPUParticles3D::get_aabb() const { return AABB(); @@ -212,7 +212,7 @@ TypedArray<String> CPUParticles3D::get_configuration_warnings() const { warnings.push_back(TTR("Nothing is visible because no mesh has been assigned.")); } - if (!anim_material_found && (get_param(PARAM_ANIM_SPEED) != 0.0 || get_param(PARAM_ANIM_OFFSET) != 0.0 || + if (!anim_material_found && (get_param_max(PARAM_ANIM_SPEED) != 0.0 || get_param_max(PARAM_ANIM_OFFSET) != 0.0 || get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) { warnings.push_back(TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); } @@ -263,28 +263,33 @@ real_t CPUParticles3D::get_flatness() const { return flatness; } -void CPUParticles3D::set_param(Parameter p_param, real_t p_value) { +void CPUParticles3D::set_param_min(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); - parameters[p_param] = p_value; + parameters_min[p_param] = p_value; + if (parameters_min[p_param] > parameters_max[p_param]) { + set_param_max(p_param, p_value); + } } -real_t CPUParticles3D::get_param(Parameter p_param) const { +real_t CPUParticles3D::get_param_min(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return parameters[p_param]; + return parameters_min[p_param]; } -void CPUParticles3D::set_param_randomness(Parameter p_param, real_t p_value) { +void CPUParticles3D::set_param_max(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); - - randomness[p_param] = p_value; + parameters_max[p_param] = p_value; + if (parameters_min[p_param] > parameters_max[p_param]) { + set_param_min(p_param, p_value); + } } -real_t CPUParticles3D::get_param_randomness(Parameter p_param) const { +real_t CPUParticles3D::get_param_max(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return randomness[p_param]; + return parameters_max[p_param]; } static void _adjust_curve_range(const Ref<Curve> &p_curve, real_t p_min, real_t p_max) { @@ -417,6 +422,23 @@ void CPUParticles3D::set_emission_ring_inner_radius(real_t p_radius) { emission_ring_inner_radius = p_radius; } +void CPUParticles3D::set_scale_curve_x(Ref<Curve> p_scale_curve) { + scale_curve_x = p_scale_curve; +} + +void CPUParticles3D::set_scale_curve_y(Ref<Curve> p_scale_curve) { + scale_curve_y = p_scale_curve; +} + +void CPUParticles3D::set_scale_curve_z(Ref<Curve> p_scale_curve) { + scale_curve_z = p_scale_curve; +} + +void CPUParticles3D::set_split_scale(bool p_split_scale) { + split_scale = p_split_scale; + notify_property_list_changed(); +} + real_t CPUParticles3D::get_emission_sphere_radius() const { return emission_sphere_radius; } @@ -465,6 +487,22 @@ Vector3 CPUParticles3D::get_gravity() const { return gravity; } +Ref<Curve> CPUParticles3D::get_scale_curve_x() const { + return scale_curve_x; +} + +Ref<Curve> CPUParticles3D::get_scale_curve_y() const { + return scale_curve_y; +} + +Ref<Curve> CPUParticles3D::get_scale_curve_z() const { + return scale_curve_z; +} + +bool CPUParticles3D::get_split_scale() { + return split_scale; +} + void CPUParticles3D::_validate_property(PropertyInfo &property) const { if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { property.usage = PROPERTY_USAGE_NONE; @@ -489,6 +527,10 @@ void CPUParticles3D::_validate_property(PropertyInfo &property) const { if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { property.usage = PROPERTY_USAGE_NONE; } + + if (property.name.begins_with("scale_curve_") && !split_scale) { + property.usage = PROPERTY_USAGE_NONE; + } } static uint32_t idhash(uint32_t x) { @@ -707,7 +749,7 @@ void CPUParticles3D::_particles_process(double p_delta) { if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0); - p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp((real_t)1.0, real_t(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], Math::randf()); } else { //initiate velocity spread in 3D real_t angle1_rad = Math::deg2rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * spread); @@ -731,13 +773,13 @@ void CPUParticles3D::_particles_process(double p_delta) { binormal.normalize(); Vector3 normal = binormal.cross(direction_nrm); spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z; - p.velocity = spread_direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp((real_t)1.0, real_t(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], float(Math::randf())); } - real_t base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp((real_t)1.0, p.angle_rand, randomness[PARAM_ANGLE]); + real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); 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((real_t)1.0, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1) + p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand); //animation offset (0-1) p.transform = Transform3D(); p.time = 0; p.lifetime = lifetime * (1.0 - Math::randf() * lifetime_randomness); @@ -894,26 +936,25 @@ void CPUParticles3D::_particles_process(double p_delta) { position.z = 0.0; } //apply linear acceleration - force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector3(); + force += p.velocity.length() > 0.0 ? p.velocity.normalized() * tex_linear_accel * Math::lerp(parameters_min[PARAM_LINEAR_ACCEL], parameters_max[PARAM_LINEAR_ACCEL], rand_from_seed(alt_seed)) : Vector3(); //apply radial acceleration Vector3 org = emission_xform.origin; Vector3 diff = position - org; - force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3(); - //apply tangential acceleration; + force += diff.length() > 0.0 ? diff.normalized() * (tex_radial_accel)*Math::lerp(parameters_min[PARAM_RADIAL_ACCEL], parameters_max[PARAM_RADIAL_ACCEL], rand_from_seed(alt_seed)) : Vector3(); if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { Vector2 yx = Vector2(diff.y, diff.x); Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized(); - force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : 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((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + force += crossDiff.length() > 0.0 ? crossDiff.normalized() * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : Vector3(); } //apply attractor forces p.velocity += force * local_delta; //orbit velocity if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - real_t orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]); + real_t orbit_amount = tex_orbit_velocity * Math::lerp(parameters_min[PARAM_ORBIT_VELOCITY], parameters_max[PARAM_ORBIT_VELOCITY], rand_from_seed(alt_seed)); if (orbit_amount != 0.0) { real_t ang = orbit_amount * local_delta * Math_TAU; // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix, @@ -927,9 +968,10 @@ void CPUParticles3D::_particles_process(double p_delta) { 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) { + + if (parameters_max[PARAM_DAMPING] + tex_damping > 0.0) { real_t v = p.velocity.length(); - real_t damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]); + real_t damp = tex_damping * Math::lerp(parameters_min[PARAM_DAMPING], parameters_max[PARAM_DAMPING], rand_from_seed(alt_seed)); v -= damp * local_delta; if (v < 0.0) { p.velocity = Vector3(); @@ -937,17 +979,38 @@ void CPUParticles3D::_particles_process(double p_delta) { p.velocity = p.velocity.normalized() * v; } } - real_t base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp((real_t)1.0, p.angle_rand, randomness[PARAM_ANGLE]); - base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); + real_t base_angle = (tex_angle)*Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); + base_angle += p.custom[1] * lifetime * tex_angular_velocity * Math::lerp(parameters_min[PARAM_ANGULAR_VELOCITY], parameters_max[PARAM_ANGULAR_VELOCITY], rand_from_seed(alt_seed)); p.custom[0] = Math::deg2rad(base_angle); //angle - p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp((real_t)1.0, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle + p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + p.custom[1] * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed)); //angle } //apply color //apply hue rotation - real_t tex_scale = 1.0; - if (curve_parameters[PARAM_SCALE].is_valid()) { - tex_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); + Vector3 tex_scale = Vector3(1.0, 1.0, 1.0); + if (split_scale) { + if (scale_curve_x.is_valid()) { + tex_scale.x = scale_curve_x->interpolate(tv); + } else { + tex_scale.x = 1.0; + } + if (scale_curve_y.is_valid()) { + tex_scale.y = scale_curve_y->interpolate(tv); + } else { + tex_scale.y = 1.0; + } + if (scale_curve_z.is_valid()) { + tex_scale.z = scale_curve_z->interpolate(tv); + } else { + tex_scale.z = 1.0; + } + } else { + if (curve_parameters[PARAM_SCALE].is_valid()) { + float tmp_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); + tex_scale.x = tmp_scale; + tex_scale.y = tmp_scale; + tex_scale.z = tmp_scale; + } } real_t tex_hue_variation = 0.0; @@ -955,7 +1018,7 @@ void CPUParticles3D::_particles_process(double p_delta) { tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(tv); } - real_t hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_TAU * Math::lerp(1, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]); + real_t hue_rot_angle = (tex_hue_variation)*Math_TAU * Math::lerp(parameters_min[PARAM_HUE_VARIATION], parameters_max[PARAM_HUE_VARIATION], p.hue_rot_rand); real_t hue_rot_c = Math::cos(hue_rot_angle); real_t hue_rot_s = Math::sin(hue_rot_angle); @@ -1025,13 +1088,21 @@ void CPUParticles3D::_particles_process(double p_delta) { } } + p.transform.basis = p.transform.basis.orthonormalized(); //scale by scale - real_t base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], (real_t)1.0, p.scale_rand * randomness[PARAM_SCALE]); - if (base_scale < CMP_EPSILON) { - base_scale = CMP_EPSILON; + + Vector3 base_scale = tex_scale * Math::lerp(parameters_min[PARAM_SCALE], parameters_max[PARAM_SCALE], p.scale_rand); + if (base_scale.x < CMP_EPSILON) { + base_scale.x = CMP_EPSILON; + } + if (base_scale.y < CMP_EPSILON) { + base_scale.y = CMP_EPSILON; + } + if (base_scale.z < CMP_EPSILON) { + base_scale.z = CMP_EPSILON; } - p.transform.basis.scale(Vector3(1, 1, 1) * base_scale); + p.transform.basis.scale(base_scale); if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { p.velocity.z = 0.0; @@ -1267,18 +1338,25 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { 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()); + Ref<CurveXYZTexture> scale3D = material->get_param_texture(ParticlesMaterial::PARAM_SCALE); + if (scale3D.is_valid()) { + split_scale = true; + scale_curve_x = scale3D->get_curve_x(); + scale_curve_y = scale3D->get_curve_y(); + scale_curve_z = scale3D->get_curve_z(); + } set_gravity(material->get_gravity()); set_lifetime_randomness(material->get_lifetime_randomness()); #define CONVERT_PARAM(m_param) \ - set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \ + set_param_min(m_param, material->get_param_min(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)); + set_param_max(m_param, material->get_param_max(ParticlesMaterial::m_param)); CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); @@ -1364,11 +1442,11 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &CPUParticles3D::set_flatness); ClassDB::bind_method(D_METHOD("get_flatness"), &CPUParticles3D::get_flatness); - ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &CPUParticles3D::set_param); - ClassDB::bind_method(D_METHOD("get_param", "param"), &CPUParticles3D::get_param); + ClassDB::bind_method(D_METHOD("set_param_min", "param", "value"), &CPUParticles3D::set_param_min); + ClassDB::bind_method(D_METHOD("get_param_min", "param"), &CPUParticles3D::get_param_min); - ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &CPUParticles3D::set_param_randomness); - ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &CPUParticles3D::get_param_randomness); + ClassDB::bind_method(D_METHOD("set_param_max", "param", "value"), &CPUParticles3D::set_param_max); + ClassDB::bind_method(D_METHOD("get_param_max", "param"), &CPUParticles3D::get_param_max); ClassDB::bind_method(D_METHOD("set_param_curve", "param", "curve"), &CPUParticles3D::set_param_curve); ClassDB::bind_method(D_METHOD("get_param_curve", "param"), &CPUParticles3D::get_param_curve); @@ -1415,6 +1493,18 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles3D::get_gravity); ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles3D::set_gravity); + ClassDB::bind_method(D_METHOD("get_split_scale"), &CPUParticles3D::get_split_scale); + ClassDB::bind_method(D_METHOD("set_split_scale", "split_scale"), &CPUParticles3D::set_split_scale); + + ClassDB::bind_method(D_METHOD("get_scale_curve_x"), &CPUParticles3D::get_scale_curve_x); + ClassDB::bind_method(D_METHOD("set_scale_curve_x", "scale_curve"), &CPUParticles3D::set_scale_curve_x); + + ClassDB::bind_method(D_METHOD("get_scale_curve_y"), &CPUParticles3D::get_scale_curve_y); + ClassDB::bind_method(D_METHOD("set_scale_curve_y", "scale_curve"), &CPUParticles3D::set_scale_curve_y); + + ClassDB::bind_method(D_METHOD("get_scale_curve_z"), &CPUParticles3D::get_scale_curve_z); + ClassDB::bind_method(D_METHOD("set_scale_curve_z", "scale_curve"), &CPUParticles3D::set_scale_curve_z); + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles3D::convert_from_particles); ADD_GROUP("Emission Shape", "emission_"); @@ -1439,54 +1529,58 @@ void CPUParticles3D::_bind_methods() { ADD_GROUP("Gravity", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); ADD_GROUP("Initial Velocity", "initial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY); ADD_GROUP("Angular Velocity", "angular_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", 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::FLOAT, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", 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::FLOAT, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", 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::FLOAT, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", 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::FLOAT, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", 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::FLOAT, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param_min", "get_param_min", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param_max", "get_param_max", 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::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param", "get_param", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_max", "get_param_max", 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::FLOAT, "scale_amount", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_SCALE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_amount_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_SCALE); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "split_scale"), "set_split_scale", "get_split_scale"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scale_curve_x", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_scale_curve_x", "get_scale_curve_x"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scale_curve_y", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_scale_curve_y", "get_scale_curve_y"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scale_curve_z", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_scale_curve_z", "get_scale_curve_z"); ADD_GROUP("Color", ""); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp"); ADD_GROUP("Hue Variation", "hue_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", 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::FLOAT, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_lesser"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_lesser"), "set_param_max", "get_param_max", 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::FLOAT, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", 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); BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); @@ -1527,18 +1621,30 @@ CPUParticles3D::CPUParticles3D() { set_emitting(true); set_amount(8); - set_param(PARAM_INITIAL_LINEAR_VELOCITY, 0); - set_param(PARAM_ANGULAR_VELOCITY, 0); - set_param(PARAM_ORBIT_VELOCITY, 0); - set_param(PARAM_LINEAR_ACCEL, 0); - set_param(PARAM_RADIAL_ACCEL, 0); - set_param(PARAM_TANGENTIAL_ACCEL, 0); - 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_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0); + set_param_min(PARAM_ANGULAR_VELOCITY, 0); + set_param_min(PARAM_ORBIT_VELOCITY, 0); + set_param_min(PARAM_LINEAR_ACCEL, 0); + set_param_min(PARAM_RADIAL_ACCEL, 0); + set_param_min(PARAM_TANGENTIAL_ACCEL, 0); + set_param_min(PARAM_DAMPING, 0); + set_param_min(PARAM_ANGLE, 0); + set_param_min(PARAM_SCALE, 1); + set_param_min(PARAM_HUE_VARIATION, 0); + set_param_min(PARAM_ANIM_SPEED, 0); + set_param_min(PARAM_ANIM_OFFSET, 0); + set_param_max(PARAM_INITIAL_LINEAR_VELOCITY, 0); + set_param_max(PARAM_ANGULAR_VELOCITY, 0); + set_param_max(PARAM_ORBIT_VELOCITY, 0); + set_param_max(PARAM_LINEAR_ACCEL, 0); + set_param_max(PARAM_RADIAL_ACCEL, 0); + set_param_max(PARAM_TANGENTIAL_ACCEL, 0); + set_param_max(PARAM_DAMPING, 0); + set_param_max(PARAM_ANGLE, 0); + set_param_max(PARAM_SCALE, 1); + set_param_max(PARAM_HUE_VARIATION, 0); + set_param_max(PARAM_ANIM_SPEED, 0); + set_param_max(PARAM_ANIM_OFFSET, 0); set_emission_shape(EMISSION_SHAPE_POINT); set_emission_sphere_radius(1); set_emission_box_extents(Vector3(1, 1, 1)); @@ -1549,10 +1655,6 @@ CPUParticles3D::CPUParticles3D() { 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 < PARTICLE_FLAG_MAX; i++) { particle_flags[i] = false; } diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index bb6699ddc7..160814ead4 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -31,8 +31,6 @@ #ifndef CPU_PARTICLES_H #define CPU_PARTICLES_H -#include "core/templates/rid.h" -#include "core/templates/safe_refcount.h" #include "scene/3d/visual_instance_3d.h" class CPUParticles3D : public GeometryInstance3D { @@ -156,8 +154,8 @@ private: real_t spread = 45.0; real_t flatness = 0.0; - real_t parameters[PARAM_MAX]; - real_t randomness[PARAM_MAX] = {}; + real_t parameters_min[PARAM_MAX]; + real_t parameters_max[PARAM_MAX] = {}; Ref<Curve> curve_parameters[PARAM_MAX]; Color color = Color(1, 1, 1, 1); @@ -177,6 +175,11 @@ private: real_t emission_ring_radius; real_t emission_ring_inner_radius; + Ref<Curve> scale_curve_x; + Ref<Curve> scale_curve_y; + Ref<Curve> scale_curve_z; + bool split_scale = false; + Vector3 gravity = Vector3(0, -9.8, 0); void _update_internal(); @@ -248,11 +251,11 @@ public: void set_flatness(real_t p_flatness); real_t get_flatness() const; - void set_param(Parameter p_param, real_t p_value); - real_t get_param(Parameter p_param) const; + void set_param_min(Parameter p_param, real_t p_value); + real_t get_param_min(Parameter p_param) const; - void set_param_randomness(Parameter p_param, real_t p_value); - real_t get_param_randomness(Parameter p_param) const; + void set_param_max(Parameter p_param, real_t p_value); + real_t get_param_max(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; @@ -277,6 +280,10 @@ public: void set_emission_ring_height(real_t p_height); void set_emission_ring_radius(real_t p_radius); void set_emission_ring_inner_radius(real_t p_radius); + void set_scale_curve_x(Ref<Curve> p_scale_curve); + void set_scale_curve_y(Ref<Curve> p_scale_curve); + void set_scale_curve_z(Ref<Curve> p_scale_curve); + void set_split_scale(bool p_split_scale); EmissionShape get_emission_shape() const; real_t get_emission_sphere_radius() const; @@ -289,6 +296,10 @@ public: real_t get_emission_ring_height() const; real_t get_emission_ring_radius() const; real_t get_emission_ring_inner_radius() const; + Ref<Curve> get_scale_curve_x() const; + Ref<Curve> get_scale_curve_y() const; + Ref<Curve> get_scale_curve_z() const; + bool get_split_scale(); void set_gravity(const Vector3 &p_gravity); Vector3 get_gravity() const; diff --git a/scene/3d/decal.h b/scene/3d/decal.h index 041c8f97b2..e9bda3276d 100644 --- a/scene/3d/decal.h +++ b/scene/3d/decal.h @@ -32,8 +32,6 @@ #define DECAL_H #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/texture.h" -#include "servers/rendering_server.h" class Decal : public VisualInstance3D { GDCLASS(Decal, VisualInstance3D); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 7a6410ec51..baf28ae102 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -30,11 +30,8 @@ #include "gpu_particles_3d.h" -#include "core/os/os.h" #include "scene/resources/particles_material.h" -#include "servers/rendering_server.h" - AABB GPUParticles3D::get_aabb() const { return AABB(); } @@ -315,7 +312,7 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const { } else { const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr()); if (!anim_material_found && process && - (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || + (process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { warnings.push_back(TTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); } @@ -487,6 +484,7 @@ void GPUParticles3D::set_skin(const Ref<Skin> &p_skin) { skin = p_skin; _skinning_changed(); } + Ref<Skin> GPUParticles3D::get_skin() const { return skin; } diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index 31f33fde59..5e96f660da 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -31,9 +31,7 @@ #ifndef PARTICLES_H #define PARTICLES_H -#include "core/templates/rid.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/material.h" #include "scene/resources/skin.h" class GPUParticles3D : public GeometryInstance3D { diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 8b214a9cfb..a34a30913e 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -30,7 +30,6 @@ #include "gpu_particles_collision_3d.h" -#include "core/templates/thread_work_pool.h" #include "mesh_instance_3d.h" #include "scene/3d/camera_3d.h" #include "scene/main/viewport.h" diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h index f96edd4bfc..fbf68ed6df 100644 --- a/scene/3d/gpu_particles_collision_3d.h +++ b/scene/3d/gpu_particles_collision_3d.h @@ -32,9 +32,7 @@ #define GPU_PARTICLES_COLLISION_3D_H #include "core/templates/local_vector.h" -#include "core/templates/rid.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/material.h" class GPUParticlesCollision3D : public VisualInstance3D { GDCLASS(GPUParticlesCollision3D, VisualInstance3D); diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 40f33dfa72..ab417fafdd 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -30,10 +30,6 @@ #include "light_3d.h" -#include "core/config/engine.h" -#include "core/config/project_settings.h" -#include "scene/resources/surface_tool.h" - bool Light3D::_can_gizmo_scale() const { return false; } @@ -344,7 +340,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_SHADOW_FADE_START, 0.8); set_param(PARAM_SHADOW_PANCAKE_SIZE, 20.0); set_param(PARAM_SHADOW_BLUR, 1.0); - set_param(PARAM_SHADOW_BIAS, 0.02); + set_param(PARAM_SHADOW_BIAS, 0.03); set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0); set_param(PARAM_TRANSMITTANCE_BIAS, 0.05); set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 0.1); @@ -425,8 +421,7 @@ DirectionalLight3D::DirectionalLight3D() : set_param(PARAM_SHADOW_MAX_DISTANCE, 100); set_param(PARAM_SHADOW_FADE_START, 0.8); // Increase the default shadow bias to better suit most scenes. - // Leave normal bias untouched as it doesn't benefit DirectionalLight3D as much as OmniLight3D. - set_param(PARAM_SHADOW_BIAS, 0.05); + set_param(PARAM_SHADOW_BIAS, 0.1); set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); blend_splits = false; } @@ -467,8 +462,7 @@ OmniLight3D::OmniLight3D() : Light3D(RenderingServer::LIGHT_OMNI) { set_shadow_mode(SHADOW_CUBE); // Increase the default shadow biases to better suit most scenes. - set_param(PARAM_SHADOW_BIAS, 0.1); - set_param(PARAM_SHADOW_NORMAL_BIAS, 2.0); + set_param(PARAM_SHADOW_BIAS, 0.2); } TypedArray<String> SpotLight3D::get_configuration_warnings() const { diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 51ed2fcf50..ecea60339f 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -32,8 +32,6 @@ #define LIGHT_3D_H #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/texture.h" -#include "servers/rendering_server.h" class Light3D : public VisualInstance3D { GDCLASS(Light3D, VisualInstance3D); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 0085c8933d..7dd083e314 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -31,14 +31,9 @@ #include "lightmap_gi.h" #include "core/io/config_file.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/resource_saver.h" -#include "core/math/camera_matrix.h" #include "core/math/delaunay_3d.h" -#include "core/os/os.h" -#include "core/templates/sort_array.h" #include "lightmap_probe.h" +#include "scene/3d/mesh_instance_3d.h" void LightmapGIData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) { User user; diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 8a54512383..e73350fd64 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -34,10 +34,7 @@ #include "core/templates/local_vector.h" #include "scene/3d/light_3d.h" #include "scene/3d/lightmapper.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/multimesh_instance_3d.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/sky.h" class LightmapGIData : public Resource { GDCLASS(LightmapGIData, Resource); diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index 3a6a88d435..d028628901 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -31,8 +31,9 @@ #ifndef LIGHTMAPPER_H #define LIGHTMAPPER_H -#include "scene/resources/mesh.h" -#include "servers/rendering/rendering_device.h" +#include "core/object/ref_counted.h" + +class Image; #if !defined(__aligned) diff --git a/scene/3d/listener_3d.cpp b/scene/3d/listener_3d.cpp index 43d6f262d8..1c52933ee5 100644 --- a/scene/3d/listener_3d.cpp +++ b/scene/3d/listener_3d.cpp @@ -30,7 +30,7 @@ #include "listener_3d.h" -#include "scene/resources/mesh.h" +#include "scene/main/viewport.h" void Listener3D::_update_audio_listener_state() { } diff --git a/scene/3d/listener_3d.h b/scene/3d/listener_3d.h index bcc66f167c..25eacf5135 100644 --- a/scene/3d/listener_3d.h +++ b/scene/3d/listener_3d.h @@ -32,7 +32,6 @@ #define LISTENER_3D_H #include "scene/3d/node_3d.h" -#include "scene/main/window.h" class Listener3D : public Node3D { GDCLASS(Listener3D, Node3D); diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 9ca1d55d0b..de6925244a 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -33,8 +33,6 @@ #include "collision_shape_3d.h" #include "core/core_string_names.h" #include "physics_body_3d.h" -#include "scene/resources/material.h" -#include "skeleton_3d.h" bool MeshInstance3D::_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. diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index e2d20d0a90..beb7f6cf95 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -31,10 +31,10 @@ #ifndef MESH_INSTANCE_H #define MESH_INSTANCE_H -#include "scene/3d/skeleton_3d.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/mesh.h" -#include "scene/resources/skin.h" + +class Skin; +class SkinReference; class MeshInstance3D : public GeometryInstance3D { GDCLASS(MeshInstance3D, GeometryInstance3D); diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index f890ceeb95..c2d5c757db 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -30,7 +30,6 @@ #include "navigation_agent_3d.h" -#include "core/config/engine.h" #include "servers/navigation_server_3d.h" void NavigationAgent3D::_bind_methods() { diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 56da2d1acf..bebfdc5f7e 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -31,7 +31,6 @@ #ifndef NAVIGATION_AGENT_H #define NAVIGATION_AGENT_H -#include "core/templates/vector.h" #include "scene/main/node.h" class Node3D; diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index 2f78f624a4..ab0b158303 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -32,7 +32,6 @@ #define NAVIGATION_OBSTACLE_H #include "scene/3d/node_3d.h" -#include "scene/main/node.h" class NavigationObstacle3D : public Node { GDCLASS(NavigationObstacle3D, Node); diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 2976dad39d..8a51a259f7 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -30,7 +30,6 @@ #include "navigation_region_3d.h" -#include "core/os/thread.h" #include "mesh_instance_3d.h" #include "servers/navigation_server_3d.h" diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index c2045215b1..ec7761ef93 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -32,7 +32,6 @@ #define NAVIGATION_REGION_H #include "scene/3d/node_3d.h" -#include "scene/resources/mesh.h" #include "scene/resources/navigation_mesh.h" class NavigationRegion3D : public Node3D { diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 8f186199db..12470939f5 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -30,11 +30,9 @@ #include "node_3d.h" -#include "core/config/engine.h" #include "core/object/message_queue.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/main/scene_tree.h" -#include "scene/main/window.h" +#include "scene/main/viewport.h" #include "scene/scene_string_names.h" /* diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index da54452abb..0fd0c4e205 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -32,7 +32,6 @@ #define NODE_3D_H #include "scene/main/node.h" -#include "scene/main/scene_tree.h" class Node3DGizmo : public RefCounted { GDCLASS(Node3DGizmo, RefCounted); diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 6818acf69d..9ea37e4bfa 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -30,9 +30,6 @@ #include "path_3d.h" -#include "core/config/engine.h" -#include "scene/scene_string_names.h" - void Path3D::_notification(int p_what) { } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index e1e6153bfb..0356994cdb 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -30,14 +30,8 @@ #include "physics_body_3d.h" -#include "core/config/engine.h" #include "core/core_string_names.h" -#include "core/object/class_db.h" -#include "core/templates/list.h" -#include "core/templates/rid.h" -#include "scene/3d/collision_shape_3d.h" #include "scene/scene_string_names.h" -#include "servers/navigation_server_3d.h" #ifdef TOOLS_ENABLED #include "editor/plugins/node_3d_editor_plugin.h" @@ -117,9 +111,9 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_t return Ref<KinematicCollision3D>(); } -bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, const Set<RID> &p_exclude) { +bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) { Transform3D gt = get_global_transform(); - bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_exclude); + bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude); // Restore direction of motion to be along original motion, // in order to avoid sliding due to recovery, @@ -146,27 +140,27 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M } // Check depth of recovery. - real_t projected_length = r_result.motion.dot(motion_normal); - Vector3 recovery = r_result.motion - motion_normal * projected_length; + real_t projected_length = r_result.travel.dot(motion_normal); + Vector3 recovery = r_result.travel - motion_normal * projected_length; real_t recovery_length = recovery.length(); // Fixes cases where canceling slide causes the motion to go too deep into the ground, // because we're only taking rest information into account and not general recovery. if (recovery_length < (real_t)p_margin + precision) { // Apply adjustment to motion. - r_result.motion = motion_normal * projected_length; - r_result.remainder = p_motion - r_result.motion; + r_result.travel = motion_normal * projected_length; + r_result.remainder = p_motion - r_result.travel; } } } for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { - r_result.motion[i] = 0; + r_result.travel[i] = 0; } } if (!p_test_only) { - gt.origin += r_result.motion; + gt.origin += r_result.travel; set_global_transform(gt); } @@ -289,18 +283,20 @@ bool StaticBody3D::is_sync_to_physics_enabled() const { return sync_to_physics; } -void StaticBody3D::_direct_state_changed(Object *p_state) { - PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); +void StaticBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + StaticBody3D *body = (StaticBody3D *)p_instance; + body->_body_state_changed(p_state); +} - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); +void StaticBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); if (!sync_to_physics) { return; } - last_valid_transform = state->get_transform(); + last_valid_transform = p_state->get_transform(); set_notify_local_transform(false); set_global_transform(last_valid_transform); set_notify_local_transform(true); @@ -353,7 +349,7 @@ void StaticBody3D::_notification(int p_what) { // Used by sync to physics, send the new transform to the physics... Transform3D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); + double delta_time = get_physics_process_delta_time(); new_transform.origin += constant_linear_velocity * delta_time; real_t ang_vel = constant_angular_velocity.length(); @@ -384,7 +380,7 @@ void StaticBody3D::_notification(int p_what) { Transform3D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); + double delta_time = get_physics_process_delta_time(); new_transform.origin += constant_linear_velocity * delta_time; real_t ang_vel = constant_angular_velocity.length(); @@ -464,13 +460,13 @@ void StaticBody3D::_update_kinematic_motion() { bool needs_physics_process = false; if (kinematic_motion) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody3D::_direct_state_changed)); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, &StaticBody3D::_body_state_changed_callback); if (!constant_angular_velocity.is_equal_approx(Vector3()) || !constant_linear_velocity.is_equal_approx(Vector3())) { needs_physics_process = true; } } else { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); } set_physics_process_internal(needs_physics_process); @@ -588,26 +584,27 @@ struct _RigidBodyInOut { int local_shape = 0; }; -void RigidBody3D::_direct_state_changed(Object *p_state) { -#ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); -#else - state = (PhysicsDirectBodyState3D *)p_state; //trust it -#endif +void RigidBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + RigidBody3D *body = (RigidBody3D *)p_instance; + body->_body_state_changed(p_state); +} +void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { set_ignore_transform_notification(true); - set_global_transform(state->get_transform()); - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); - inverse_inertia_tensor = state->get_inverse_inertia_tensor(); - if (sleeping != state->is_sleeping()) { - sleeping = state->is_sleeping(); + set_global_transform(p_state->get_transform()); + + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); + + inverse_inertia_tensor = p_state->get_inverse_inertia_tensor(); + + if (sleeping != p_state->is_sleeping()) { + sleeping = p_state->is_sleeping(); emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed); } - if (get_script_instance()) { - get_script_instance()->call("_integrate_forces", state); - } + + GDVIRTUAL_CALL(_integrate_forces, p_state); + set_ignore_transform_notification(false); _on_transform_changed(); @@ -623,18 +620,18 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { } } - _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(state->get_contact_count() * sizeof(_RigidBodyInOut)); + _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBodyInOut)); int toadd_count = 0; //state->get_contact_count(); RigidBody3D_RemoveAction *toremove = (RigidBody3D_RemoveAction *)alloca(rc * sizeof(RigidBody3D_RemoveAction)); int toremove_count = 0; //put the ones to add - for (int i = 0; i < state->get_contact_count(); i++) { - RID rid = state->get_contact_collider(i); - ObjectID obj = state->get_contact_collider_id(i); - int local_shape = state->get_contact_local_shape(i); - int shape = state->get_contact_collider_shape(i); + for (int i = 0; i < p_state->get_contact_count(); i++) { + RID rid = p_state->get_contact_collider(i); + ObjectID obj = p_state->get_contact_collider_id(i); + int local_shape = p_state->get_contact_local_shape(i); + int shape = p_state->get_contact_collider_shape(i); //bool found=false; @@ -689,8 +686,6 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { contact_monitor->locked = false; } - - state = nullptr; } void RigidBody3D::_notification(int p_what) { @@ -793,25 +788,15 @@ real_t RigidBody3D::get_angular_damp() const { } void RigidBody3D::set_axis_velocity(const Vector3 &p_axis) { - Vector3 v = state ? state->get_linear_velocity() : linear_velocity; Vector3 axis = p_axis.normalized(); - v -= axis * axis.dot(v); - v += p_axis; - if (state) { - set_linear_velocity(v); - } else { - PhysicsServer3D::get_singleton()->body_set_axis_velocity(get_rid(), p_axis); - linear_velocity = v; - } + linear_velocity -= axis * axis.dot(linear_velocity); + linear_velocity += p_axis; + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } void RigidBody3D::set_linear_velocity(const Vector3 &p_velocity) { linear_velocity = p_velocity; - if (state) { - state->set_linear_velocity(linear_velocity); - } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); - } + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } Vector3 RigidBody3D::get_linear_velocity() const { @@ -820,11 +805,7 @@ Vector3 RigidBody3D::get_linear_velocity() const { void RigidBody3D::set_angular_velocity(const Vector3 &p_velocity) { angular_velocity = p_velocity; - if (state) { - state->set_angular_velocity(angular_velocity); - } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); - } + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } Vector3 RigidBody3D::get_angular_velocity() const { @@ -1028,7 +1009,7 @@ void RigidBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody3D::get_colliding_bodies); - BIND_VMETHOD(MethodInfo("_integrate_forces", PropertyInfo(Variant::OBJECT, "state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectBodyState3D"))); + GDVIRTUAL_BIND(_integrate_forces, "state"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); @@ -1061,7 +1042,7 @@ void RigidBody3D::_bind_methods() { RigidBody3D::RigidBody3D() : PhysicsBody3D(PhysicsServer3D::BODY_MODE_DYNAMIC) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody3D::_direct_state_changed)); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, &RigidBody3D::_body_state_changed_callback); } RigidBody3D::~RigidBody3D() { @@ -1085,13 +1066,11 @@ void RigidBody3D::_reload_physics_characteristics() { //so, if you pass 45 as limit, avoid numerical precision errors when angle is 45. #define FLOOR_ANGLE_THRESHOLD 0.01 -void CharacterBody3D::move_and_slide() { - Vector3 body_velocity_normal = linear_velocity.normalized(); - +bool CharacterBody3D::move_and_slide() { bool was_on_floor = on_floor; // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky - float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { @@ -1117,11 +1096,11 @@ void CharacterBody3D::move_and_slide() { floor_normal = Vector3(); floor_velocity = Vector3(); - if (current_floor_velocity != Vector3() && on_floor_body.is_valid()) { + if (!current_floor_velocity.is_equal_approx(Vector3()) && on_floor_body.is_valid()) { PhysicsServer3D::MotionResult floor_result; Set<RID> exclude; exclude.insert(on_floor_body); - if (move_and_collide(current_floor_velocity * delta, floor_result, margin, false, false, exclude)) { + if (move_and_collide(current_floor_velocity * delta, floor_result, margin, false, false, false, exclude)) { motion_results.push_back(floor_result); _set_collision_direction(floor_result); } @@ -1132,43 +1111,39 @@ void CharacterBody3D::move_and_slide() { // No sliding on first attempt to keep floor motion stable when possible, // when stop on slope is enabled. - bool sliding_enabled = !stop_on_slope; + bool sliding_enabled = !floor_stop_on_slope; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionResult result; - bool found_collision = false; - bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled); - if (!collided) { - motion = Vector3(); //clear because no collision happened and motion completed - } else { - found_collision = true; - + if (collided) { motion_results.push_back(result); _set_collision_direction(result); - if (on_floor && stop_on_slope) { - if ((body_velocity_normal + up_direction).length() < 0.01) { - Transform3D gt = get_global_transform(); - if (result.motion.length() > margin) { - gt.origin -= result.motion.slide(up_direction); - } else { - gt.origin -= result.motion; - } - set_global_transform(gt); - linear_velocity = Vector3(); - return; + if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { + Transform3D gt = get_global_transform(); + if (result.travel.length() > margin) { + gt.origin -= result.travel.slide(up_direction); + } else { + gt.origin -= result.travel; } + set_global_transform(gt); + linear_velocity = Vector3(); + motion = Vector3(); + break; } - if (sliding_enabled || !on_floor) { - motion = result.remainder.slide(result.collision_normal); - linear_velocity = linear_velocity.slide(result.collision_normal); + if (result.remainder.is_equal_approx(Vector3())) { + motion = Vector3(); + break; + } - for (int j = 0; j < 3; j++) { - if (locked_axis & (1 << j)) { - linear_velocity[j] = 0.0; - } + if (sliding_enabled || !on_floor) { + Vector3 slide_motion = result.remainder.slide(result.collision_normal); + if (slide_motion.dot(linear_velocity) > 0.0) { + motion = slide_motion; + } else { + motion = Vector3(); } } else { motion = result.remainder; @@ -1177,49 +1152,54 @@ void CharacterBody3D::move_and_slide() { sliding_enabled = true; - if (!found_collision || motion == Vector3()) { + if (!collided || motion.is_equal_approx(Vector3())) { break; } } + if (was_on_floor && !on_floor && !snap.is_equal_approx(Vector3())) { + // Apply snap. + Transform3D gt = get_global_transform(); + PhysicsServer3D::MotionResult result; + if (move_and_collide(snap, result, margin, true, false, true)) { + bool apply = true; + if (up_direction != Vector3()) { + if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { + on_floor = true; + floor_normal = result.collision_normal; + on_floor_body = result.collider; + floor_velocity = result.collider_velocity; + if (floor_stop_on_slope) { + // move and collide may stray the object a bit because of pre un-stucking, + // so only ensure that motion happens on floor direction in this case. + if (result.travel.length() > margin) { + result.travel = result.travel.project(up_direction); + } else { + result.travel = Vector3(); + } + } + } else { + apply = false; //snapped with floor direction, but did not snap to a floor, do not snap. + } + } + if (apply) { + gt.origin += result.travel; + set_global_transform(gt); + } + } + } + if (!on_floor && !on_wall) { // Add last platform velocity when just left a moving platform. linear_velocity += current_floor_velocity; } - if (!was_on_floor || snap == Vector3()) { - return; + // Reset the gravity accumulation when touching the ground. + if (on_floor && linear_velocity.dot(up_direction) <= 0) { + linear_velocity = linear_velocity.slide(up_direction); } - // Apply snap. - Transform3D gt = get_global_transform(); - PhysicsServer3D::MotionResult result; - if (move_and_collide(snap, result, margin, true, false)) { - bool apply = true; - if (up_direction != Vector3()) { - if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { - on_floor = true; - floor_normal = result.collision_normal; - on_floor_body = result.collider; - floor_velocity = result.collider_velocity; - if (stop_on_slope) { - // move and collide may stray the object a bit because of pre un-stucking, - // so only ensure that motion happens on floor direction in this case. - if (result.motion.length() > margin) { - result.motion = result.motion.project(up_direction); - } else { - result.motion = Vector3(); - } - } - } else { - apply = false; //snapped with floor direction, but did not snap to a floor, do not snap. - } - } - if (apply) { - gt.origin += result.motion; - set_global_transform(gt); - } - } + return motion_results.size() > 0; } void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResult &p_result) { @@ -1227,17 +1207,20 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu //all is a wall on_wall = true; } else { - if (Math::acos(p_result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor + if (p_result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor on_floor = true; floor_normal = p_result.collision_normal; on_floor_body = p_result.collider; floor_velocity = p_result.collider_velocity; - } else if (Math::acos(p_result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling + } else if (p_result.get_angle(-up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling on_ceiling = true; } else { on_wall = true; - on_floor_body = p_result.collider; - floor_velocity = p_result.collider_velocity; + // Don't apply wall velocity when the collider is a CharacterBody3D. + if (Object::cast_to<CharacterBody3D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) { + on_floor_body = p_result.collider; + floor_velocity = p_result.collider_velocity; + } } } } @@ -1262,23 +1245,40 @@ bool CharacterBody3D::is_on_floor() const { return on_floor; } +bool CharacterBody3D::is_on_floor_only() const { + return on_floor && !on_wall && !on_ceiling; +} + bool CharacterBody3D::is_on_wall() const { return on_wall; } +bool CharacterBody3D::is_on_wall_only() const { + return on_wall && !on_floor && !on_ceiling; +} + bool CharacterBody3D::is_on_ceiling() const { return on_ceiling; } +bool CharacterBody3D::is_on_ceiling_only() const { + return on_ceiling && !on_floor && !on_wall; +} + Vector3 CharacterBody3D::get_floor_normal() const { return floor_normal; } -Vector3 CharacterBody3D::get_floor_velocity() const { +real_t CharacterBody3D::get_floor_angle(const Vector3 &p_up_direction) const { + ERR_FAIL_COND_V(p_up_direction == Vector3(), 0); + return Math::acos(floor_normal.dot(p_up_direction)); +} + +Vector3 CharacterBody3D::get_platform_velocity() const { return floor_velocity; } -int CharacterBody3D::get_slide_count() const { +int CharacterBody3D::get_slide_collision_count() const { return motion_results.size(); } @@ -1302,12 +1302,19 @@ Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) { return slide_colliders[p_bounce]; } -bool CharacterBody3D::is_stop_on_slope_enabled() const { - return stop_on_slope; +Ref<KinematicCollision3D> CharacterBody3D::_get_last_slide_collision() { + if (motion_results.size() == 0) { + return Ref<KinematicCollision3D>(); + } + return _get_slide_collision(motion_results.size() - 1); +} + +bool CharacterBody3D::is_floor_stop_on_slope_enabled() const { + return floor_stop_on_slope; } -void CharacterBody3D::set_stop_on_slope_enabled(bool p_enabled) { - stop_on_slope = p_enabled; +void CharacterBody3D::set_floor_stop_on_slope_enabled(bool p_enabled) { + floor_stop_on_slope = p_enabled; } int CharacterBody3D::get_max_slides() const { @@ -1365,8 +1372,8 @@ void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin); - ClassDB::bind_method(D_METHOD("is_stop_on_slope_enabled"), &CharacterBody3D::is_stop_on_slope_enabled); - ClassDB::bind_method(D_METHOD("set_stop_on_slope_enabled", "enabled"), &CharacterBody3D::set_stop_on_slope_enabled); + ClassDB::bind_method(D_METHOD("is_floor_stop_on_slope_enabled"), &CharacterBody3D::is_floor_stop_on_slope_enabled); + ClassDB::bind_method(D_METHOD("set_floor_stop_on_slope_enabled", "enabled"), &CharacterBody3D::set_floor_stop_on_slope_enabled); ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody3D::get_max_slides); ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody3D::set_max_slides); ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody3D::get_floor_max_angle); @@ -1377,20 +1384,26 @@ void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody3D::set_up_direction); ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody3D::is_on_floor); + ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody3D::is_on_floor_only); ClassDB::bind_method(D_METHOD("is_on_ceiling"), &CharacterBody3D::is_on_ceiling); + ClassDB::bind_method(D_METHOD("is_on_ceiling_only"), &CharacterBody3D::is_on_ceiling_only); ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody3D::is_on_wall); + ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody3D::is_on_wall_only); ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody3D::get_floor_normal); - ClassDB::bind_method(D_METHOD("get_floor_velocity"), &CharacterBody3D::get_floor_velocity); + ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody3D::get_floor_angle, DEFVAL(Vector3(0.0, 1.0, 0.0))); + ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody3D::get_platform_velocity); - ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody3D::get_slide_count); + ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody3D::get_slide_collision_count); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody3D::_get_slide_collision); + ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody3D::_get_last_slide_collision); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_RANGE, "1,8,1,or_greater"), "set_max_slides", "get_max_slides"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "snap"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); + ADD_GROUP("Floor", "floor_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); } @@ -1417,13 +1430,18 @@ Vector3 KinematicCollision3D::get_normal() const { } Vector3 KinematicCollision3D::get_travel() const { - return result.motion; + return result.travel; } Vector3 KinematicCollision3D::get_remainder() const { return result.remainder; } +real_t KinematicCollision3D::get_angle(const Vector3 &p_up_direction) const { + ERR_FAIL_COND_V(p_up_direction == Vector3(), 0); + return result.get_angle(p_up_direction); +} + Object *KinematicCollision3D::get_local_shape() const { if (!owner) { return nullptr; @@ -1478,6 +1496,7 @@ void KinematicCollision3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_normal"), &KinematicCollision3D::get_normal); ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision3D::get_travel); ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision3D::get_remainder); + ClassDB::bind_method(D_METHOD("get_angle", "up_direction"), &KinematicCollision3D::get_angle, DEFVAL(Vector3(0.0, 1.0, 0.0))); ClassDB::bind_method(D_METHOD("get_local_shape"), &KinematicCollision3D::get_local_shape); ClassDB::bind_method(D_METHOD("get_collider"), &KinematicCollision3D::get_collider); ClassDB::bind_method(D_METHOD("get_collider_id"), &KinematicCollision3D::get_collider_id); @@ -2205,7 +2224,6 @@ void PhysicalBone3D::_notification(int p_what) { if (parent_skeleton) { if (-1 != bone_id) { parent_skeleton->unbind_physical_bone_from_bone(bone_id); - parent_skeleton->unbind_child_node_from_bone(bone_id, this); bone_id = -1; } } @@ -2221,23 +2239,19 @@ void PhysicalBone3D::_notification(int p_what) { } } -void PhysicalBone3D::_direct_state_changed(Object *p_state) { +void PhysicalBone3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + PhysicalBone3D *bone = (PhysicalBone3D *)p_instance; + bone->_body_state_changed(p_state); +} + +void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { if (!simulate_physics || !_internal_simulate_physics) { return; } - /// Update bone transform - - PhysicsDirectBodyState3D *state; - -#ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); -#else - state = (PhysicsDirectBodyState3D *)p_state; //trust it -#endif + /// Update bone transform. - Transform3D global_transform(state->get_transform()); + Transform3D global_transform(p_state->get_transform()); set_ignore_transform_notification(true); set_global_transform(global_transform); @@ -2662,7 +2676,6 @@ void PhysicalBone3D::update_bone_id() { if (-1 != bone_id) { // Assert the unbind from old node parent_skeleton->unbind_physical_bone_from_bone(bone_id); - parent_skeleton->unbind_child_node_from_bone(bone_id, this); } bone_id = new_bone_id; @@ -2700,7 +2713,7 @@ void PhysicalBone3D::_start_physics_simulation() { set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &PhysicalBone3D::_direct_state_changed)); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, &PhysicalBone3D::_body_state_changed_callback); set_as_top_level(true); _internal_simulate_physics = true; } @@ -2719,7 +2732,7 @@ void PhysicalBone3D::_stop_physics_simulation() { PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0); } if (_internal_simulate_physics) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); parent_skeleton->set_bone_global_pose_override(bone_id, Transform3D(), 0.0, false); set_as_top_level(false); _internal_simulate_physics = false; diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index b076560ead..8c09f77846 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -53,7 +53,7 @@ protected: Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001); public: - bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, const Set<RID> &p_exclude = Set<RID>()); + bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()); bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001); void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); @@ -86,7 +86,8 @@ class StaticBody3D : public PhysicsBody3D { Transform3D last_valid_transform; - void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + void _body_state_changed(PhysicsDirectBodyState3D *p_state); protected: void _notification(int p_what); @@ -132,9 +133,10 @@ public: MODE_KINEMATIC, }; + GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) + protected: bool can_sleep = true; - PhysicsDirectBodyState3D *state = nullptr; Mode mode = MODE_DYNAMIC; real_t mass = 1.0; @@ -195,7 +197,8 @@ protected: void _body_exit_tree(ObjectID p_id); void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); - virtual void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); void _notification(int p_what); static void _bind_methods(); @@ -278,7 +281,7 @@ class CharacterBody3D : public PhysicsBody3D { private: real_t margin = 0.001; - bool stop_on_slope = false; + bool floor_stop_on_slope = false; int max_slides = 4; real_t floor_max_angle = Math::deg2rad((real_t)45.0); Vector3 snap; @@ -296,14 +299,15 @@ private: Vector<Ref<KinematicCollision3D>> slide_colliders; Ref<KinematicCollision3D> _get_slide_collision(int p_bounce); + Ref<KinematicCollision3D> _get_last_slide_collision(); void _set_collision_direction(const PhysicsServer3D::MotionResult &p_result); void set_safe_margin(real_t p_margin); real_t get_safe_margin() const; - bool is_stop_on_slope_enabled() const; - void set_stop_on_slope_enabled(bool p_enabled); + bool is_floor_stop_on_slope_enabled() const; + void set_floor_stop_on_slope_enabled(bool p_enabled); int get_max_slides() const; void set_max_slides(int p_max_slides); @@ -322,18 +326,22 @@ protected: static void _bind_methods(); public: - void move_and_slide(); + bool move_and_slide(); virtual Vector3 get_linear_velocity() const override; void set_linear_velocity(const Vector3 &p_velocity); bool is_on_floor() const; + bool is_on_floor_only() const; bool is_on_wall() const; + bool is_on_wall_only() const; bool is_on_ceiling() const; + bool is_on_ceiling_only() const; Vector3 get_floor_normal() const; - Vector3 get_floor_velocity() const; + real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; + Vector3 get_platform_velocity() const; - int get_slide_count() const; + int get_slide_collision_count() const; PhysicsServer3D::MotionResult get_slide_collision(int p_bounce) const; CharacterBody3D(); @@ -356,6 +364,7 @@ public: Vector3 get_normal() const; Vector3 get_travel() const; Vector3 get_remainder() const; + real_t get_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; Object *get_local_shape() const; Object *get_collider() const; ObjectID get_collider_id() const; @@ -517,7 +526,8 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); - void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + void _body_state_changed(PhysicsDirectBodyState3D *p_state); static void _bind_methods(); diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/physics_joint_3d.cpp index 59440bd1a8..12938946a0 100644 --- a/scene/3d/physics_joint_3d.cpp +++ b/scene/3d/physics_joint_3d.cpp @@ -259,11 +259,11 @@ real_t PinJoint3D::get_param(Param p_param) const { void PinJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) { Vector3 pinpos = get_global_transform().origin; - Vector3 local_a = body_a->get_global_transform().affine_inverse().xform(pinpos); + Vector3 local_a = body_a->to_local(pinpos); Vector3 local_b; if (body_b) { - local_b = body_b->get_global_transform().affine_inverse().xform(pinpos); + local_b = body_b->to_local(pinpos); } else { local_b = pinpos; } diff --git a/scene/3d/position_3d.cpp b/scene/3d/position_3d.cpp index b231ba0df7..9747465103 100644 --- a/scene/3d/position_3d.cpp +++ b/scene/3d/position_3d.cpp @@ -29,7 +29,6 @@ /*************************************************************************/ #include "position_3d.h" -#include "scene/resources/mesh.h" Position3D::Position3D() { } diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index e757d6d3f4..fd4c6e7416 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -31,9 +31,7 @@ #include "ray_cast_3d.h" #include "collision_object_3d.h" -#include "core/config/engine.h" #include "mesh_instance_3d.h" -#include "servers/physics_server_3d.h" void RayCast3D::set_target_position(const Vector3 &p_point) { target_position = p_point; diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 4bf20c89c5..d1b9b12f65 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -32,9 +32,6 @@ #define REFLECTIONPROBE_H #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/sky.h" -#include "scene/resources/texture.h" -#include "servers/rendering_server.h" class ReflectionProbe : public VisualInstance3D { GDCLASS(ReflectionProbe, VisualInstance3D); diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 86e2af7df5..94fb49ae81 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -30,11 +30,10 @@ #include "skeleton_3d.h" -#include "core/config/engine.h" -#include "core/config/project_settings.h" #include "core/object/message_queue.h" #include "core/variant/type_info.h" #include "scene/3d/physics_body_3d.h" +#include "scene/resources/skeleton_modification_3d.h" #include "scene/resources/surface_tool.h" #include "scene/scene_string_names.h" @@ -72,6 +71,13 @@ SkinReference::~SkinReference() { bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; +#ifndef _3D_DISABLED + if (path.begins_with("modification_stack")) { + set_modification_stack(p_value); + return true; + } +#endif //_3D_DISABLED + if (!path.begins_with("bones/")) { return false; } @@ -104,6 +110,13 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { String path = p_path; +#ifndef _3D_DISABLED + if (path.begins_with("modification_stack")) { + r_ret = modification_stack; + return true; + } +#endif //_3D_DISABLED + if (!path.begins_with("bones/")) { return false; } @@ -139,6 +152,14 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } + +#ifndef _3D_DISABLED + p_list->push_back( + PropertyInfo(Variant::OBJECT, "modification_stack", + PROPERTY_HINT_RESOURCE_TYPE, + "SkeletonModificationStack3D", + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); +#endif //_3D_DISABLED } void Skeleton3D::_update_process_order() { @@ -149,47 +170,32 @@ void Skeleton3D::_update_process_order() { Bone *bonesptr = bones.ptrw(); int len = bones.size(); - process_order.resize(len); - int *order = process_order.ptrw(); + parentless_bones.clear(); + + for (int i = 0; i < len; i++) { + bonesptr[i].child_bones.clear(); + } + for (int i = 0; i < len; i++) { if (bonesptr[i].parent >= len) { //validate this just in case ERR_PRINT("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent)); bonesptr[i].parent = -1; } - order[i] = i; - bonesptr[i].sort_index = i; - } - //now check process order - int pass_count = 0; - while (pass_count < len * len) { - //using bubblesort because of simplicity, it won't run every frame though. - //bublesort worst case is O(n^2), and this may be an infinite loop if cyclic - bool swapped = false; - for (int i = 0; i < len; i++) { - int parent_idx = bonesptr[order[i]].parent; - if (parent_idx < 0) { - continue; //do nothing because it has no parent - } - //swap indices - int parent_order = bonesptr[parent_idx].sort_index; - if (parent_order > i) { - bonesptr[order[i]].sort_index = parent_order; - bonesptr[parent_idx].sort_index = i; - //swap order - SWAP(order[i], order[parent_order]); - swapped = true; - } - } - if (!swapped) { - break; - } - pass_count++; - } + if (bonesptr[i].parent != -1) { + int parent_bone_idx = bonesptr[i].parent; - if (pass_count == len * len) { - ERR_PRINT("Skeleton3D parenthood graph is cyclic"); + // Check to see if this node is already added to the parent: + if (bonesptr[parent_bone_idx].child_bones.find(i) < 0) { + // Add the child node + bonesptr[parent_bone_idx].child_bones.push_back(i); + } else { + ERR_PRINT("Skeleton3D parenthood graph is cyclic"); + } + } else { + parentless_bones.push_back(i); + } } process_order_dirty = false; @@ -200,78 +206,12 @@ void Skeleton3D::_notification(int p_what) { case NOTIFICATION_UPDATE_SKELETON: { RenderingServer *rs = RenderingServer::get_singleton(); Bone *bonesptr = bones.ptrw(); - int len = bones.size(); - _update_process_order(); - - const int *order = process_order.ptr(); - - for (int i = 0; i < len; i++) { - Bone &b = bonesptr[order[i]]; - - if (b.disable_rest) { - if (b.enabled) { - Transform3D pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * pose; - b.pose_global_no_override = bonesptr[b.parent].pose_global * pose; - } else { - b.pose_global = pose; - b.pose_global_no_override = pose; - } - } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global; - b.pose_global_no_override = bonesptr[b.parent].pose_global; - } else { - b.pose_global = Transform3D(); - b.pose_global_no_override = Transform3D(); - } - } - - } else { - if (b.enabled) { - Transform3D pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); - b.pose_global_no_override = bonesptr[b.parent].pose_global * (b.rest * pose); - } else { - b.pose_global = b.rest * pose; - b.pose_global_no_override = b.rest * pose; - } - } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - b.pose_global_no_override = bonesptr[b.parent].pose_global * b.rest; - } else { - b.pose_global = b.rest; - b.pose_global_no_override = b.rest; - } - } - } - - if (b.global_pose_override_amount >= CMP_EPSILON) { - b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); - } - - if (b.global_pose_override_reset) { - b.global_pose_override_amount = 0.0; - } + int len = bones.size(); + dirty = false; - for (const ObjectID &E : b.nodes_bound) { - Object *obj = ObjectDB::get_instance(E); - ERR_CONTINUE(!obj); - Node3D *node_3d = Object::cast_to<Node3D>(obj); - ERR_CONTINUE(!node_3d); - node_3d->set_transform(b.pose_global); - } - } + // Update bone transforms + force_update_all_bone_transforms(); //update skins for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { @@ -329,32 +269,53 @@ void Skeleton3D::_notification(int p_what) { } } - dirty = false; - #ifdef TOOLS_ENABLED emit_signal(SceneStringNames::get_singleton()->pose_updated); #endif // TOOLS_ENABLED } break; +#ifndef _3D_DISABLED case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { // This is active only if the skeleton animates the physical bones // and the state of the bone is not active. - if (animate_physical_bones) { - for (int i = 0; i < bones.size(); i += 1) { - if (bones[i].physical_bone) { - if (bones[i].physical_bone->is_simulating_physics() == false) { - bones[i].physical_bone->reset_to_rest_position(); + if (Engine::get_singleton()->is_editor_hint()) { + if (animate_physical_bones) { + for (int i = 0; i < bones.size(); i += 1) { + if (bones[i].physical_bone) { + if (bones[i].physical_bone->is_simulating_physics() == false) { + bones[i].physical_bone->reset_to_rest_position(); + } } } } } + + if (modification_stack.is_valid()) { + execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process); + } + } break; +#endif // _3D_DISABLED + +#ifndef _3D_DISABLED + case NOTIFICATION_INTERNAL_PROCESS: { + if (modification_stack.is_valid()) { + execute_modifications(get_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_process); + } + } break; +#endif // _3D_DISABLED + +#ifndef _3D_DISABLED case NOTIFICATION_READY: { - if (Engine::get_singleton()->is_editor_hint()) { - set_physics_process_internal(true); + set_physics_process_internal(true); + set_process_internal(true); + + if (modification_stack.is_valid()) { + set_modification_stack(modification_stack); } } break; +#endif // _3D_DISABLED } } @@ -367,15 +328,23 @@ void Skeleton3D::clear_bones_global_pose_override() { } void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].global_pose_override_amount = p_amount; bones.write[p_bone].global_pose_override = p_pose; bones.write[p_bone].global_pose_override_reset = !p_persistent; _make_dirty(); } +Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + return bones[p_bone].global_pose_override; +} + Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); if (dirty) { const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); } @@ -383,13 +352,107 @@ Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const { } Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); if (dirty) { const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); } return bones[p_bone].pose_global_no_override; } +void Skeleton3D::clear_bones_local_pose_override() { + for (int i = 0; i < bones.size(); i += 1) { + bones.write[i].local_pose_override_amount = 0; + } + _make_dirty(); +} + +void Skeleton3D::set_bone_local_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].local_pose_override_amount = p_amount; + bones.write[p_bone].local_pose_override = p_pose; + bones.write[p_bone].local_pose_override_reset = !p_persistent; + _make_dirty(); +} + +Transform3D Skeleton3D::get_bone_local_pose_override(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + return bones[p_bone].local_pose_override; +} + +void Skeleton3D::update_bone_rest_forward_vector(int p_bone, bool p_force_update) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + if (bones[p_bone].rest_bone_forward_vector.length_squared() > 0 && p_force_update == false) { + update_bone_rest_forward_axis(p_bone, p_force_update); + } + + // If it is a child/leaf bone... + if (get_bone_parent(p_bone) > 0) { + bones.write[p_bone].rest_bone_forward_vector = bones[p_bone].rest.origin.normalized(); + } else { + // If it has children... + Vector<int> child_bones = get_bone_children(p_bone); + if (child_bones.size() > 0) { + Vector3 combined_child_dir = Vector3(0, 0, 0); + for (int i = 0; i < child_bones.size(); i++) { + combined_child_dir += bones[child_bones[i]].rest.origin.normalized(); + } + combined_child_dir = combined_child_dir / child_bones.size(); + bones.write[p_bone].rest_bone_forward_vector = combined_child_dir.normalized(); + } else { + WARN_PRINT_ONCE("Cannot calculate forward direction for bone " + itos(p_bone)); + WARN_PRINT_ONCE("Assuming direction of (0, 1, 0) for bone"); + bones.write[p_bone].rest_bone_forward_vector = Vector3(0, 1, 0); + } + } + update_bone_rest_forward_axis(p_bone, p_force_update); +} + +void Skeleton3D::update_bone_rest_forward_axis(int p_bone, bool p_force_update) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + if (bones[p_bone].rest_bone_forward_axis > -1 && p_force_update == false) { + return; + } + + Vector3 forward_axis_absolute = bones[p_bone].rest_bone_forward_vector.abs(); + if (forward_axis_absolute.x > forward_axis_absolute.y && forward_axis_absolute.x > forward_axis_absolute.z) { + if (bones[p_bone].rest_bone_forward_vector.x > 0) { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_X_FORWARD; + } else { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_X_FORWARD; + } + } else if (forward_axis_absolute.y > forward_axis_absolute.x && forward_axis_absolute.y > forward_axis_absolute.z) { + if (bones[p_bone].rest_bone_forward_vector.y > 0) { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_Y_FORWARD; + } else { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_Y_FORWARD; + } + } else { + if (bones[p_bone].rest_bone_forward_vector.z > 0) { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_Z_FORWARD; + } else { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_Z_FORWARD; + } + } +} + +Vector3 Skeleton3D::get_bone_axis_forward_vector(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3(0, 0, 0)); + return bones[p_bone].rest_bone_forward_vector; +} + +int Skeleton3D::get_bone_axis_forward_enum(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, -1); + return bones[p_bone].rest_bone_forward_axis; +} + // skeleton creation api void Skeleton3D::add_bone(const String &p_name) { ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1); @@ -418,14 +481,15 @@ int Skeleton3D::find_bone(const String &p_name) const { } String Skeleton3D::get_bone_name(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), ""); - + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, ""); return bones[p_bone].name; } void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); - for (int i = 0; i < bones.size(); i++) { + for (int i = 0; i < bone_size; i++) { if (i != p_bone) { ERR_FAIL_COND(bones[i].name == p_name); } @@ -453,7 +517,8 @@ int Skeleton3D::get_bone_count() const { } void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); bones.write[p_bone].parent = p_parent; @@ -462,7 +527,8 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { } void Skeleton3D::unparent_bone_and_rest(int p_bone) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); _update_process_order(); @@ -479,76 +545,93 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { } void Skeleton3D::set_bone_disable_rest(int p_bone, bool p_disable) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].disable_rest = p_disable; } bool Skeleton3D::is_bone_rest_disabled(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), false); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, false); return bones[p_bone].disable_rest; } int Skeleton3D::get_bone_parent(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), -1); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, -1); return bones[p_bone].parent; } -void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) { - ERR_FAIL_INDEX(p_bone, bones.size()); +Vector<int> Skeleton3D::get_bone_children(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Vector<int>()); + return bones[p_bone].child_bones; +} - bones.write[p_bone].rest = p_rest; +void Skeleton3D::set_bone_children(int p_bone, Vector<int> p_children) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].child_bones = p_children; + + process_order_dirty = true; _make_dirty(); } -Transform3D Skeleton3D::get_bone_rest(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D()); - return bones[p_bone].rest; +void Skeleton3D::add_bone_child(int p_bone, int p_child) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].child_bones.push_back(p_child); + + process_order_dirty = true; + _make_dirty(); } -void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { - ERR_FAIL_INDEX(p_bone, bones.size()); +void Skeleton3D::remove_bone_child(int p_bone, int p_child) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].enabled = p_enabled; + int child_idx = bones[p_bone].child_bones.find(p_child); + if (child_idx >= 0) { + bones.write[p_bone].child_bones.remove(child_idx); + } else { + WARN_PRINT("Cannot remove child bone: Child bone not found."); + } + + process_order_dirty = true; _make_dirty(); } -bool Skeleton3D::is_bone_enabled(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), false); - return bones[p_bone].enabled; +Vector<int> Skeleton3D::get_parentless_bones() { + return parentless_bones; } -void Skeleton3D::bind_child_node_to_bone(int p_bone, Node *p_node) { - ERR_FAIL_NULL(p_node); - ERR_FAIL_INDEX(p_bone, bones.size()); - - ObjectID id = p_node->get_instance_id(); +void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); - for (const ObjectID &E : bones[p_bone].nodes_bound) { - if (E == id) { - return; // already here - } - } + bones.write[p_bone].rest = p_rest; + _make_dirty(); +} +Transform3D Skeleton3D::get_bone_rest(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - bones.write[p_bone].nodes_bound.push_back(id); + return bones[p_bone].rest; } -void Skeleton3D::unbind_child_node_from_bone(int p_bone, Node *p_node) { - ERR_FAIL_NULL(p_node); - ERR_FAIL_INDEX(p_bone, bones.size()); +void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); - ObjectID id = p_node->get_instance_id(); - bones.write[p_bone].nodes_bound.erase(id); + bones.write[p_bone].enabled = p_enabled; + _make_dirty(); } -void Skeleton3D::get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) const { - ERR_FAIL_INDEX(p_bone, bones.size()); - - for (const ObjectID &E : bones[p_bone].nodes_bound) { - Object *obj = ObjectDB::get_instance(E); - ERR_CONTINUE(!obj); - p_bound->push_back(Object::cast_to<Node>(obj)); - } +bool Skeleton3D::is_bone_enabled(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, false); + return bones[p_bone].enabled; } void Skeleton3D::clear_bones() { @@ -561,7 +644,8 @@ void Skeleton3D::clear_bones() { // posing api void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].pose = p_pose; if (is_inside_tree()) { @@ -569,12 +653,14 @@ void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) { } } Transform3D Skeleton3D::get_bone_pose(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); return bones[p_bone].pose; } void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); //ERR_FAIL_COND( !is_inside_scene() ); bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform3D()); @@ -584,7 +670,8 @@ void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_po } Transform3D Skeleton3D::get_bone_custom_pose(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform3D()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); return bones[p_bone].custom_pose; } @@ -597,24 +684,22 @@ void Skeleton3D::_make_dirty() { dirty = true; } -int Skeleton3D::get_process_order(int p_idx) { - ERR_FAIL_INDEX_V(p_idx, bones.size(), -1); +void Skeleton3D::localize_rests() { _update_process_order(); - return process_order[p_idx]; -} -Vector<int> Skeleton3D::get_bone_process_orders() { - _update_process_order(); - return process_order; -} + Vector<int> bones_to_process = get_parentless_bones(); + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + bones_to_process.erase(current_bone_idx); -void Skeleton3D::localize_rests() { - _update_process_order(); + if (bones[current_bone_idx].parent >= 0) { + set_bone_rest(current_bone_idx, bones[bones[current_bone_idx].parent].rest.affine_inverse() * bones[current_bone_idx].rest); + } - for (int i = bones.size() - 1; i >= 0; i--) { - int idx = process_order[i]; - if (bones[idx].parent >= 0) { - set_bone_rest(idx, bones[bones[idx].parent].rest.affine_inverse() * bones[idx].rest); + // Add the bone's children to the list of bones to be processed + int child_bone_size = bones[current_bone_idx].child_bones.size(); + for (int i = 0; i < child_bone_size; i++) { + bones_to_process.push_back(bones[current_bone_idx].child_bones[i]); } } } @@ -641,7 +726,8 @@ bool Skeleton3D::get_animate_physical_bones() const { } void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); ERR_FAIL_COND(bones[p_bone].physical_bone); ERR_FAIL_COND(!p_physical_bone); bones.write[p_bone].physical_bone = p_physical_bone; @@ -650,20 +736,23 @@ void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physic } void Skeleton3D::unbind_physical_bone_from_bone(int p_bone) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].physical_bone = nullptr; _rebuild_physical_bones_cache(); } PhysicalBone3D *Skeleton3D::get_physical_bone(int p_bone) { - ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); return bones[p_bone].physical_bone; } PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) { - ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); if (bones[p_bone].cache_parent_physical_bone) { return bones[p_bone].cache_parent_physical_bone; @@ -673,7 +762,8 @@ PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) { } PhysicalBone3D *Skeleton3D::_get_physical_bone_parent(int p_bone) { - ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); const int parent_bone = bones[p_bone].parent; if (0 > parent_bone) { @@ -804,15 +894,20 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { // pose changed, rebuild cache of inverses const Bone *bonesptr = bones.ptr(); int len = bones.size(); - const int *order = process_order.ptr(); // calculate global rests and invert them - for (int i = 0; i < len; i++) { - const Bone &b = bonesptr[order[i]]; + Vector<int> bones_to_process = get_parentless_bones(); + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + bones_to_process.erase(current_bone_idx); + const Bone &b = bonesptr[current_bone_idx]; + + // Note: the code below may not work by default. May need to track an integer for the bone pose index order + // in the while loop, instead of using current_bone_idx. if (b.parent >= 0) { - skin->set_bind_pose(order[i], skin->get_bind_pose(b.parent) * b.rest); + skin->set_bind_pose(current_bone_idx, skin->get_bind_pose(b.parent) * b.rest); } else { - skin->set_bind_pose(order[i], b.rest); + skin->set_bind_pose(current_bone_idx, b.rest); } } @@ -843,17 +938,202 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { return skin_ref; } +void Skeleton3D::force_update_all_bone_transforms() { + _update_process_order(); + + for (int i = 0; i < parentless_bones.size(); i++) { + force_update_bone_children_transforms(parentless_bones[i]); + } +} + +void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone_idx, bone_size); + + Bone *bonesptr = bones.ptrw(); + List<int> bones_to_process = List<int>(); + bones_to_process.push_back(p_bone_idx); + + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + bones_to_process.erase(current_bone_idx); + + Bone &b = bonesptr[current_bone_idx]; + + if (b.disable_rest) { + if (b.enabled) { + Transform3D pose = b.pose; + if (b.custom_pose_enable) { + pose = b.custom_pose * pose; + } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * pose; + b.pose_global_no_override = b.pose_global; + } else { + b.pose_global = pose; + b.pose_global_no_override = b.pose_global; + } + } else { + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global; + b.pose_global_no_override = b.pose_global; + } else { + b.pose_global = Transform3D(); + b.pose_global_no_override = b.pose_global; + } + } + + } else { + if (b.enabled) { + Transform3D pose = b.pose; + if (b.custom_pose_enable) { + pose = b.custom_pose * pose; + } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); + b.pose_global_no_override = b.pose_global; + } else { + b.pose_global = b.rest * pose; + b.pose_global_no_override = b.pose_global; + } + } else { + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * b.rest; + b.pose_global_no_override = b.pose_global; + } else { + b.pose_global = b.rest; + b.pose_global_no_override = b.pose_global; + } + } + } + + if (b.local_pose_override_amount >= CMP_EPSILON) { + Transform3D override_local_pose; + if (b.parent >= 0) { + override_local_pose = bonesptr[b.parent].pose_global * (b.rest * b.local_pose_override); + } else { + override_local_pose = (b.rest * b.local_pose_override); + } + b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount); + } + + if (b.global_pose_override_amount >= CMP_EPSILON) { + b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); + } + + if (b.local_pose_override_reset) { + b.local_pose_override_amount = 0.0; + } + if (b.global_pose_override_reset) { + b.global_pose_override_amount = 0.0; + } + + // Add the bone's children to the list of bones to be processed + int child_bone_size = b.child_bones.size(); + for (int i = 0; i < child_bone_size; i++) { + bones_to_process.push_back(b.child_bones[i]); + } + + emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx); + } +} + // helper functions -Transform3D Skeleton3D::bone_transform_to_world_transform(Transform3D p_bone_transform) { - return get_global_transform() * p_bone_transform; + +Transform3D Skeleton3D::global_pose_to_world_transform(Transform3D p_global_pose) { + return get_global_transform() * p_global_pose; } -Transform3D Skeleton3D::world_transform_to_bone_transform(Transform3D p_world_transform) { +Transform3D Skeleton3D::world_transform_to_global_pose(Transform3D p_world_transform) { return get_global_transform().affine_inverse() * p_world_transform; } +Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_global_pose) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); + if (bones[p_bone_idx].parent >= 0) { + int parent_bone_idx = bones[p_bone_idx].parent; + Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); + return conversion_transform.affine_inverse() * p_global_pose; + } else { + return p_global_pose; + } +} + +Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_local_pose) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); + if (bones[p_bone_idx].parent >= 0) { + int parent_bone_idx = bones[p_bone_idx].parent; + Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); + return conversion_transform * p_local_pose; + } else { + return p_local_pose; + } +} + +Basis Skeleton3D::global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Basis()); + Basis return_basis = p_basis; + + if (bones[p_bone_idx].rest_bone_forward_axis < 0) { + update_bone_rest_forward_vector(p_bone_idx, true); + } + + if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_X_FORWARD) { + return_basis.rotate_local(Vector3(0, 1, 0), (Math_PI / 2.0)); + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_X_FORWARD) { + return_basis.rotate_local(Vector3(0, 1, 0), -(Math_PI / 2.0)); + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_Y_FORWARD) { + return_basis.rotate_local(Vector3(1, 0, 0), -(Math_PI / 2.0)); + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_Y_FORWARD) { + return_basis.rotate_local(Vector3(1, 0, 0), (Math_PI / 2.0)); + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_Z_FORWARD) { + // Do nothing! + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_Z_FORWARD) { + return_basis.rotate_local(Vector3(0, 0, 1), Math_PI); + } + + return return_basis; +} + +// Modifications + +#ifndef _3D_DISABLED + +void Skeleton3D::set_modification_stack(Ref<SkeletonModificationStack3D> p_stack) { + if (modification_stack.is_valid()) { + modification_stack->is_setup = false; + modification_stack->set_skeleton(nullptr); + } + + modification_stack = p_stack; + if (modification_stack.is_valid()) { + modification_stack->set_skeleton(this); + modification_stack->setup(); + } +} +Ref<SkeletonModificationStack3D> Skeleton3D::get_modification_stack() { + return modification_stack; +} + +void Skeleton3D::execute_modifications(real_t p_delta, int p_execution_mode) { + if (!modification_stack.is_valid()) { + return; + } + + // Needed to avoid the issue where the stack looses reference to the skeleton when the scene is saved. + if (modification_stack->skeleton != this) { + modification_stack->set_skeleton(this); + } + + modification_stack->execute(p_delta, p_execution_mode); +} + +#endif // _3D_DISABLED + void Skeleton3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_bone_process_orders"), &Skeleton3D::get_bone_process_orders); ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone); ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone); ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name); @@ -866,6 +1146,13 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("unparent_bone_and_rest", "bone_idx"), &Skeleton3D::unparent_bone_and_rest); + ClassDB::bind_method(D_METHOD("get_bone_children", "bone_idx"), &Skeleton3D::get_bone_children); + ClassDB::bind_method(D_METHOD("set_bone_children", "bone_idx", "bone_children"), &Skeleton3D::set_bone_children); + ClassDB::bind_method(D_METHOD("add_bone_child", "bone_idx", "child_bone_idx"), &Skeleton3D::add_bone_child); + ClassDB::bind_method(D_METHOD("remove_bone_child", "bone_idx", "child_bone_idx"), &Skeleton3D::remove_bone_child); + + ClassDB::bind_method(D_METHOD("get_parentless_bones"), &Skeleton3D::get_parentless_bones); + ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest); @@ -883,14 +1170,26 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override); ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override); ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose); ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override); + ClassDB::bind_method(D_METHOD("clear_bones_local_pose_override"), &Skeleton3D::clear_bones_local_pose_override); + ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_local_pose_override, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton3D::get_bone_local_pose_override); + ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose); ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose); - ClassDB::bind_method(D_METHOD("bone_transform_to_world_transform", "bone_transform"), &Skeleton3D::bone_transform_to_world_transform); - ClassDB::bind_method(D_METHOD("world_transform_to_bone_transform", "world_transform"), &Skeleton3D::world_transform_to_bone_transform); + ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms); + ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms); + + // Helper functions + ClassDB::bind_method(D_METHOD("global_pose_to_world_transform", "global_pose"), &Skeleton3D::global_pose_to_world_transform); + ClassDB::bind_method(D_METHOD("world_transform_to_global_pose", "world_transform"), &Skeleton3D::world_transform_to_global_pose); + ClassDB::bind_method(D_METHOD("global_pose_to_local_pose", "bone_idx", "global_pose"), &Skeleton3D::global_pose_to_local_pose); + ClassDB::bind_method(D_METHOD("local_pose_to_global_pose", "bone_idx", "local_pose"), &Skeleton3D::local_pose_to_global_pose); + ClassDB::bind_method(D_METHOD("global_pose_z_forward_to_bone_forward", "bone_idx", "basis"), &Skeleton3D::global_pose_z_forward_to_bone_forward); ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones); ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones); @@ -900,12 +1199,21 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception); ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception); + // Modifications + ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton3D::set_modification_stack); + ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton3D::get_modification_stack); + ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications); + +#ifndef _3D_DISABLED ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones"); +#endif // _3D_DISABLED #ifdef TOOLS_ENABLED ADD_SIGNAL(MethodInfo("pose_updated")); #endif // TOOLS_ENABLED + ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx"))); + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 3fdf5321a5..c8a19db813 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -31,8 +31,8 @@ #ifndef SKELETON_3D_H #define SKELETON_3D_H -#include "core/templates/rid.h" #include "scene/3d/node_3d.h" +#include "scene/resources/skeleton_modification_3d.h" #include "scene/resources/skin.h" typedef int BoneId; @@ -62,6 +62,8 @@ public: ~SkinReference(); }; +class SkeletonModificationStack3D; + class Skeleton3D : public Node3D { GDCLASS(Skeleton3D, Node3D); @@ -71,9 +73,8 @@ private: struct Bone { String name; - bool enabled = true; - int parent = -1; - int sort_index = 0; //used for re-sorting process order + bool enabled; + int parent; bool disable_rest = false; Transform3D rest; @@ -92,7 +93,35 @@ private: PhysicalBone3D *physical_bone = nullptr; PhysicalBone3D *cache_parent_physical_bone = nullptr; - List<ObjectID> nodes_bound; + real_t local_pose_override_amount; + bool local_pose_override_reset; + Transform3D local_pose_override; + + Vector<int> child_bones; + + // The forward direction vector and rest bone forward axis are cached because they do not change + // 99% of the time, but recalculating them can be expensive on models with many bones. + Vector3 rest_bone_forward_vector; + int rest_bone_forward_axis = -1; + + Bone() { + parent = -1; + enabled = true; + disable_rest = false; + custom_pose_enable = false; + global_pose_override_amount = 0; + global_pose_override_reset = false; +#ifndef _3D_DISABLED + physical_bone = nullptr; + cache_parent_physical_bone = nullptr; +#endif // _3D_DISABLED + local_pose_override_amount = 0; + local_pose_override_reset = false; + child_bones = Vector<int>(); + + rest_bone_forward_vector = Vector3(0, 0, 0); + rest_bone_forward_axis = -1; + } }; Set<SkinReference *> skin_bindings; @@ -101,8 +130,9 @@ private: bool animate_physical_bones = true; Vector<Bone> bones; - Vector<int> process_order; - bool process_order_dirty = true; + bool process_order_dirty; + + Vector<int> parentless_bones; void _make_dirty(); bool dirty = false; @@ -118,7 +148,20 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifndef _3D_DISABLED + Ref<SkeletonModificationStack3D> modification_stack; +#endif // _3D_DISABLED + public: + enum Bone_Forward_Axis { + BONE_AXIS_X_FORWARD = 0, + BONE_AXIS_Y_FORWARD = 1, + BONE_AXIS_Z_FORWARD = 2, + BONE_AXIS_NEGATIVE_X_FORWARD = 3, + BONE_AXIS_NEGATIVE_Y_FORWARD = 4, + BONE_AXIS_NEGATIVE_Z_FORWARD = 5, + }; + enum { NOTIFICATION_UPDATE_SKELETON = 50 }; @@ -136,6 +179,12 @@ public: void unparent_bone_and_rest(int p_bone); + Vector<int> get_bone_children(int p_bone); + void set_bone_children(int p_bone, Vector<int> p_children); + void add_bone_child(int p_bone, int p_child); + void remove_bone_child(int p_bone, int p_child); + Vector<int> get_parentless_bones(); + void set_bone_disable_rest(int p_bone, bool p_disable); bool is_bone_rest_disabled(int p_bone) const; @@ -146,16 +195,8 @@ public: Transform3D get_bone_global_pose(int p_bone) const; Transform3D get_bone_global_pose_no_override(int p_bone) const; - void clear_bones_global_pose_override(); - void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); - void set_bone_enabled(int p_bone, bool p_enabled); bool is_bone_enabled(int p_bone) const; - - void bind_child_node_to_bone(int p_bone, Node *p_node); - void unbind_child_node_from_bone(int p_bone, Node *p_node); - void get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) const; - void clear_bones(); // posing api @@ -166,15 +207,40 @@ public: void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose); Transform3D get_bone_custom_pose(int p_bone) const; + void clear_bones_global_pose_override(); + Transform3D get_bone_global_pose_override(int p_bone) const; + void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); + + void clear_bones_local_pose_override(); + Transform3D get_bone_local_pose_override(int p_bone) const; + void set_bone_local_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); + void localize_rests(); // used for loaders and tools - int get_process_order(int p_idx); - Vector<int> get_bone_process_orders(); Ref<SkinReference> register_skin(const Ref<Skin> &p_skin); + void force_update_all_bone_transforms(); + void force_update_bone_children_transforms(int bone_idx); + + void update_bone_rest_forward_vector(int p_bone, bool p_force_update = false); + void update_bone_rest_forward_axis(int p_bone, bool p_force_update = false); + Vector3 get_bone_axis_forward_vector(int p_bone); + int get_bone_axis_forward_enum(int p_bone); + // Helper functions - Transform3D bone_transform_to_world_transform(Transform3D p_transform); - Transform3D world_transform_to_bone_transform(Transform3D p_transform); + Transform3D global_pose_to_world_transform(Transform3D p_global_pose); + Transform3D world_transform_to_global_pose(Transform3D p_transform); + Transform3D global_pose_to_local_pose(int p_bone_idx, Transform3D p_global_pose); + Transform3D local_pose_to_global_pose(int p_bone_idx, Transform3D p_local_pose); + + Basis global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis); + + // Modifications +#ifndef _3D_DISABLED + Ref<SkeletonModificationStack3D> get_modification_stack(); + void set_modification_stack(Ref<SkeletonModificationStack3D> p_stack); + void execute_modifications(real_t p_delta, int p_execution_mode); +#endif // _3D_DISABLED // Physical bone API diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 81dfe675c3..4cf08e7c99 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -33,11 +33,6 @@ #ifndef _3D_DISABLED -/** - * @author AndreaCatania - */ - -#include "core/math/transform_3d.h" #include "scene/3d/skeleton_3d.h" class FabrikInverseKinematic { diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 3dbeac5e97..7eb189e890 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -30,13 +30,7 @@ #include "soft_body_3d.h" -#include "core/object/class_db.h" -#include "core/os/os.h" -#include "core/templates/list.h" -#include "core/templates/rid.h" -#include "scene/3d/collision_object_3d.h" #include "scene/3d/physics_body_3d.h" -#include "scene/3d/skeleton_3d.h" SoftBodyRenderingServerHandler::SoftBodyRenderingServerHandler() {} @@ -361,6 +355,11 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody3D::set_drag_coefficient); ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody3D::get_drag_coefficient); + ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform); + + ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::pin_point, DEFVAL(NodePath())); + ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned); + ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody3D::set_ray_pickable); ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody3D::is_ray_pickable); diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp index 5e9265b4c3..4748a9d889 100644 --- a/scene/3d/spring_arm_3d.cpp +++ b/scene/3d/spring_arm_3d.cpp @@ -30,11 +30,6 @@ #include "spring_arm_3d.h" -#include "core/config/engine.h" -#include "scene/3d/collision_object_3d.h" -#include "scene/resources/sphere_shape_3d.h" -#include "servers/physics_server_3d.h" - void SpringArm3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index a28382f4cb..b9a2736918 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -1177,7 +1177,7 @@ void AnimatedSprite3D::stop() { } bool AnimatedSprite3D::is_playing() const { - return is_processing(); + return playing; } void AnimatedSprite3D::_reset_timeout() { diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index d35b9ffe1b..90c2a309e1 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -31,8 +31,8 @@ #ifndef SPRITE_3D_H #define SPRITE_3D_H -#include "scene/2d/animated_sprite_2d.h" #include "scene/3d/visual_instance_3d.h" +#include "scene/resources/sprite_frames.h" class SpriteBase3D : public GeometryInstance3D { GDCLASS(SpriteBase3D, GeometryInstance3D); diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 92c0e09947..daeea81891 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -802,24 +802,21 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { } } -void VehicleBody3D::_direct_state_changed(Object *p_state) { - RigidBody3D::_direct_state_changed(p_state); +void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { + RigidBody3D::_body_state_changed(p_state); - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); - - real_t step = state->get_step(); + real_t step = p_state->get_step(); for (int i = 0; i < wheels.size(); i++) { - _update_wheel(i, state); + _update_wheel(i, p_state); } for (int i = 0; i < wheels.size(); i++) { - _ray_cast(i, state); - wheels[i]->set_transform(state->get_transform().inverse() * wheels[i]->m_worldTransform); + _ray_cast(i, p_state); + wheels[i]->set_transform(p_state->get_transform().inverse() * wheels[i]->m_worldTransform); } - _update_suspension(state); + _update_suspension(p_state); for (int i = 0; i < wheels.size(); i++) { //apply suspension force @@ -831,20 +828,20 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { suspensionForce = wheel.m_maxSuspensionForce; } Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; - Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - state->get_transform().origin; + Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - p_state->get_transform().origin; - state->apply_impulse(impulse, relative_position); + p_state->apply_impulse(impulse, relative_position); } - _update_friction(state); + _update_friction(p_state); for (int i = 0; i < wheels.size(); i++) { VehicleWheel3D &wheel = *wheels[i]; - Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - state->get_transform().origin; - Vector3 vel = state->get_linear_velocity() + (state->get_angular_velocity()).cross(relpos); // * mPos); + Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - p_state->get_transform().origin; + Vector3 vel = p_state->get_linear_velocity() + (p_state->get_angular_velocity()).cross(relpos); // * mPos); if (wheel.m_raycastInfo.m_isInContact) { - const Transform3D &chassisWorldTransform = state->get_transform(); + const Transform3D &chassisWorldTransform = p_state->get_transform(); Vector3 fwd( chassisWorldTransform.basis[0][Vector3::AXIS_Z], @@ -864,8 +861,6 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact } - - state = nullptr; } void VehicleBody3D::set_engine_force(real_t p_engine_force) { @@ -926,7 +921,5 @@ void VehicleBody3D::_bind_methods() { VehicleBody3D::VehicleBody3D() { exclude.insert(get_rid()); - //PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &VehicleBody3D::_direct_state_changed)); - set_mass(40); } diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index 2c10205ea3..f29c3d89b7 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -192,7 +192,8 @@ class VehicleBody3D : public RigidBody3D { static void _bind_methods(); - void _direct_state_changed(Object *p_state) override; + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state) override; public: void set_engine_force(real_t p_engine_force); diff --git a/scene/3d/velocity_tracker_3d.cpp b/scene/3d/velocity_tracker_3d.cpp index 8f4fecb348..200664a41b 100644 --- a/scene/3d/velocity_tracker_3d.cpp +++ b/scene/3d/velocity_tracker_3d.cpp @@ -29,7 +29,6 @@ /*************************************************************************/ #include "velocity_tracker_3d.h" -#include "core/config/engine.h" void VelocityTracker3D::set_track_physics_step(bool p_track_physics_step) { physics_step = p_track_physics_step; diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp index 6a80aa3f45..3d0bc3df9c 100644 --- a/scene/3d/visible_on_screen_notifier_3d.cpp +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -30,10 +30,6 @@ #include "visible_on_screen_notifier_3d.h" -#include "core/config/engine.h" -#include "scene/3d/camera_3d.h" -#include "scene/3d/physics_body_3d.h" -#include "scene/animation/animation_player.h" #include "scene/scene_string_names.h" void VisibleOnScreenNotifier3D::_visibility_enter() { diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index b437379b2a..73c2887983 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -31,8 +31,6 @@ #include "visual_instance_3d.h" #include "scene/scene_string_names.h" -#include "servers/rendering_server.h" -#include "skeleton_3d.h" AABB VisualInstance3D::get_transformed_aabb() const { return get_global_transform().xform(get_aabb()); @@ -416,7 +414,7 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling"); ADD_GROUP("Global Illumination", "gi_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale"); ADD_GROUP("Visibility Range", "visibility_range_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin", "get_visibility_range_begin"); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index aa64195c2b..8d24e13d47 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -31,10 +31,7 @@ #ifndef VISUAL_INSTANCE_H #define VISUAL_INSTANCE_H -#include "core/math/face3.h" -#include "core/templates/rid.h" #include "scene/3d/node_3d.h" -#include "scene/resources/material.h" class VisualInstance3D : public Node3D { GDCLASS(VisualInstance3D, Node3D); diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 5cf7522667..d3d12d94e9 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -30,9 +30,8 @@ #include "voxel_gi.h" -#include "core/os/os.h" - #include "mesh_instance_3d.h" +#include "multimesh_instance_3d.h" #include "voxelizer.h" void VoxelGIData::_set_data(const Dictionary &p_data) { diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h index 434d209421..5d0dda1ba3 100644 --- a/scene/3d/voxel_gi.h +++ b/scene/3d/voxel_gi.h @@ -31,7 +31,6 @@ #ifndef VOXEL_GI_H #define VOXEL_GI_H -#include "multimesh_instance_3d.h" #include "scene/3d/visual_instance_3d.h" class VoxelGIData : public Resource { diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 6124326d2d..2d32379d69 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -29,11 +29,6 @@ /*************************************************************************/ #include "voxelizer.h" -#include "core/math/geometry_3d.h" -#include "core/os/os.h" -#include "core/os/threaded_array_processor.h" - -#include <stdlib.h> static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) { if (p_pos.is_equal_approx(p_vtx[0])) { diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index e500d2d4c3..09c126bc4e 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -31,8 +31,6 @@ #ifndef VOXEL_LIGHT_BAKER_H #define VOXEL_LIGHT_BAKER_H -#include "core/math/vector3i.h" -#include "scene/3d/mesh_instance_3d.h" #include "scene/resources/multimesh.h" class Voxelizer { diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index 352bef072f..26fa43b969 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -30,6 +30,7 @@ #include "world_environment.h" +#include "scene/3d/node_3d.h" #include "scene/main/window.h" void WorldEnvironment::_notification(int p_what) { diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index 9e85982381..310d1e96a5 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -31,7 +31,7 @@ #ifndef SCENARIO_FX_H #define SCENARIO_FX_H -#include "scene/3d/node_3d.h" +#include "scene/main/node.h" #include "scene/resources/camera_effects.h" #include "scene/resources/environment.h" diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index e620f33478..ebfb58e9fe 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -30,9 +30,8 @@ #include "xr_nodes.h" -#include "core/input/input.h" +#include "scene/main/viewport.h" #include "servers/xr/xr_interface.h" -#include "servers/xr_server.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 312287a93b..6e54ff83d7 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -32,8 +32,6 @@ #define XR_NODES_H #include "scene/3d/camera_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/resources/mesh.h" #include "servers/xr/xr_positional_tracker.h" /** |