summaryrefslogtreecommitdiff
path: root/scene/3d
diff options
context:
space:
mode:
Diffstat (limited to 'scene/3d')
-rw-r--r--scene/3d/SCsub5
-rw-r--r--scene/3d/area_3d.cpp70
-rw-r--r--scene/3d/area_3d.h14
-rw-r--r--scene/3d/audio_stream_player_3d.cpp670
-rw-r--r--scene/3d/audio_stream_player_3d.h42
-rw-r--r--scene/3d/bone_attachment_3d.cpp320
-rw-r--r--scene/3d/bone_attachment_3d.h39
-rw-r--r--scene/3d/camera_3d.cpp4
-rw-r--r--scene/3d/camera_3d.h3
-rw-r--r--scene/3d/collision_object_3d.cpp9
-rw-r--r--scene/3d/collision_object_3d.h6
-rw-r--r--scene/3d/collision_polygon_3d.cpp1
-rw-r--r--scene/3d/collision_shape_3d.cpp2
-rw-r--r--scene/3d/collision_shape_3d.h1
-rw-r--r--scene/3d/cpu_particles_3d.cpp262
-rw-r--r--scene/3d/cpu_particles_3d.h27
-rw-r--r--scene/3d/decal.h2
-rw-r--r--scene/3d/gpu_particles_3d.cpp6
-rw-r--r--scene/3d/gpu_particles_3d.h2
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp1
-rw-r--r--scene/3d/gpu_particles_collision_3d.h2
-rw-r--r--scene/3d/light_3d.cpp12
-rw-r--r--scene/3d/light_3d.h2
-rw-r--r--scene/3d/lightmap_gi.cpp7
-rw-r--r--scene/3d/lightmap_gi.h3
-rw-r--r--scene/3d/lightmapper.h5
-rw-r--r--scene/3d/listener_3d.cpp2
-rw-r--r--scene/3d/listener_3d.h1
-rw-r--r--scene/3d/mesh_instance_3d.cpp2
-rw-r--r--scene/3d/mesh_instance_3d.h6
-rw-r--r--scene/3d/navigation_agent_3d.cpp1
-rw-r--r--scene/3d/navigation_agent_3d.h1
-rw-r--r--scene/3d/navigation_obstacle_3d.h1
-rw-r--r--scene/3d/navigation_region_3d.cpp1
-rw-r--r--scene/3d/navigation_region_3d.h1
-rw-r--r--scene/3d/node_3d.cpp4
-rw-r--r--scene/3d/node_3d.h1
-rw-r--r--scene/3d/path_3d.cpp3
-rw-r--r--scene/3d/physics_body_3d.cpp347
-rw-r--r--scene/3d/physics_body_3d.h32
-rw-r--r--scene/3d/physics_joint_3d.cpp4
-rw-r--r--scene/3d/position_3d.cpp1
-rw-r--r--scene/3d/ray_cast_3d.cpp2
-rw-r--r--scene/3d/reflection_probe.h3
-rw-r--r--scene/3d/skeleton_3d.cpp700
-rw-r--r--scene/3d/skeleton_3d.h104
-rw-r--r--scene/3d/skeleton_ik_3d.h5
-rw-r--r--scene/3d/soft_body_3d.cpp11
-rw-r--r--scene/3d/spring_arm_3d.cpp5
-rw-r--r--scene/3d/sprite_3d.cpp2
-rw-r--r--scene/3d/sprite_3d.h2
-rw-r--r--scene/3d/vehicle_body_3d.cpp33
-rw-r--r--scene/3d/vehicle_body_3d.h3
-rw-r--r--scene/3d/velocity_tracker_3d.cpp1
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp4
-rw-r--r--scene/3d/visual_instance_3d.cpp4
-rw-r--r--scene/3d/visual_instance_3d.h3
-rw-r--r--scene/3d/voxel_gi.cpp3
-rw-r--r--scene/3d/voxel_gi.h1
-rw-r--r--scene/3d/voxelizer.cpp5
-rw-r--r--scene/3d/voxelizer.h2
-rw-r--r--scene/3d/world_environment.cpp1
-rw-r--r--scene/3d/world_environment.h2
-rw-r--r--scene/3d/xr_nodes.cpp3
-rw-r--r--scene/3d/xr_nodes.h2
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(&current.filter, false);
- current.filter_process[k * 2 + 1].set_filter(&current.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(&current.filter);
- current.filter_process[k * 2 + 1].set_filter(&current.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"
/**