diff options
Diffstat (limited to 'scene')
110 files changed, 3185 insertions, 1591 deletions
diff --git a/scene/2d/listener_2d.cpp b/scene/2d/audio_listener_2d.cpp index 444f05f2b1..f16e359a1d 100644 --- a/scene/2d/listener_2d.cpp +++ b/scene/2d/audio_listener_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* listener_2d.cpp */ +/* audio_listener_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "listener_2d.h" +#include "audio_listener_2d.h" -bool Listener2D::_set(const StringName &p_name, const Variant &p_value) { +bool AudioListener2D::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "current") { if (p_value.operator bool()) { make_current(); @@ -43,7 +43,7 @@ bool Listener2D::_set(const StringName &p_name, const Variant &p_value) { return true; } -bool Listener2D::_get(const StringName &p_name, Variant &r_ret) const { +bool AudioListener2D::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == "current") { if (is_inside_tree() && get_tree()->is_node_being_edited(this)) { r_ret = current; @@ -56,11 +56,11 @@ bool Listener2D::_get(const StringName &p_name, Variant &r_ret) const { return true; } -void Listener2D::_get_property_list(List<PropertyInfo> *p_list) const { +void AudioListener2D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::BOOL, "current")); } -void Listener2D::_notification(int p_what) { +void AudioListener2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { if (!get_tree()->is_node_being_edited(this) && current) { @@ -80,33 +80,33 @@ void Listener2D::_notification(int p_what) { } } -void Listener2D::make_current() { +void AudioListener2D::make_current() { current = true; if (!is_inside_tree()) { return; } - get_viewport()->_listener_2d_set(this); + get_viewport()->_audio_listener_2d_set(this); } -void Listener2D::clear_current() { +void AudioListener2D::clear_current() { current = false; if (!is_inside_tree()) { return; } - get_viewport()->_listener_2d_remove(this); + get_viewport()->_audio_listener_2d_remove(this); } -bool Listener2D::is_current() const { +bool AudioListener2D::is_current() const { if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { - return get_viewport()->get_listener_2d() == this; + return get_viewport()->get_audio_listener_2d() == this; } else { return current; } return false; } -void Listener2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("make_current"), &Listener2D::make_current); - ClassDB::bind_method(D_METHOD("clear_current"), &Listener2D::clear_current); - ClassDB::bind_method(D_METHOD("is_current"), &Listener2D::is_current); +void AudioListener2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("make_current"), &AudioListener2D::make_current); + ClassDB::bind_method(D_METHOD("clear_current"), &AudioListener2D::clear_current); + ClassDB::bind_method(D_METHOD("is_current"), &AudioListener2D::is_current); } diff --git a/scene/2d/listener_2d.h b/scene/2d/audio_listener_2d.h index 0289a8087d..875887acc6 100644 --- a/scene/2d/listener_2d.h +++ b/scene/2d/audio_listener_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* listener_2d.h */ +/* audio_listener_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -34,8 +34,8 @@ #include "scene/2d/node_2d.h" #include "scene/main/window.h" -class Listener2D : public Node2D { - GDCLASS(Listener2D, Node2D); +class AudioListener2D : public Node2D { + GDCLASS(AudioListener2D, Node2D); private: bool current = false; diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 8401909384..bddc342c1a 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -31,7 +31,7 @@ #include "audio_stream_player_2d.h" #include "scene/2d/area_2d.h" -#include "scene/2d/listener_2d.h" +#include "scene/2d/audio_listener_2d.h" #include "scene/main/window.h" void AudioStreamPlayer2D::_notification(int p_what) { @@ -156,7 +156,7 @@ void AudioStreamPlayer2D::_update_panning() { Vector2 relative_to_listener; //screen in global is used for attenuation - Listener2D *listener = vp->get_listener_2d(); + AudioListener2D *listener = vp->get_audio_listener_2d(); if (listener) { listener_in_global = listener->get_global_position(); relative_to_listener = global_pos - listener_in_global; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index bf91ce8e65..8195d98f55 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -198,7 +198,7 @@ Transform2D Camera2D::get_camera_transform() { screen_rect.position += offset; } - camera_screen_center = screen_rect.position + screen_rect.size * 0.5; + camera_screen_center = screen_rect.get_center(); Transform2D xform; xform.scale_basis(zoom); diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 8c8a292ad7..271a4da705 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -243,7 +243,7 @@ TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject2D>(get_parent())) { - warnings.push_back(TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape.")); + warnings.push_back(TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape.")); } int polygon_count = polygon.size(); diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index d52795f0d5..54cb851216 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -175,7 +175,7 @@ TypedArray<String> CollisionShape2D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject2D>(get_parent())) { - warnings.push_back(TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape.")); + warnings.push_back(TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape.")); } if (!shape.is_valid()) { warnings.push_back(TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!")); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index b836497627..bf26ec1f20 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -155,7 +155,7 @@ void CPUParticles2D::_update_mesh_texture() { Vector<Vector2> vertices; vertices.push_back(-tex_size * 0.5); vertices.push_back(-tex_size * 0.5 + Vector2(tex_size.x, 0)); - vertices.push_back(-tex_size * 0.5 + Vector2(tex_size.x, tex_size.y)); + vertices.push_back(-tex_size * 0.5 + tex_size); vertices.push_back(-tex_size * 0.5 + Vector2(0, tex_size.y)); Vector<Vector2> uvs; AtlasTexture *atlas_texure = Object::cast_to<AtlasTexture>(*texture); @@ -727,7 +727,7 @@ void CPUParticles2D::_particles_process(double p_delta) { p.hue_rot_rand = Math::randf(); p.anim_offset_rand = Math::randf(); - real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); + real_t angle1_rad = direction.angle() + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad)); p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], Math::randf()); diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 72ea6541e3..cbf0d50c4e 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -464,7 +464,7 @@ void NavigationRegion2D::_notification(int p_what) { draw_line(a, b, doors_color); // Draw a circle to illustrate the margins. - real_t angle = (b - a).angle(); + real_t angle = b.angle_to_point(a); draw_arc(a, radius, angle + Math_PI / 2.0, angle - Math_PI / 2.0 + Math_TAU, 10, doors_color); draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color); } diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index 1fe6a4a4b8..67e35cc7a3 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -100,6 +100,10 @@ void ParallaxLayer::_notification(int p_what) { _update_mirroring(); } break; case NOTIFICATION_EXIT_TREE: { + if (Engine::get_singleton()->is_editor_hint()) { + break; + } + set_position(orig_offset); set_scale(orig_scale); } break; diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index d547914e16..48817679bc 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -33,7 +33,7 @@ void PhysicalBone2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - // Position the RigidBody in the correct position. + // Position the RigidDynamicBody in the correct position. if (follow_bone_when_simulating) { _position_at_bone2d(); } @@ -150,27 +150,15 @@ void PhysicalBone2D::_start_physics_simulation() { return; } - // Reset to Bone2D position + // Reset to Bone2D position. _position_at_bone2d(); - // Apply the layers and masks + // Apply the layers and masks. PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - // Apply the correct mode - RigidBody2D::Mode rigid_mode = get_mode(); - if (rigid_mode == RigidBody2D::MODE_STATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); - } else if (rigid_mode == RigidBody2D::MODE_DYNAMIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } else if (rigid_mode == RigidBody2D::MODE_KINEMATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); - } else if (rigid_mode == RigidBody2D::MODE_DYNAMIC_LOCKED) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); - } else { - // Default to Dynamic. - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } + // Apply the correct mode. + _apply_body_mode(); _internal_simulate_physics = true; set_physics_process_internal(true); @@ -295,7 +283,7 @@ void PhysicalBone2D::_bind_methods() { } PhysicalBone2D::PhysicalBone2D() { - // Stop the RigidBody from executing its force integration. + // Stop the RigidDynamicBody from executing its force integration. PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h index 46a2772bad..a250d0aadd 100644 --- a/scene/2d/physical_bone_2d.h +++ b/scene/2d/physical_bone_2d.h @@ -36,8 +36,8 @@ #include "scene/2d/skeleton_2d.h" -class PhysicalBone2D : public RigidBody2D { - GDCLASS(PhysicalBone2D, RigidBody2D); +class PhysicalBone2D : public RigidDynamicBody2D { + GDCLASS(PhysicalBone2D, RigidDynamicBody2D); protected: void _notification(int p_what); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 30f012c7aa..c07a999588 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -34,8 +34,8 @@ #include "scene/scene_string_names.h" void PhysicsBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with); @@ -57,8 +57,12 @@ PhysicsBody2D::~PhysicsBody2D() { Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin) { PhysicsServer2D::MotionResult result; - if (move_and_collide(p_motion, result, p_margin, p_test_only)) { - if (motion_cache.is_null()) { + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + if (move_and_collide(p_motion * delta, result, p_margin, p_test_only)) { + // Create a new instance when the cached reference is invalid or still in use in script. + if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); motion_cache->owner = this; } @@ -132,7 +136,10 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result); } - return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r); + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r); } TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() { @@ -309,7 +316,7 @@ AnimatableBody2D::AnimatableBody2D() : _update_kinematic_motion(); } -void RigidBody2D::_body_enter_tree(ObjectID p_id) { +void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); @@ -331,7 +338,7 @@ void RigidBody2D::_body_enter_tree(ObjectID p_id) { contact_monitor->locked = false; } -void RigidBody2D::_body_exit_tree(ObjectID p_id) { +void RigidDynamicBody2D::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); @@ -352,7 +359,7 @@ void RigidBody2D::_body_exit_tree(ObjectID p_id) { contact_monitor->locked = false; } -void RigidBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { +void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { bool body_in = p_status == 1; ObjectID objid = p_instance; @@ -371,8 +378,8 @@ void RigidBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instan //E->get().rc=0; E->get().in_scene = node && node->is_inside_tree(); if (node) { - node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree), make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree), make_binds(objid)); if (E->get().in_scene) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -400,8 +407,8 @@ void RigidBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instan if (E->get().shapes.is_empty()) { if (node) { - node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree)); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree)); if (in_scene) { emit_signal(SceneStringNames::get_singleton()->body_exited, node); } @@ -415,21 +422,21 @@ void RigidBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instan } } -struct _RigidBody2DInOut { +struct _RigidDynamicBody2DInOut { RID rid; ObjectID id; int shape = 0; int local_shape = 0; }; -void RigidBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { - RigidBody2D *body = (RigidBody2D *)p_instance; +void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { + RigidDynamicBody2D *body = (RigidDynamicBody2D *)p_instance; body->_body_state_changed(p_state); } -void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { +void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { set_block_transform_notify(true); // don't want notify (would feedback loop) - if (mode != MODE_KINEMATIC) { + if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) { set_global_transform(p_state->get_transform()); } @@ -457,9 +464,9 @@ void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { } } - _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBody2DInOut)); + _RigidDynamicBody2DInOut *toadd = (_RigidDynamicBody2DInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidDynamicBody2DInOut)); int toadd_count = 0; //state->get_contact_count(); - RigidBody2D_RemoveAction *toremove = (RigidBody2D_RemoveAction *)alloca(rc * sizeof(RigidBody2D_RemoveAction)); + RigidDynamicBody2D_RemoveAction *toremove = (RigidDynamicBody2D_RemoveAction *)alloca(rc * sizeof(RigidDynamicBody2D_RemoveAction)); int toremove_count = 0; //put the ones to add @@ -523,52 +530,83 @@ void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { } } -void RigidBody2D::set_mode(Mode p_mode) { - mode = p_mode; - switch (p_mode) { - case MODE_DYNAMIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } break; - case MODE_STATIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); +void RigidDynamicBody2D::_apply_body_mode() { + if (freeze) { + switch (freeze_mode) { + case FREEZE_MODE_STATIC: { + set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); + } break; + case FREEZE_MODE_KINEMATIC: { + set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); + } break; + } + } else if (lock_rotation) { + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LINEAR); + } else { + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); + } +} - } break; - case MODE_KINEMATIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); +void RigidDynamicBody2D::set_lock_rotation_enabled(bool p_lock_rotation) { + if (p_lock_rotation == lock_rotation) { + return; + } - } break; - case MODE_DYNAMIC_LOCKED: { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); + lock_rotation = p_lock_rotation; + _apply_body_mode(); +} - } break; +bool RigidDynamicBody2D::is_lock_rotation_enabled() const { + return lock_rotation; +} + +void RigidDynamicBody2D::set_freeze_enabled(bool p_freeze) { + if (p_freeze == freeze) { + return; } + + freeze = p_freeze; + _apply_body_mode(); } -RigidBody2D::Mode RigidBody2D::get_mode() const { - return mode; +bool RigidDynamicBody2D::is_freeze_enabled() const { + return freeze; } -void RigidBody2D::set_mass(real_t p_mass) { +void RigidDynamicBody2D::set_freeze_mode(FreezeMode p_freeze_mode) { + if (p_freeze_mode == freeze_mode) { + return; + } + + freeze_mode = p_freeze_mode; + _apply_body_mode(); +} + +RigidDynamicBody2D::FreezeMode RigidDynamicBody2D::get_freeze_mode() const { + return freeze_mode; +} + +void RigidDynamicBody2D::set_mass(real_t p_mass) { ERR_FAIL_COND(p_mass <= 0); mass = p_mass; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_MASS, mass); } -real_t RigidBody2D::get_mass() const { +real_t RigidDynamicBody2D::get_mass() const { return mass; } -void RigidBody2D::set_inertia(real_t p_inertia) { +void RigidDynamicBody2D::set_inertia(real_t p_inertia) { ERR_FAIL_COND(p_inertia < 0); inertia = p_inertia; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia); } -real_t RigidBody2D::get_inertia() const { +real_t RigidDynamicBody2D::get_inertia() const { return inertia; } -void RigidBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) { +void RigidDynamicBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) { if (center_of_mass_mode == p_mode) { return; } @@ -590,11 +628,11 @@ void RigidBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) { } } -RigidBody2D::CenterOfMassMode RigidBody2D::get_center_of_mass_mode() const { +RigidDynamicBody2D::CenterOfMassMode RigidDynamicBody2D::get_center_of_mass_mode() const { return center_of_mass_mode; } -void RigidBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) { +void RigidDynamicBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) { if (center_of_mass == p_center_of_mass) { return; } @@ -605,84 +643,84 @@ void RigidBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) { PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass); } -const Vector2 &RigidBody2D::get_center_of_mass() const { +const Vector2 &RigidDynamicBody2D::get_center_of_mass() const { return center_of_mass; } -void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { +void RigidDynamicBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { - if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics))) { - physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody2D::_reload_physics_characteristics))) { + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody2D::_reload_physics_characteristics)); } } physics_material_override = p_physics_material_override; if (physics_material_override.is_valid()) { - physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics)); + physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody2D::_reload_physics_characteristics)); } _reload_physics_characteristics(); } -Ref<PhysicsMaterial> RigidBody2D::get_physics_material_override() const { +Ref<PhysicsMaterial> RigidDynamicBody2D::get_physics_material_override() const { return physics_material_override; } -void RigidBody2D::set_gravity_scale(real_t p_gravity_scale) { +void RigidDynamicBody2D::set_gravity_scale(real_t p_gravity_scale) { gravity_scale = p_gravity_scale; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE, gravity_scale); } -real_t RigidBody2D::get_gravity_scale() const { +real_t RigidDynamicBody2D::get_gravity_scale() const { return gravity_scale; } -void RigidBody2D::set_linear_damp(real_t p_linear_damp) { +void RigidDynamicBody2D::set_linear_damp(real_t p_linear_damp) { ERR_FAIL_COND(p_linear_damp < -1); linear_damp = p_linear_damp; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_LINEAR_DAMP, linear_damp); } -real_t RigidBody2D::get_linear_damp() const { +real_t RigidDynamicBody2D::get_linear_damp() const { return linear_damp; } -void RigidBody2D::set_angular_damp(real_t p_angular_damp) { +void RigidDynamicBody2D::set_angular_damp(real_t p_angular_damp) { ERR_FAIL_COND(p_angular_damp < -1); angular_damp = p_angular_damp; PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP, angular_damp); } -real_t RigidBody2D::get_angular_damp() const { +real_t RigidDynamicBody2D::get_angular_damp() const { return angular_damp; } -void RigidBody2D::set_axis_velocity(const Vector2 &p_axis) { +void RigidDynamicBody2D::set_axis_velocity(const Vector2 &p_axis) { Vector2 axis = p_axis.normalized(); linear_velocity -= axis * axis.dot(linear_velocity); linear_velocity += p_axis; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } -void RigidBody2D::set_linear_velocity(const Vector2 &p_velocity) { +void RigidDynamicBody2D::set_linear_velocity(const Vector2 &p_velocity) { linear_velocity = p_velocity; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } -Vector2 RigidBody2D::get_linear_velocity() const { +Vector2 RigidDynamicBody2D::get_linear_velocity() const { return linear_velocity; } -void RigidBody2D::set_angular_velocity(real_t p_velocity) { +void RigidDynamicBody2D::set_angular_velocity(real_t p_velocity) { angular_velocity = p_velocity; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } -real_t RigidBody2D::get_angular_velocity() const { +real_t RigidDynamicBody2D::get_angular_velocity() const { return angular_velocity; } -void RigidBody2D::set_use_custom_integrator(bool p_enable) { +void RigidDynamicBody2D::set_use_custom_integrator(bool p_enable) { if (custom_integrator == p_enable) { return; } @@ -691,87 +729,87 @@ void RigidBody2D::set_use_custom_integrator(bool p_enable) { PhysicsServer2D::get_singleton()->body_set_omit_force_integration(get_rid(), p_enable); } -bool RigidBody2D::is_using_custom_integrator() { +bool RigidDynamicBody2D::is_using_custom_integrator() { return custom_integrator; } -void RigidBody2D::set_sleeping(bool p_sleeping) { +void RigidDynamicBody2D::set_sleeping(bool p_sleeping) { sleeping = p_sleeping; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_SLEEPING, sleeping); } -void RigidBody2D::set_can_sleep(bool p_active) { +void RigidDynamicBody2D::set_can_sleep(bool p_active) { can_sleep = p_active; PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_CAN_SLEEP, p_active); } -bool RigidBody2D::is_able_to_sleep() const { +bool RigidDynamicBody2D::is_able_to_sleep() const { return can_sleep; } -bool RigidBody2D::is_sleeping() const { +bool RigidDynamicBody2D::is_sleeping() const { return sleeping; } -void RigidBody2D::set_max_contacts_reported(int p_amount) { +void RigidDynamicBody2D::set_max_contacts_reported(int p_amount) { max_contacts_reported = p_amount; PhysicsServer2D::get_singleton()->body_set_max_contacts_reported(get_rid(), p_amount); } -int RigidBody2D::get_max_contacts_reported() const { +int RigidDynamicBody2D::get_max_contacts_reported() const { return max_contacts_reported; } -void RigidBody2D::apply_central_impulse(const Vector2 &p_impulse) { +void RigidDynamicBody2D::apply_central_impulse(const Vector2 &p_impulse) { PhysicsServer2D::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); } -void RigidBody2D::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) { +void RigidDynamicBody2D::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) { PhysicsServer2D::get_singleton()->body_apply_impulse(get_rid(), p_impulse, p_position); } -void RigidBody2D::apply_torque_impulse(real_t p_torque) { +void RigidDynamicBody2D::apply_torque_impulse(real_t p_torque) { PhysicsServer2D::get_singleton()->body_apply_torque_impulse(get_rid(), p_torque); } -void RigidBody2D::set_applied_force(const Vector2 &p_force) { +void RigidDynamicBody2D::set_applied_force(const Vector2 &p_force) { PhysicsServer2D::get_singleton()->body_set_applied_force(get_rid(), p_force); }; -Vector2 RigidBody2D::get_applied_force() const { +Vector2 RigidDynamicBody2D::get_applied_force() const { return PhysicsServer2D::get_singleton()->body_get_applied_force(get_rid()); }; -void RigidBody2D::set_applied_torque(const real_t p_torque) { +void RigidDynamicBody2D::set_applied_torque(const real_t p_torque) { PhysicsServer2D::get_singleton()->body_set_applied_torque(get_rid(), p_torque); }; -real_t RigidBody2D::get_applied_torque() const { +real_t RigidDynamicBody2D::get_applied_torque() const { return PhysicsServer2D::get_singleton()->body_get_applied_torque(get_rid()); }; -void RigidBody2D::add_central_force(const Vector2 &p_force) { +void RigidDynamicBody2D::add_central_force(const Vector2 &p_force) { PhysicsServer2D::get_singleton()->body_add_central_force(get_rid(), p_force); } -void RigidBody2D::add_force(const Vector2 &p_force, const Vector2 &p_position) { +void RigidDynamicBody2D::add_force(const Vector2 &p_force, const Vector2 &p_position) { PhysicsServer2D::get_singleton()->body_add_force(get_rid(), p_force, p_position); } -void RigidBody2D::add_torque(const real_t p_torque) { +void RigidDynamicBody2D::add_torque(const real_t p_torque) { PhysicsServer2D::get_singleton()->body_add_torque(get_rid(), p_torque); } -void RigidBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) { +void RigidDynamicBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) { ccd_mode = p_mode; PhysicsServer2D::get_singleton()->body_set_continuous_collision_detection_mode(get_rid(), PhysicsServer2D::CCDMode(p_mode)); } -RigidBody2D::CCDMode RigidBody2D::get_continuous_collision_detection_mode() const { +RigidDynamicBody2D::CCDMode RigidDynamicBody2D::get_continuous_collision_detection_mode() const { return ccd_mode; } -TypedArray<Node2D> RigidBody2D::get_colliding_bodies() const { +TypedArray<Node2D> RigidDynamicBody2D::get_colliding_bodies() const { ERR_FAIL_COND_V(!contact_monitor, Array()); TypedArray<Node2D> ret; @@ -789,7 +827,7 @@ TypedArray<Node2D> RigidBody2D::get_colliding_bodies() const { return ret; } -void RigidBody2D::set_contact_monitor(bool p_enabled) { +void RigidDynamicBody2D::set_contact_monitor(bool p_enabled) { if (p_enabled == is_contact_monitor_enabled()) { return; } @@ -803,8 +841,8 @@ void RigidBody2D::set_contact_monitor(bool p_enabled) { Node *node = Object::cast_to<Node>(obj); if (node) { - node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree)); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree)); } } @@ -816,11 +854,11 @@ void RigidBody2D::set_contact_monitor(bool p_enabled) { } } -bool RigidBody2D::is_contact_monitor_enabled() const { +bool RigidDynamicBody2D::is_contact_monitor_enabled() const { return contact_monitor != nullptr; } -void RigidBody2D::_notification(int p_what) { +void RigidDynamicBody2D::_notification(int p_what) { #ifdef TOOLS_ENABLED switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -838,90 +876,95 @@ void RigidBody2D::_notification(int p_what) { #endif } -TypedArray<String> RigidBody2D::get_configuration_warnings() const { +TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const { Transform2D t = get_transform(); TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings(); - if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to RigidBody2D (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05) { + warnings.push_back(TTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } -void RigidBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidBody2D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidBody2D::get_mode); +void RigidDynamicBody2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody2D::set_mass); + ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody2D::get_mass); + + ClassDB::bind_method(D_METHOD("get_inertia"), &RigidDynamicBody2D::get_inertia); + ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidDynamicBody2D::set_inertia); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidBody2D::set_mass); - ClassDB::bind_method(D_METHOD("get_mass"), &RigidBody2D::get_mass); + ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidDynamicBody2D::set_center_of_mass_mode); + ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidDynamicBody2D::get_center_of_mass_mode); - ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody2D::get_inertia); - ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidBody2D::set_inertia); + ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidDynamicBody2D::set_center_of_mass); + ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidDynamicBody2D::get_center_of_mass); - ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidBody2D::set_center_of_mass_mode); - ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidBody2D::get_center_of_mass_mode); + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidDynamicBody2D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidDynamicBody2D::get_physics_material_override); - ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidBody2D::set_center_of_mass); - ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidBody2D::get_center_of_mass); + ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidDynamicBody2D::set_gravity_scale); + ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidDynamicBody2D::get_gravity_scale); - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override); - ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override); + ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidDynamicBody2D::set_linear_damp); + ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidDynamicBody2D::get_linear_damp); - ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody2D::set_gravity_scale); - ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody2D::get_gravity_scale); + ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &RigidDynamicBody2D::set_angular_damp); + ClassDB::bind_method(D_METHOD("get_angular_damp"), &RigidDynamicBody2D::get_angular_damp); - ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidBody2D::set_linear_damp); - ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidBody2D::get_linear_damp); + ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidDynamicBody2D::set_linear_velocity); + ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidDynamicBody2D::get_linear_velocity); - ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &RigidBody2D::set_angular_damp); - ClassDB::bind_method(D_METHOD("get_angular_damp"), &RigidBody2D::get_angular_damp); + ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &RigidDynamicBody2D::set_angular_velocity); + ClassDB::bind_method(D_METHOD("get_angular_velocity"), &RigidDynamicBody2D::get_angular_velocity); - ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody2D::set_linear_velocity); - ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody2D::get_linear_velocity); + ClassDB::bind_method(D_METHOD("set_max_contacts_reported", "amount"), &RigidDynamicBody2D::set_max_contacts_reported); + ClassDB::bind_method(D_METHOD("get_max_contacts_reported"), &RigidDynamicBody2D::get_max_contacts_reported); - ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &RigidBody2D::set_angular_velocity); - ClassDB::bind_method(D_METHOD("get_angular_velocity"), &RigidBody2D::get_angular_velocity); + ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &RigidDynamicBody2D::set_use_custom_integrator); + ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &RigidDynamicBody2D::is_using_custom_integrator); - ClassDB::bind_method(D_METHOD("set_max_contacts_reported", "amount"), &RigidBody2D::set_max_contacts_reported); - ClassDB::bind_method(D_METHOD("get_max_contacts_reported"), &RigidBody2D::get_max_contacts_reported); + ClassDB::bind_method(D_METHOD("set_contact_monitor", "enabled"), &RigidDynamicBody2D::set_contact_monitor); + ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled"), &RigidDynamicBody2D::is_contact_monitor_enabled); - ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &RigidBody2D::set_use_custom_integrator); - ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &RigidBody2D::is_using_custom_integrator); + ClassDB::bind_method(D_METHOD("set_continuous_collision_detection_mode", "mode"), &RigidDynamicBody2D::set_continuous_collision_detection_mode); + ClassDB::bind_method(D_METHOD("get_continuous_collision_detection_mode"), &RigidDynamicBody2D::get_continuous_collision_detection_mode); - ClassDB::bind_method(D_METHOD("set_contact_monitor", "enabled"), &RigidBody2D::set_contact_monitor); - ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled"), &RigidBody2D::is_contact_monitor_enabled); + ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidDynamicBody2D::set_axis_velocity); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidDynamicBody2D::apply_central_impulse, Vector2()); + ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidDynamicBody2D::apply_impulse, Vector2()); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "torque"), &RigidDynamicBody2D::apply_torque_impulse); - ClassDB::bind_method(D_METHOD("set_continuous_collision_detection_mode", "mode"), &RigidBody2D::set_continuous_collision_detection_mode); - ClassDB::bind_method(D_METHOD("get_continuous_collision_detection_mode"), &RigidBody2D::get_continuous_collision_detection_mode); + ClassDB::bind_method(D_METHOD("set_applied_force", "force"), &RigidDynamicBody2D::set_applied_force); + ClassDB::bind_method(D_METHOD("get_applied_force"), &RigidDynamicBody2D::get_applied_force); - ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody2D::set_axis_velocity); - ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody2D::apply_central_impulse, Vector2()); - ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidBody2D::apply_impulse, Vector2()); - ClassDB::bind_method(D_METHOD("apply_torque_impulse", "torque"), &RigidBody2D::apply_torque_impulse); + ClassDB::bind_method(D_METHOD("set_applied_torque", "torque"), &RigidDynamicBody2D::set_applied_torque); + ClassDB::bind_method(D_METHOD("get_applied_torque"), &RigidDynamicBody2D::get_applied_torque); - ClassDB::bind_method(D_METHOD("set_applied_force", "force"), &RigidBody2D::set_applied_force); - ClassDB::bind_method(D_METHOD("get_applied_force"), &RigidBody2D::get_applied_force); + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidDynamicBody2D::add_central_force); + ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &RigidDynamicBody2D::add_force, Vector2()); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidDynamicBody2D::add_torque); - ClassDB::bind_method(D_METHOD("set_applied_torque", "torque"), &RigidBody2D::set_applied_torque); - ClassDB::bind_method(D_METHOD("get_applied_torque"), &RigidBody2D::get_applied_torque); + ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidDynamicBody2D::set_sleeping); + ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidDynamicBody2D::is_sleeping); - ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidBody2D::add_central_force); - ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &RigidBody2D::add_force, Vector2()); - ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidBody2D::add_torque); + ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody2D::set_can_sleep); + ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody2D::is_able_to_sleep); - ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody2D::set_sleeping); - ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody2D::is_sleeping); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody2D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody2D::is_lock_rotation_enabled); - ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidBody2D::set_can_sleep); - ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidBody2D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody2D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody2D::is_freeze_enabled); - ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody2D::get_colliding_bodies); + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody2D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody2D::get_freeze_mode); + + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody2D::get_colliding_bodies); 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,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); @@ -935,6 +978,9 @@ void RigidBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); @@ -951,10 +997,8 @@ void RigidBody2D::_bind_methods() { ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); - BIND_ENUM_CONSTANT(MODE_DYNAMIC); - BIND_ENUM_CONSTANT(MODE_STATIC); - BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); - BIND_ENUM_CONSTANT(MODE_KINEMATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); @@ -964,7 +1008,7 @@ void RigidBody2D::_bind_methods() { BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE); } -void RigidBody2D::_validate_property(PropertyInfo &property) const { +void RigidDynamicBody2D::_validate_property(PropertyInfo &property) const { if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) { if (property.name == "center_of_mass") { property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; @@ -972,18 +1016,18 @@ void RigidBody2D::_validate_property(PropertyInfo &property) const { } } -RigidBody2D::RigidBody2D() : +RigidDynamicBody2D::RigidDynamicBody2D() : PhysicsBody2D(PhysicsServer2D::BODY_MODE_DYNAMIC) { PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); } -RigidBody2D::~RigidBody2D() { +RigidDynamicBody2D::~RigidDynamicBody2D() { if (contact_monitor) { memdelete(contact_monitor); } } -void RigidBody2D::_reload_physics_characteristics() { +void RigidDynamicBody2D::_reload_physics_characteristics() { if (physics_material_override.is_null()) { PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0); PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, 1); @@ -1089,9 +1133,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { Transform2D gt = get_global_transform(); - if (result.travel.length() > margin) { - gt.elements[2] -= result.travel.slide(up_direction); - } else { + if (result.travel.length() <= margin + CMP_EPSILON) { gt.elements[2] -= result.travel; } set_global_transform(gt); @@ -1110,7 +1152,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Avoid to move forward on a wall if floor_block_on_wall is true. if (p_was_on_floor && !on_floor && !vel_dir_facing_up) { // If the movement is large the body can be prevented from reaching the walls. - if (result.travel.length() <= margin) { + if (result.travel.length() <= margin + CMP_EPSILON) { // Cancels the motion. Transform2D gt = get_global_transform(); gt.elements[2] -= result.travel; @@ -1239,13 +1281,16 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) { } void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_equal_approx(floor_snap_length, 0) || on_floor || !was_on_floor || vel_dir_facing_up) { + if (on_floor || !was_on_floor || vel_dir_facing_up) { return; } + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + Transform2D gt = get_global_transform(); PhysicsServer2D::MotionResult result; - if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) { + if (move_and_collide(-up_direction * length, result, margin, true, false, true)) { bool apply = true; if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { on_floor = true; @@ -1273,12 +1318,15 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) } bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_equal_approx(floor_snap_length, 0) || up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { + if (up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { return false; } + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + PhysicsServer2D::MotionResult result; - if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) { + if (move_and_collide(-up_direction * length, result, margin, true, false, true)) { if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { return true; } @@ -1306,11 +1354,7 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) { platform_rid = p_result.collider; platform_velocity = p_result.collider_velocity; - platform_layer = 0; - CollisionObject2D *collision_object = Object::cast_to<CollisionObject2D>(ObjectDB::get_instance(p_result.collider_id)); - if (collision_object) { - platform_layer = collision_object->get_collision_layer(); - } + platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid); } const Vector2 &CharacterBody2D::get_linear_velocity() const { @@ -1373,7 +1417,8 @@ Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) { slide_colliders.resize(p_bounce + 1); } - if (slide_colliders[p_bounce].is_null()) { + // Create a new instance when the cached reference is invalid or still in use in script. + if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->reference_get_count() > 1) { slide_colliders.write[p_bounce].instantiate(); slide_colliders.write[p_bounce]->owner = this; } @@ -1570,7 +1615,7 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled"); 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::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length"); ADD_GROUP("Moving platform", "moving_platform"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 1d6437a3ad..e789ac4cb7 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -92,7 +92,7 @@ class AnimatableBody2D : public StaticBody2D { GDCLASS(AnimatableBody2D, StaticBody2D); private: - bool sync_to_physics = false; + bool sync_to_physics = true; Transform2D last_valid_transform; @@ -113,15 +113,13 @@ private: bool is_sync_to_physics_enabled() const; }; -class RigidBody2D : public PhysicsBody2D { - GDCLASS(RigidBody2D, PhysicsBody2D); +class RigidDynamicBody2D : public PhysicsBody2D { + GDCLASS(RigidDynamicBody2D, PhysicsBody2D); public: - enum Mode { - MODE_DYNAMIC, - MODE_STATIC, - MODE_DYNAMIC_LOCKED, - MODE_KINEMATIC, + enum FreezeMode { + FREEZE_MODE_STATIC, + FREEZE_MODE_KINEMATIC, }; enum CenterOfMassMode { @@ -137,7 +135,9 @@ public: private: bool can_sleep = true; - Mode mode = MODE_DYNAMIC; + bool lock_rotation = false; + bool freeze = false; + FreezeMode freeze_mode = FREEZE_MODE_STATIC; real_t mass = 1.0; real_t inertia = 0.0; @@ -177,7 +177,7 @@ private: local_shape = p_ls; } }; - struct RigidBody2D_RemoveAction { + struct RigidDynamicBody2D_RemoveAction { RID rid; ObjectID body_id; ShapePair pair; @@ -211,9 +211,17 @@ protected: GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *) + void _apply_body_mode(); + public: - void set_mode(Mode p_mode); - Mode get_mode() const; + void set_lock_rotation_enabled(bool p_lock_rotation); + bool is_lock_rotation_enabled() const; + + void set_freeze_enabled(bool p_freeze); + bool is_freeze_enabled() const; + + void set_freeze_mode(FreezeMode p_freeze_mode); + FreezeMode get_freeze_mode() const; void set_mass(real_t p_mass); real_t get_mass() const; @@ -283,16 +291,16 @@ public: virtual TypedArray<String> get_configuration_warnings() const override; - RigidBody2D(); - ~RigidBody2D(); + RigidDynamicBody2D(); + ~RigidDynamicBody2D(); private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidBody2D::Mode); -VARIANT_ENUM_CAST(RigidBody2D::CenterOfMassMode); -VARIANT_ENUM_CAST(RigidBody2D::CCDMode); +VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode); +VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode); +VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode); class CharacterBody2D : public PhysicsBody2D { GDCLASS(CharacterBody2D, PhysicsBody2D); @@ -327,14 +335,14 @@ private: real_t margin = 0.08; MotionMode motion_mode = MOTION_MODE_GROUNDED; - bool floor_stop_on_slope = false; bool floor_constant_speed = false; + bool floor_stop_on_slope = true; bool floor_block_on_wall = true; bool slide_on_ceiling = true; int max_slides = 4; int platform_layer; real_t floor_max_angle = Math::deg2rad((real_t)45.0); - float floor_snap_length = 0; + real_t floor_snap_length = 1; real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0); Vector2 up_direction = Vector2(0.0, -1.0); uint32_t moving_platform_floor_layers = UINT32_MAX; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 4bbbc3575d..63a0fb9b89 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -456,7 +456,7 @@ void Bone2D::calculate_length_and_rotation() { if (child) { Vector2 child_local_pos = to_local(child->get_global_position()); length = child_local_pos.length(); - bone_angle = Math::atan2(child_local_pos.normalized().y, child_local_pos.normalized().x); + bone_angle = child_local_pos.normalized().angle(); calculated = true; break; } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index e899b9a8ef..929233e4e0 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -261,6 +261,7 @@ void TileMap::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { pending_update = true; + _clear_internals(); _recreate_internals(); } break; case NOTIFICATION_EXIT_TREE: { @@ -298,6 +299,7 @@ void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { if (tile_set.is_valid()) { tile_set->connect("changed", callable_mp(this, &TileMap::_tile_set_changed)); + _clear_internals(); _recreate_internals(); } @@ -308,6 +310,7 @@ void TileMap::set_quadrant_size(int p_size) { ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); quadrant_size = p_size; + _clear_internals(); _recreate_internals(); emit_signal(SNAME("changed")); } @@ -327,6 +330,9 @@ void TileMap::add_layer(int p_to_pos) { ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1); + // Must clear before adding the layer. + _clear_internals(); + layers.insert(p_to_pos, TileMapLayer()); _recreate_internals(); notify_property_list_changed(); @@ -340,6 +346,9 @@ void TileMap::move_layer(int p_layer, int p_to_pos) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1); + // Clear before shuffling layers. + _clear_internals(); + TileMapLayer tl = layers[p_layer]; layers.insert(p_to_pos, tl); layers.remove(p_to_pos < p_layer ? p_layer + 1 : p_layer); @@ -358,6 +367,9 @@ void TileMap::move_layer(int p_layer, int p_to_pos) { void TileMap::remove_layer(int p_layer) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); + // Clear before removing the layer. + _clear_internals(); + layers.remove(p_layer); _recreate_internals(); notify_property_list_changed(); @@ -385,6 +397,7 @@ String TileMap::get_layer_name(int p_layer) const { void TileMap::set_layer_enabled(int p_layer, bool p_enabled) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].enabled = p_enabled; + _clear_internals(); _recreate_internals(); emit_signal(SNAME("changed")); @@ -399,6 +412,7 @@ bool TileMap::is_layer_enabled(int p_layer) const { void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].y_sort_enabled = p_y_sort_enabled; + _clear_internals(); _recreate_internals(); emit_signal(SNAME("changed")); @@ -413,6 +427,7 @@ bool TileMap::is_layer_y_sort_enabled(int p_layer) const { void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].y_sort_origin = p_y_sort_origin; + _clear_internals(); _recreate_internals(); emit_signal(SNAME("changed")); } @@ -425,6 +440,7 @@ int TileMap::get_layer_y_sort_origin(int p_layer) const { void TileMap::set_layer_z_index(int p_layer, int p_z_index) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].z_index = p_z_index; + _clear_internals(); _recreate_internals(); emit_signal(SNAME("changed")); @@ -438,6 +454,7 @@ int TileMap::get_layer_z_index(int p_layer) const { void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) { collision_visibility_mode = p_show_collision; + _clear_internals(); _recreate_internals(); emit_signal(SNAME("changed")); } @@ -448,6 +465,7 @@ TileMap::VisibilityMode TileMap::get_collision_visibility_mode() { void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) { navigation_visibility_mode = p_show_navigation; + _clear_internals(); _recreate_internals(); emit_signal(SNAME("changed")); } @@ -458,6 +476,7 @@ TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() { void TileMap::set_y_sort_enabled(bool p_enable) { Node2D::set_y_sort_enabled(p_enable); + _clear_internals(); _recreate_internals(); emit_signal(SNAME("changed")); } @@ -578,10 +597,10 @@ void TileMap::_update_dirty_quadrants() { } void TileMap::_recreate_internals() { - // Clear all internals. - _clear_internals(); - for (unsigned int layer = 0; layer < layers.size(); layer++) { + // Make sure that _clear_internals() was called prior. + ERR_FAIL_COND_MSG(layers[layer].quadrant_map.size() > 0, "TileMap layer " + itos(layer) + " had a non-empty quadrant map."); + if (!layers[layer].enabled) { continue; } @@ -872,7 +891,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List modulate.a *= 0.3; } } - draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, modulate); + draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate); // --- Occluders --- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { @@ -989,15 +1008,19 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } } -void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) { +void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation) { ERR_FAIL_COND(!p_tile_set.is_valid()); ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { + // Check for the frame. + if (p_frame >= 0) { + ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); + } + // Get the texture. Ref<Texture2D> tex = atlas_source->get_texture(); if (!tex.is_valid()) { @@ -1013,13 +1036,15 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe // Get tile data. TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); - // Compute the offset - Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords); + // Get the tile modulation. + Color modulate = tile_data->get_modulate() * p_modulation; + + // Compute the offset. Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile); - // Compute the destination rectangle in the CanvasItem. + // Get destination rect. Rect2 dest_rect; - dest_rect.size = source_rect.size; + dest_rect.size = atlas_source->get_tile_texture_region(p_atlas_coords).size; dest_rect.size.x += FP_ADJUST; dest_rect.size.y += FP_ADJUST; @@ -1038,12 +1063,28 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe dest_rect.size.y = -dest_rect.size.y; } - // Get the tile modulation. - Color modulate = tile_data->get_modulate(); - modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a); - // Draw the tile. - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + if (p_frame >= 0) { + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, p_frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, 0); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else { + real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); + real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; + real_t time = 0.0; + for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { + real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed; + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, 0.0); + + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + + time += frame_duration; + } + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); + } } } @@ -1993,10 +2034,22 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { return false; } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { int index = components[0].trim_prefix("layer_").to_int(); - if (index < 0 || index >= (int)layers.size()) { + if (index < 0) { return false; } + if (index >= (int)layers.size()) { + _clear_internals(); + while (index >= (int)layers.size()) { + layers.push_back(TileMapLayer()); + } + _recreate_internals(); + + notify_property_list_changed(); + emit_signal(SNAME("changed")); + update_configuration_warnings(); + } + if (components[1] == "name") { set_layer_name(index, p_value); return true; @@ -3000,6 +3053,7 @@ void TileMap::_bind_methods() { void TileMap::_tile_set_changed() { emit_signal(SNAME("changed")); + _clear_internals(); _recreate_internals(); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 3ac50fc7cc..ca38baa550 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -305,7 +305,7 @@ public: void set_quadrant_size(int p_size); int get_quadrant_size() const; - static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); + static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); // Layers management. int get_layers_count() const; diff --git a/scene/3d/listener_3d.cpp b/scene/3d/audio_listener_3d.cpp index 1c52933ee5..b2319e40d7 100644 --- a/scene/3d/listener_3d.cpp +++ b/scene/3d/audio_listener_3d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* listener_3d.cpp */ +/* audio_listener_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,18 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "listener_3d.h" +#include "audio_listener_3d.h" #include "scene/main/viewport.h" -void Listener3D::_update_audio_listener_state() { +void AudioListener3D::_update_audio_listener_state() { } -void Listener3D::_request_listener_update() { +void AudioListener3D::_request_listener_update() { _update_listener(); } -bool Listener3D::_set(const StringName &p_name, const Variant &p_value) { +bool AudioListener3D::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "current") { if (p_value.operator bool()) { make_current(); @@ -53,7 +53,7 @@ bool Listener3D::_set(const StringName &p_name, const Variant &p_value) { return true; } -bool Listener3D::_get(const StringName &p_name, Variant &r_ret) const { +bool AudioListener3D::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == "current") { if (is_inside_tree() && get_tree()->is_node_being_edited(this)) { r_ret = current; @@ -67,20 +67,20 @@ bool Listener3D::_get(const StringName &p_name, Variant &r_ret) const { return true; } -void Listener3D::_get_property_list(List<PropertyInfo> *p_list) const { +void AudioListener3D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::BOOL, "current")); } -void Listener3D::_update_listener() { +void AudioListener3D::_update_listener() { if (is_inside_tree() && is_current()) { get_viewport()->_listener_transform_3d_changed_notify(); } } -void Listener3D::_notification(int p_what) { +void AudioListener3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { - bool first_listener = get_viewport()->_listener_3d_add(this); + bool first_listener = get_viewport()->_audio_listener_3d_add(this); if (!get_tree()->is_node_being_edited(this) && (current || first_listener)) { make_current(); } @@ -99,41 +99,41 @@ void Listener3D::_notification(int p_what) { } } - get_viewport()->_listener_3d_remove(this); + get_viewport()->_audio_listener_3d_remove(this); } break; } } -Transform3D Listener3D::get_listener_transform() const { +Transform3D AudioListener3D::get_listener_transform() const { return get_global_transform().orthonormalized(); } -void Listener3D::make_current() { +void AudioListener3D::make_current() { current = true; if (!is_inside_tree()) { return; } - get_viewport()->_listener_3d_set(this); + get_viewport()->_audio_listener_3d_set(this); } -void Listener3D::clear_current() { +void AudioListener3D::clear_current() { current = false; if (!is_inside_tree()) { return; } - if (get_viewport()->get_listener_3d() == this) { - get_viewport()->_listener_3d_set(nullptr); - get_viewport()->_listener_3d_make_next_current(this); + if (get_viewport()->get_audio_listener_3d() == this) { + get_viewport()->_audio_listener_3d_set(nullptr); + get_viewport()->_audio_listener_3d_make_next_current(this); } } -bool Listener3D::is_current() const { +bool AudioListener3D::is_current() const { if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { - return get_viewport()->get_listener_3d() == this; + return get_viewport()->get_audio_listener_3d() == this; } else { return current; } @@ -141,27 +141,16 @@ bool Listener3D::is_current() const { return false; } -bool Listener3D::_can_gizmo_scale() const { - return false; -} - -RES Listener3D::_get_gizmo_geometry() const { - Ref<ArrayMesh> mesh = memnew(ArrayMesh); - - return mesh; -} - -void Listener3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("make_current"), &Listener3D::make_current); - ClassDB::bind_method(D_METHOD("clear_current"), &Listener3D::clear_current); - ClassDB::bind_method(D_METHOD("is_current"), &Listener3D::is_current); - ClassDB::bind_method(D_METHOD("get_listener_transform"), &Listener3D::get_listener_transform); +void AudioListener3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("make_current"), &AudioListener3D::make_current); + ClassDB::bind_method(D_METHOD("clear_current"), &AudioListener3D::clear_current); + ClassDB::bind_method(D_METHOD("is_current"), &AudioListener3D::is_current); + ClassDB::bind_method(D_METHOD("get_listener_transform"), &AudioListener3D::get_listener_transform); } -Listener3D::Listener3D() { +AudioListener3D::AudioListener3D() { set_notify_transform(true); - //active=false; } -Listener3D::~Listener3D() { +AudioListener3D::~AudioListener3D() { } diff --git a/scene/3d/listener_3d.h b/scene/3d/audio_listener_3d.h index 25eacf5135..492cacb0e9 100644 --- a/scene/3d/listener_3d.h +++ b/scene/3d/audio_listener_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* listener_3d.h */ +/* audio_listener_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -33,8 +33,8 @@ #include "scene/3d/node_3d.h" -class Listener3D : public Node3D { - GDCLASS(Listener3D, Node3D); +class AudioListener3D : public Node3D { + GDCLASS(AudioListener3D, Node3D); private: bool force_change = false; @@ -42,9 +42,6 @@ private: RID scenario_id; - virtual bool _can_gizmo_scale() const; - virtual RES _get_gizmo_geometry() const; - friend class Viewport; void _update_audio_listener_state(); @@ -69,8 +66,8 @@ public: void set_visible_layers(uint32_t p_layers); uint32_t get_visible_layers() const; - Listener3D(); - ~Listener3D(); + AudioListener3D(); + ~AudioListener3D(); }; #endif diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 907c6cd03a..a54b10ba70 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -31,8 +31,8 @@ #include "audio_stream_player_3d.h" #include "scene/3d/area_3d.h" +#include "scene/3d/audio_listener_3d.h" #include "scene/3d/camera_3d.h" -#include "scene/3d/listener_3d.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) @@ -391,7 +391,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { bool listener_is_camera = true; Node3D *listener_node = camera; - Listener3D *listener = vp->get_listener_3d(); + AudioListener3D *listener = vp->get_audio_listener_3d(); if (listener) { listener_node = listener; listener_is_camera = false; @@ -713,15 +713,6 @@ AudioStreamPlayer3D::AttenuationModel AudioStreamPlayer3D::get_attenuation_model return attenuation_model; } -void AudioStreamPlayer3D::set_out_of_range_mode(OutOfRangeMode p_mode) { - ERR_FAIL_INDEX((int)p_mode, 2); - out_of_range_mode = p_mode; -} - -AudioStreamPlayer3D::OutOfRangeMode AudioStreamPlayer3D::get_out_of_range_mode() const { - return out_of_range_mode; -} - void AudioStreamPlayer3D::set_doppler_tracking(DopplerTracking p_tracking) { if (doppler_tracking == p_tracking) { return; @@ -832,9 +823,6 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_attenuation_model", "model"), &AudioStreamPlayer3D::set_attenuation_model); ClassDB::bind_method(D_METHOD("get_attenuation_model"), &AudioStreamPlayer3D::get_attenuation_model); - ClassDB::bind_method(D_METHOD("set_out_of_range_mode", "mode"), &AudioStreamPlayer3D::set_out_of_range_mode); - ClassDB::bind_method(D_METHOD("get_out_of_range_mode"), &AudioStreamPlayer3D::get_out_of_range_mode); - ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &AudioStreamPlayer3D::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &AudioStreamPlayer3D::get_doppler_tracking); @@ -856,7 +844,6 @@ void AudioStreamPlayer3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); @@ -875,9 +862,6 @@ void AudioStreamPlayer3D::_bind_methods() { BIND_ENUM_CONSTANT(ATTENUATION_LOGARITHMIC); BIND_ENUM_CONSTANT(ATTENUATION_DISABLED); - BIND_ENUM_CONSTANT(OUT_OF_RANGE_MIX); - BIND_ENUM_CONSTANT(OUT_OF_RANGE_PAUSE); - BIND_ENUM_CONSTANT(DOPPLER_TRACKING_DISABLED); BIND_ENUM_CONSTANT(DOPPLER_TRACKING_IDLE_STEP); BIND_ENUM_CONSTANT(DOPPLER_TRACKING_PHYSICS_STEP); diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index abd5a841b2..697bbe2381 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -51,11 +51,6 @@ public: ATTENUATION_DISABLED, }; - enum OutOfRangeMode { - OUT_OF_RANGE_MIX, - OUT_OF_RANGE_PAUSE, - }; - enum DopplerTracking { DOPPLER_TRACKING_DISABLED, DOPPLER_TRACKING_IDLE_STEP, @@ -118,8 +113,6 @@ private: DopplerTracking doppler_tracking = DOPPLER_TRACKING_DISABLED; - OutOfRangeMode out_of_range_mode = OUT_OF_RANGE_MIX; - float _get_attenuation_db(float p_distance) const; protected: @@ -182,9 +175,6 @@ public: void set_attenuation_model(AttenuationModel p_model); AttenuationModel get_attenuation_model() const; - void set_out_of_range_mode(OutOfRangeMode p_mode); - OutOfRangeMode get_out_of_range_mode() const; - void set_doppler_tracking(DopplerTracking p_tracking); DopplerTracking get_doppler_tracking() const; @@ -198,6 +188,5 @@ public: }; VARIANT_ENUM_CAST(AudioStreamPlayer3D::AttenuationModel) -VARIANT_ENUM_CAST(AudioStreamPlayer3D::OutOfRangeMode) VARIANT_ENUM_CAST(AudioStreamPlayer3D::DopplerTracking) #endif // AUDIO_STREAM_PLAYER_3D_H diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 70361f4787..afd11482e3 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -110,7 +110,7 @@ TypedArray<String> BoneAttachment3D::get_configuration_warnings() const { } 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.")); + warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); } } @@ -161,7 +161,7 @@ void BoneAttachment3D::_check_bind() { 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)); + sk->call_deferred(SNAME("connect"), "bone_pose_changed", callable_mp(this, &BoneAttachment3D::on_bone_pose_update)); bound = true; call_deferred(SNAME("on_bone_pose_update"), bone_idx); } diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 3ada9072c2..9aad338d15 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -254,10 +254,6 @@ bool Camera3D::is_current() const { } } -bool Camera3D::_can_gizmo_scale() const { - return false; -} - Vector3 Camera3D::project_ray_normal(const Point2 &p_pos) const { Vector3 ray = project_local_ray_normal(p_pos); return get_camera_transform().basis.xform(ray).normalized(); diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 3b704944b0..c1af7fa4f7 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -79,8 +79,6 @@ private: Ref<Environment> environment; Ref<CameraEffects> effects; - virtual bool _can_gizmo_scale() const; - // void _camera_make_current(Node *p_camera); friend class Viewport; void _update_audio_listener_state(); diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index d5835586f9..6328d9c67d 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -170,7 +170,7 @@ TypedArray<String> CollisionPolygon3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - warnings.push_back(TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); + warnings.push_back(TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape.")); } if (polygon.is_empty()) { diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index d4668a24f2..4e496fba47 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -115,7 +115,7 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - warnings.push_back(TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); + warnings.push_back(TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape.")); } if (!shape.is_valid()) { @@ -123,10 +123,9 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const { } if (shape.is_valid() && - Object::cast_to<RigidBody3D>(get_parent()) && - Object::cast_to<ConcavePolygonShape3D>(*shape) && - Object::cast_to<RigidBody3D>(get_parent())->get_mode() != RigidBody3D::MODE_STATIC) { - warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static.")); + Object::cast_to<RigidDynamicBody3D>(get_parent()) && + Object::cast_to<ConcavePolygonShape3D>(*shape)) { + warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static.")); } return warnings; diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index baf28ae102..32a62d8c7e 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -469,7 +469,7 @@ void GPUParticles3D::_skinning_changed() { if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { xforms.resize(draw_pass->get_builtin_bind_pose_count()); for (int j = 0; j < draw_pass->get_builtin_bind_pose_count(); j++) { - xforms.write[i] = draw_pass->get_builtin_bind_pose(j); + xforms.write[j] = draw_pass->get_builtin_bind_pose(j); } break; } diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index a34a30913e..4fa34615bf 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -420,7 +420,7 @@ Ref<Image> GPUParticlesCollisionSDF::bake() { } //test against original bounds - if (!Geometry3D::triangle_box_overlap(aabb.position + aabb.size * 0.5, aabb.size * 0.5, face.vertex)) { + if (!Geometry3D::triangle_box_overlap(aabb.get_center(), aabb.size * 0.5, face.vertex)) { continue; } @@ -438,7 +438,7 @@ Ref<Image> GPUParticlesCollisionSDF::bake() { } //test against original bounds - if (!Geometry3D::triangle_box_overlap(aabb.position + aabb.size * 0.5, aabb.size * 0.5, face.vertex)) { + if (!Geometry3D::triangle_box_overlap(aabb.get_center(), aabb.size * 0.5, face.vertex)) { continue; } diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index ab417fafdd..c787ba5087 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -30,10 +30,6 @@ #include "light_3d.h" -bool Light3D::_can_gizmo_scale() const { - return false; -} - void Light3D::set_param(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); param[p_param] = p_value; diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index ecea60339f..f788c323f7 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -86,8 +86,6 @@ private: protected: RID light; - virtual bool _can_gizmo_scale() const; - static void _bind_methods(); void _notification(int p_what); virtual void _validate_property(PropertyInfo &property) const override; diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 7dd083e314..b56f0fd145 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -467,7 +467,7 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc } } if (i == 0) { - centers.push_back(bounds.position + bounds.size * 0.5); + centers.push_back(bounds.get_center()); } else { bounds_all.merge_with(bounds); } @@ -555,7 +555,7 @@ void LightmapGI::_plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cel subcell.position = Vector3(pos) * p_cell_size; subcell.size = Vector3(half_size, half_size, half_size) * p_cell_size; - if (!Geometry3D::triangle_box_overlap(subcell.position + subcell.size * 0.5, subcell.size * 0.5, p_triangle)) { + if (!Geometry3D::triangle_box_overlap(subcell.get_center(), subcell.size * 0.5, p_triangle)) { continue; } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 00c6664e65..bbf2c81a92 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -38,8 +38,8 @@ #endif void PhysicsBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "test_only", "safe_margin"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001)); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001)); + ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1)); ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock); ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock); @@ -95,10 +95,14 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } -Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_test_only, real_t p_margin) { +Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_test_only, real_t p_margin, int p_max_collisions) { PhysicsServer3D::MotionResult result; - if (move_and_collide(p_motion, result, p_margin, p_test_only)) { - if (motion_cache.is_null()) { + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + if (move_and_collide(p_motion * delta, result, p_margin, p_test_only, p_max_collisions)) { + // Create a new instance when the cached reference is invalid or still in use in script. + if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); motion_cache->owner = this; } @@ -111,9 +115,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, bool p_collide_separation_ray, 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, int p_max_collisions, 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_collide_separation_ray, p_exclude); + bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_max_collisions, p_collide_separation_ray, p_exclude); // Restore direction of motion to be along original motion, // in order to avoid sliding due to recovery, @@ -125,9 +129,9 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M if (colliding) { // Can't just use margin as a threshold because collision depth is calculated on unsafe motion, // so even in normal resting cases the depth can be a bit more than the margin. - precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction); + precision += motion_length * (r_result.unsafe_fraction - r_result.safe_fraction); - if (r_result.collision_depth > (real_t)p_margin + precision) { + if (r_result.collisions[0].depth > (real_t)p_margin + precision) { p_cancel_sliding = false; } } @@ -167,7 +171,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M return colliding; } -bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision, real_t p_margin) { +bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) { ERR_FAIL_COND_V(!is_inside_tree(), false); PhysicsServer3D::MotionResult *r = nullptr; @@ -176,7 +180,10 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion r = const_cast<PhysicsServer3D::MotionResult *>(&r_collision->result); } - return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r); + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r, p_max_collisions); } void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { @@ -364,7 +371,7 @@ AnimatableBody3D::AnimatableBody3D() : _update_kinematic_motion(); } -void RigidBody3D::_body_enter_tree(ObjectID p_id) { +void RigidDynamicBody3D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); @@ -387,7 +394,7 @@ void RigidBody3D::_body_enter_tree(ObjectID p_id) { contact_monitor->locked = false; } -void RigidBody3D::_body_exit_tree(ObjectID p_id) { +void RigidDynamicBody3D::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); @@ -408,7 +415,7 @@ void RigidBody3D::_body_exit_tree(ObjectID p_id) { contact_monitor->locked = false; } -void RigidBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { +void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { bool body_in = p_status == 1; ObjectID objid = p_instance; @@ -427,8 +434,8 @@ void RigidBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instan //E->get().rc=0; E->get().in_tree = node && node->is_inside_tree(); if (node) { - node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody3D::_body_enter_tree), make_binds(objid)); - node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody3D::_body_exit_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree), make_binds(objid)); + node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree), make_binds(objid)); if (E->get().in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } @@ -454,8 +461,8 @@ void RigidBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instan if (E->get().shapes.is_empty()) { if (node) { - node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody3D::_body_enter_tree)); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody3D::_body_exit_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree)); if (in_tree) { emit_signal(SceneStringNames::get_singleton()->body_exited, node); } @@ -469,19 +476,19 @@ void RigidBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instan } } -struct _RigidBodyInOut { +struct _RigidDynamicBodyInOut { RID rid; ObjectID id; int shape = 0; int local_shape = 0; }; -void RigidBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { - RigidBody3D *body = (RigidBody3D *)p_instance; +void RigidDynamicBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + RigidDynamicBody3D *body = (RigidDynamicBody3D *)p_instance; body->_body_state_changed(p_state); } -void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { +void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { set_ignore_transform_notification(true); set_global_transform(p_state->get_transform()); @@ -512,9 +519,9 @@ void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { } } - _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBodyInOut)); + _RigidDynamicBodyInOut *toadd = (_RigidDynamicBodyInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidDynamicBodyInOut)); int toadd_count = 0; //state->get_contact_count(); - RigidBody3D_RemoveAction *toremove = (RigidBody3D_RemoveAction *)alloca(rc * sizeof(RigidBody3D_RemoveAction)); + RigidDynamicBody3D_RemoveAction *toremove = (RigidDynamicBody3D_RemoveAction *)alloca(rc * sizeof(RigidDynamicBody3D_RemoveAction)); int toremove_count = 0; //put the ones to add @@ -580,7 +587,7 @@ void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { } } -void RigidBody3D::_notification(int p_what) { +void RigidDynamicBody3D::_notification(int p_what) { #ifdef TOOLS_ENABLED switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -598,40 +605,73 @@ void RigidBody3D::_notification(int p_what) { #endif } -void RigidBody3D::set_mode(Mode p_mode) { - mode = p_mode; - switch (p_mode) { - case MODE_DYNAMIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); - } break; - case MODE_STATIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); - } break; - case MODE_DYNAMIC_LOCKED: { - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED); - } break; - case MODE_KINEMATIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); - } break; +void RigidDynamicBody3D::_apply_body_mode() { + if (freeze) { + switch (freeze_mode) { + case FREEZE_MODE_STATIC: { + set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); + } break; + case FREEZE_MODE_KINEMATIC: { + set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); + } break; + } + } else if (lock_rotation) { + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR); + } else { + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); + } +} + +void RigidDynamicBody3D::set_lock_rotation_enabled(bool p_lock_rotation) { + if (p_lock_rotation == lock_rotation) { + return; + } + + lock_rotation = p_lock_rotation; + _apply_body_mode(); +} + +bool RigidDynamicBody3D::is_lock_rotation_enabled() const { + return lock_rotation; +} + +void RigidDynamicBody3D::set_freeze_enabled(bool p_freeze) { + if (p_freeze == freeze) { + return; } - update_configuration_warnings(); + + freeze = p_freeze; + _apply_body_mode(); } -RigidBody3D::Mode RigidBody3D::get_mode() const { - return mode; +bool RigidDynamicBody3D::is_freeze_enabled() const { + return freeze; } -void RigidBody3D::set_mass(real_t p_mass) { +void RigidDynamicBody3D::set_freeze_mode(FreezeMode p_freeze_mode) { + if (p_freeze_mode == freeze_mode) { + return; + } + + freeze_mode = p_freeze_mode; + _apply_body_mode(); +} + +RigidDynamicBody3D::FreezeMode RigidDynamicBody3D::get_freeze_mode() const { + return freeze_mode; +} + +void RigidDynamicBody3D::set_mass(real_t p_mass) { ERR_FAIL_COND(p_mass <= 0); mass = p_mass; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_MASS, mass); } -real_t RigidBody3D::get_mass() const { +real_t RigidDynamicBody3D::get_mass() const { return mass; } -void RigidBody3D::set_inertia(const Vector3 &p_inertia) { +void RigidDynamicBody3D::set_inertia(const Vector3 &p_inertia) { ERR_FAIL_COND(p_inertia.x < 0); ERR_FAIL_COND(p_inertia.y < 0); ERR_FAIL_COND(p_inertia.z < 0); @@ -640,11 +680,11 @@ void RigidBody3D::set_inertia(const Vector3 &p_inertia) { PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_INERTIA, inertia); } -const Vector3 &RigidBody3D::get_inertia() const { +const Vector3 &RigidDynamicBody3D::get_inertia() const { return inertia; } -void RigidBody3D::set_center_of_mass_mode(CenterOfMassMode p_mode) { +void RigidDynamicBody3D::set_center_of_mass_mode(CenterOfMassMode p_mode) { if (center_of_mass_mode == p_mode) { return; } @@ -666,11 +706,11 @@ void RigidBody3D::set_center_of_mass_mode(CenterOfMassMode p_mode) { } } -RigidBody3D::CenterOfMassMode RigidBody3D::get_center_of_mass_mode() const { +RigidDynamicBody3D::CenterOfMassMode RigidDynamicBody3D::get_center_of_mass_mode() const { return center_of_mass_mode; } -void RigidBody3D::set_center_of_mass(const Vector3 &p_center_of_mass) { +void RigidDynamicBody3D::set_center_of_mass(const Vector3 &p_center_of_mass) { if (center_of_mass == p_center_of_mass) { return; } @@ -681,88 +721,88 @@ void RigidBody3D::set_center_of_mass(const Vector3 &p_center_of_mass) { PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS, center_of_mass); } -const Vector3 &RigidBody3D::get_center_of_mass() const { +const Vector3 &RigidDynamicBody3D::get_center_of_mass() const { return center_of_mass; } -void RigidBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { +void RigidDynamicBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { - if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics))) { - physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics)); + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody3D::_reload_physics_characteristics))) { + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody3D::_reload_physics_characteristics)); } } physics_material_override = p_physics_material_override; if (physics_material_override.is_valid()) { - physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics)); + physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidDynamicBody3D::_reload_physics_characteristics)); } _reload_physics_characteristics(); } -Ref<PhysicsMaterial> RigidBody3D::get_physics_material_override() const { +Ref<PhysicsMaterial> RigidDynamicBody3D::get_physics_material_override() const { return physics_material_override; } -void RigidBody3D::set_gravity_scale(real_t p_gravity_scale) { +void RigidDynamicBody3D::set_gravity_scale(real_t p_gravity_scale) { gravity_scale = p_gravity_scale; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_GRAVITY_SCALE, gravity_scale); } -real_t RigidBody3D::get_gravity_scale() const { +real_t RigidDynamicBody3D::get_gravity_scale() const { return gravity_scale; } -void RigidBody3D::set_linear_damp(real_t p_linear_damp) { +void RigidDynamicBody3D::set_linear_damp(real_t p_linear_damp) { ERR_FAIL_COND(p_linear_damp < -1); linear_damp = p_linear_damp; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_LINEAR_DAMP, linear_damp); } -real_t RigidBody3D::get_linear_damp() const { +real_t RigidDynamicBody3D::get_linear_damp() const { return linear_damp; } -void RigidBody3D::set_angular_damp(real_t p_angular_damp) { +void RigidDynamicBody3D::set_angular_damp(real_t p_angular_damp) { ERR_FAIL_COND(p_angular_damp < -1); angular_damp = p_angular_damp; PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP, angular_damp); } -real_t RigidBody3D::get_angular_damp() const { +real_t RigidDynamicBody3D::get_angular_damp() const { return angular_damp; } -void RigidBody3D::set_axis_velocity(const Vector3 &p_axis) { +void RigidDynamicBody3D::set_axis_velocity(const Vector3 &p_axis) { Vector3 axis = p_axis.normalized(); 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) { +void RigidDynamicBody3D::set_linear_velocity(const Vector3 &p_velocity) { linear_velocity = p_velocity; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } -Vector3 RigidBody3D::get_linear_velocity() const { +Vector3 RigidDynamicBody3D::get_linear_velocity() const { return linear_velocity; } -void RigidBody3D::set_angular_velocity(const Vector3 &p_velocity) { +void RigidDynamicBody3D::set_angular_velocity(const Vector3 &p_velocity) { angular_velocity = p_velocity; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } -Vector3 RigidBody3D::get_angular_velocity() const { +Vector3 RigidDynamicBody3D::get_angular_velocity() const { return angular_velocity; } -Basis RigidBody3D::get_inverse_inertia_tensor() const { +Basis RigidDynamicBody3D::get_inverse_inertia_tensor() const { return inverse_inertia_tensor; } -void RigidBody3D::set_use_custom_integrator(bool p_enable) { +void RigidDynamicBody3D::set_use_custom_integrator(bool p_enable) { if (custom_integrator == p_enable) { return; } @@ -771,73 +811,73 @@ void RigidBody3D::set_use_custom_integrator(bool p_enable) { PhysicsServer3D::get_singleton()->body_set_omit_force_integration(get_rid(), p_enable); } -bool RigidBody3D::is_using_custom_integrator() { +bool RigidDynamicBody3D::is_using_custom_integrator() { return custom_integrator; } -void RigidBody3D::set_sleeping(bool p_sleeping) { +void RigidDynamicBody3D::set_sleeping(bool p_sleeping) { sleeping = p_sleeping; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_SLEEPING, sleeping); } -void RigidBody3D::set_can_sleep(bool p_active) { +void RigidDynamicBody3D::set_can_sleep(bool p_active) { can_sleep = p_active; PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_CAN_SLEEP, p_active); } -bool RigidBody3D::is_able_to_sleep() const { +bool RigidDynamicBody3D::is_able_to_sleep() const { return can_sleep; } -bool RigidBody3D::is_sleeping() const { +bool RigidDynamicBody3D::is_sleeping() const { return sleeping; } -void RigidBody3D::set_max_contacts_reported(int p_amount) { +void RigidDynamicBody3D::set_max_contacts_reported(int p_amount) { max_contacts_reported = p_amount; PhysicsServer3D::get_singleton()->body_set_max_contacts_reported(get_rid(), p_amount); } -int RigidBody3D::get_max_contacts_reported() const { +int RigidDynamicBody3D::get_max_contacts_reported() const { return max_contacts_reported; } -void RigidBody3D::add_central_force(const Vector3 &p_force) { +void RigidDynamicBody3D::add_central_force(const Vector3 &p_force) { PhysicsServer3D::get_singleton()->body_add_central_force(get_rid(), p_force); } -void RigidBody3D::add_force(const Vector3 &p_force, const Vector3 &p_position) { +void RigidDynamicBody3D::add_force(const Vector3 &p_force, const Vector3 &p_position) { PhysicsServer3D *singleton = PhysicsServer3D::get_singleton(); singleton->body_add_force(get_rid(), p_force, p_position); } -void RigidBody3D::add_torque(const Vector3 &p_torque) { +void RigidDynamicBody3D::add_torque(const Vector3 &p_torque) { PhysicsServer3D::get_singleton()->body_add_torque(get_rid(), p_torque); } -void RigidBody3D::apply_central_impulse(const Vector3 &p_impulse) { +void RigidDynamicBody3D::apply_central_impulse(const Vector3 &p_impulse) { PhysicsServer3D::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); } -void RigidBody3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) { +void RigidDynamicBody3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) { PhysicsServer3D *singleton = PhysicsServer3D::get_singleton(); singleton->body_apply_impulse(get_rid(), p_impulse, p_position); } -void RigidBody3D::apply_torque_impulse(const Vector3 &p_impulse) { +void RigidDynamicBody3D::apply_torque_impulse(const Vector3 &p_impulse) { PhysicsServer3D::get_singleton()->body_apply_torque_impulse(get_rid(), p_impulse); } -void RigidBody3D::set_use_continuous_collision_detection(bool p_enable) { +void RigidDynamicBody3D::set_use_continuous_collision_detection(bool p_enable) { ccd = p_enable; PhysicsServer3D::get_singleton()->body_set_enable_continuous_collision_detection(get_rid(), p_enable); } -bool RigidBody3D::is_using_continuous_collision_detection() const { +bool RigidDynamicBody3D::is_using_continuous_collision_detection() const { return ccd; } -void RigidBody3D::set_contact_monitor(bool p_enabled) { +void RigidDynamicBody3D::set_contact_monitor(bool p_enabled) { if (p_enabled == is_contact_monitor_enabled()) { return; } @@ -851,8 +891,8 @@ void RigidBody3D::set_contact_monitor(bool p_enabled) { Node *node = Object::cast_to<Node>(obj); if (node) { - node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody3D::_body_enter_tree)); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody3D::_body_exit_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree)); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree)); } } @@ -864,11 +904,11 @@ void RigidBody3D::set_contact_monitor(bool p_enabled) { } } -bool RigidBody3D::is_contact_monitor_enabled() const { +bool RigidDynamicBody3D::is_contact_monitor_enabled() const { return contact_monitor != nullptr; } -Array RigidBody3D::get_colliding_bodies() const { +Array RigidDynamicBody3D::get_colliding_bodies() const { ERR_FAIL_COND_V(!contact_monitor, Array()); Array ret; @@ -886,87 +926,92 @@ Array RigidBody3D::get_colliding_bodies() const { return ret; } -TypedArray<String> RigidBody3D::get_configuration_warnings() const { +TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const { Transform3D t = get_transform(); TypedArray<String> warnings = Node::get_configuration_warnings(); - if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to RigidBody3D (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05) { + warnings.push_back(TTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } -void RigidBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidBody3D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidBody3D::get_mode); +void RigidDynamicBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody3D::set_mass); + ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody3D::get_mass); + + ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidDynamicBody3D::set_inertia); + ClassDB::bind_method(D_METHOD("get_inertia"), &RigidDynamicBody3D::get_inertia); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidBody3D::set_mass); - ClassDB::bind_method(D_METHOD("get_mass"), &RigidBody3D::get_mass); + ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidDynamicBody3D::set_center_of_mass_mode); + ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidDynamicBody3D::get_center_of_mass_mode); - ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidBody3D::set_inertia); - ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody3D::get_inertia); + ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidDynamicBody3D::set_center_of_mass); + ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidDynamicBody3D::get_center_of_mass); - ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidBody3D::set_center_of_mass_mode); - ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidBody3D::get_center_of_mass_mode); + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidDynamicBody3D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidDynamicBody3D::get_physics_material_override); - ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidBody3D::set_center_of_mass); - ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidBody3D::get_center_of_mass); + ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidDynamicBody3D::set_linear_velocity); + ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidDynamicBody3D::get_linear_velocity); - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody3D::set_physics_material_override); - ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody3D::get_physics_material_override); + ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &RigidDynamicBody3D::set_angular_velocity); + ClassDB::bind_method(D_METHOD("get_angular_velocity"), &RigidDynamicBody3D::get_angular_velocity); - ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody3D::set_linear_velocity); - ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody3D::get_linear_velocity); + ClassDB::bind_method(D_METHOD("get_inverse_inertia_tensor"), &RigidDynamicBody3D::get_inverse_inertia_tensor); - ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &RigidBody3D::set_angular_velocity); - ClassDB::bind_method(D_METHOD("get_angular_velocity"), &RigidBody3D::get_angular_velocity); + ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidDynamicBody3D::set_gravity_scale); + ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidDynamicBody3D::get_gravity_scale); - ClassDB::bind_method(D_METHOD("get_inverse_inertia_tensor"), &RigidBody3D::get_inverse_inertia_tensor); + ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidDynamicBody3D::set_linear_damp); + ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidDynamicBody3D::get_linear_damp); - ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody3D::set_gravity_scale); - ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody3D::get_gravity_scale); + ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &RigidDynamicBody3D::set_angular_damp); + ClassDB::bind_method(D_METHOD("get_angular_damp"), &RigidDynamicBody3D::get_angular_damp); - ClassDB::bind_method(D_METHOD("set_linear_damp", "linear_damp"), &RigidBody3D::set_linear_damp); - ClassDB::bind_method(D_METHOD("get_linear_damp"), &RigidBody3D::get_linear_damp); + ClassDB::bind_method(D_METHOD("set_max_contacts_reported", "amount"), &RigidDynamicBody3D::set_max_contacts_reported); + ClassDB::bind_method(D_METHOD("get_max_contacts_reported"), &RigidDynamicBody3D::get_max_contacts_reported); - ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &RigidBody3D::set_angular_damp); - ClassDB::bind_method(D_METHOD("get_angular_damp"), &RigidBody3D::get_angular_damp); + ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &RigidDynamicBody3D::set_use_custom_integrator); + ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &RigidDynamicBody3D::is_using_custom_integrator); - ClassDB::bind_method(D_METHOD("set_max_contacts_reported", "amount"), &RigidBody3D::set_max_contacts_reported); - ClassDB::bind_method(D_METHOD("get_max_contacts_reported"), &RigidBody3D::get_max_contacts_reported); + ClassDB::bind_method(D_METHOD("set_contact_monitor", "enabled"), &RigidDynamicBody3D::set_contact_monitor); + ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled"), &RigidDynamicBody3D::is_contact_monitor_enabled); - ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &RigidBody3D::set_use_custom_integrator); - ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &RigidBody3D::is_using_custom_integrator); + ClassDB::bind_method(D_METHOD("set_use_continuous_collision_detection", "enable"), &RigidDynamicBody3D::set_use_continuous_collision_detection); + ClassDB::bind_method(D_METHOD("is_using_continuous_collision_detection"), &RigidDynamicBody3D::is_using_continuous_collision_detection); - ClassDB::bind_method(D_METHOD("set_contact_monitor", "enabled"), &RigidBody3D::set_contact_monitor); - ClassDB::bind_method(D_METHOD("is_contact_monitor_enabled"), &RigidBody3D::is_contact_monitor_enabled); + ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidDynamicBody3D::set_axis_velocity); - ClassDB::bind_method(D_METHOD("set_use_continuous_collision_detection", "enable"), &RigidBody3D::set_use_continuous_collision_detection); - ClassDB::bind_method(D_METHOD("is_using_continuous_collision_detection"), &RigidBody3D::is_using_continuous_collision_detection); + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidDynamicBody3D::add_central_force); + ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &RigidDynamicBody3D::add_force, Vector3()); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidDynamicBody3D::add_torque); - ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody3D::set_axis_velocity); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidDynamicBody3D::apply_central_impulse); + ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidDynamicBody3D::apply_impulse, Vector3()); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidDynamicBody3D::apply_torque_impulse); - ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidBody3D::add_central_force); - ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &RigidBody3D::add_force, Vector3()); - ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidBody3D::add_torque); + ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidDynamicBody3D::set_sleeping); + ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidDynamicBody3D::is_sleeping); - ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody3D::apply_central_impulse); - ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidBody3D::apply_impulse, Vector3()); - ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidBody3D::apply_torque_impulse); + ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody3D::set_can_sleep); + ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody3D::is_able_to_sleep); - ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody3D::set_sleeping); - ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody3D::is_sleeping); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody3D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody3D::is_lock_rotation_enabled); - ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidBody3D::set_can_sleep); - ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidBody3D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody3D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody3D::is_freeze_enabled); - ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody3D::get_colliding_bodies); + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody3D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody3D::get_freeze_mode); + + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody3D::get_colliding_bodies); 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,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); @@ -980,6 +1025,9 @@ void RigidBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); @@ -993,16 +1041,14 @@ void RigidBody3D::_bind_methods() { ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); - BIND_ENUM_CONSTANT(MODE_DYNAMIC); - BIND_ENUM_CONSTANT(MODE_STATIC); - BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); - BIND_ENUM_CONSTANT(MODE_KINEMATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); } -void RigidBody3D::_validate_property(PropertyInfo &property) const { +void RigidDynamicBody3D::_validate_property(PropertyInfo &property) const { if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) { if (property.name == "center_of_mass") { property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; @@ -1010,18 +1056,18 @@ void RigidBody3D::_validate_property(PropertyInfo &property) const { } } -RigidBody3D::RigidBody3D() : +RigidDynamicBody3D::RigidDynamicBody3D() : PhysicsBody3D(PhysicsServer3D::BODY_MODE_DYNAMIC) { PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); } -RigidBody3D::~RigidBody3D() { +RigidDynamicBody3D::~RigidDynamicBody3D() { if (contact_monitor) { memdelete(contact_monitor); } } -void RigidBody3D::_reload_physics_characteristics() { +void RigidDynamicBody3D::_reload_physics_characteristics() { if (physics_material_override.is_null()) { PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, 0); PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_FRICTION, 1); @@ -1037,64 +1083,115 @@ void RigidBody3D::_reload_physics_characteristics() { #define FLOOR_ANGLE_THRESHOLD 0.01 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 double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); - + previous_position = get_global_transform().origin; for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { linear_velocity[i] = 0.0; } } - Vector3 current_floor_velocity = floor_velocity; - if ((on_floor || on_wall) && on_floor_body.is_valid()) { - //this approach makes sure there is less delay between the actual body velocity and the one we saved - PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(on_floor_body); - if (bs) { - Transform3D gt = get_global_transform(); - Vector3 local_position = gt.origin - bs->get_transform().origin; - current_floor_velocity = bs->get_velocity_at_local_position(local_position); + Vector3 current_platform_velocity = platform_velocity; + + if ((collision_state.floor || collision_state.wall) && platform_rid.is_valid()) { + bool excluded = false; + if (collision_state.floor) { + excluded = (moving_platform_floor_layers & platform_layer) == 0; + } else if (collision_state.wall) { + excluded = (moving_platform_wall_layers & platform_layer) == 0; + } + if (!excluded) { + //this approach makes sure there is less delay between the actual body velocity and the one we saved + PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(platform_rid); + if (bs) { + Transform3D gt = get_global_transform(); + Vector3 local_position = gt.origin - bs->get_transform().origin; + current_platform_velocity = bs->get_velocity_at_local_position(local_position); + } + } else { + current_platform_velocity = Vector3(); } } motion_results.clear(); - on_floor = false; - on_ceiling = false; - on_wall = false; - floor_normal = Vector3(); - floor_velocity = Vector3(); - if (!current_floor_velocity.is_equal_approx(Vector3()) && on_floor_body.is_valid()) { + bool was_on_floor = collision_state.floor; + collision_state.state = 0; + + if (!current_platform_velocity.is_equal_approx(Vector3())) { 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, false, exclude)) { + exclude.insert(platform_rid); + if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, 1, false, false, exclude)) { motion_results.push_back(floor_result); - _set_collision_direction(floor_result); + + CollisionState result_state; + _set_collision_direction(floor_result, result_state); } } - on_floor_body = RID(); - Vector3 motion = linear_velocity * delta; + if (motion_mode == MOTION_MODE_GROUNDED) { + _move_and_slide_grounded(delta, was_on_floor); + } else { + _move_and_slide_free(delta); + } + + // Compute real velocity. + real_velocity = get_position_delta() / delta; + + if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) { + // Add last platform velocity when just left a moving platform. + if (!collision_state.floor && !collision_state.wall) { + if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { + current_platform_velocity = current_platform_velocity.slide(up_direction); + } + linear_velocity += current_platform_velocity; + } + } + + // Reset the gravity accumulation when touching the ground. + if (collision_state.floor && linear_velocity.dot(up_direction) <= 0) { + linear_velocity = linear_velocity.slide(up_direction); + } + + return motion_results.size() > 0; +} + +void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) { + Vector3 motion = linear_velocity * p_delta; + Vector3 motion_slide_up = motion.slide(up_direction); + Vector3 prev_floor_normal = floor_normal; + + platform_rid = RID(); + platform_velocity = Vector3(); + floor_normal = Vector3(); + wall_normal = Vector3(); // No sliding on first attempt to keep floor motion stable when possible, - // when stop on slope is enabled. + // When stop on slope is enabled or when there is no up direction. bool sliding_enabled = !floor_stop_on_slope; + // Constant speed can be applied only the first time sliding is enabled. + bool can_apply_constant_speed = sliding_enabled; + bool first_slide = true; + bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0; + Vector3 total_travel; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionResult result; - bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled); + bool collided = move_and_collide(motion, result, margin, false, 4, !sliding_enabled); + if (collided) { motion_results.push_back(result); - _set_collision_direction(result); - if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { + CollisionState previous_state = collision_state; + + CollisionState result_state; + _set_collision_direction(result, result_state); + + if (collision_state.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 { + if (result.travel.length() <= margin + CMP_EPSILON) { gt.origin -= result.travel; } set_global_transform(gt); @@ -1105,94 +1202,358 @@ bool CharacterBody3D::move_and_slide() { if (result.remainder.is_equal_approx(Vector3())) { motion = Vector3(); + last_motion = result.travel; break; } - 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(); + // Apply regular sliding by default. + bool apply_default_sliding = true; + + // Wall collision checks. + if (result_state.wall && (motion_slide_up.dot(wall_normal) <= 0)) { + // Move on floor only checks. + if (floor_block_on_wall) { + // Needs horizontal motion from current motion instead of motion_slide_up + // to properly test the angle and avoid standing on slopes + Vector3 horizontal_motion = motion.slide(up_direction); + Vector3 horizontal_normal = wall_normal.slide(up_direction).normalized(); + real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(horizontal_motion.normalized()))); + + // Avoid to move forward on a wall if floor_block_on_wall is true. + // Applies only when the motion angle is under 90 degrees, + // in order to avoid blocking lateral motion along a wall. + if (motion_angle < .5 * Math_PI) { + apply_default_sliding = false; + + if (p_was_on_floor && !vel_dir_facing_up) { + // Cancel the motion. + Transform3D gt = get_global_transform(); + real_t travel_total = result.travel.length(); + real_t cancel_dist_max = MIN(0.1, margin * 20); + if (travel_total <= margin + CMP_EPSILON) { + gt.origin -= result.travel; + } else if (travel_total < cancel_dist_max) { // If the movement is large the body can be prevented from reaching the walls. + gt.origin -= result.travel.slide(up_direction); + // Keep remaining motion in sync with amount canceled. + motion = motion.slide(up_direction); + } + set_global_transform(gt); + result.travel = Vector3(); // Cancel for constant speed computation. + + // Determines if you are on the ground, and limits the possibility of climbing on the walls because of the approximations. + _snap_on_floor(true, false); + } else { + // If the movement is not cancelled we only keep the remaining. + motion = result.remainder; + } + + // Apply slide on forward in order to allow only lateral motion on next step. + Vector3 forward = wall_normal.slide(up_direction).normalized(); + motion = motion.slide(forward); + // Avoid accelerating when you jump on the wall and smooth falling. + linear_velocity = linear_velocity.slide(forward); + + // Allow only lateral motion along previous floor when already on floor. + // Fixes slowing down when moving in diagonal against an inclined wall. + if (p_was_on_floor && !vel_dir_facing_up && (motion.dot(up_direction) > 0.0)) { + // Slide along the corner between the wall and previous floor. + Vector3 floor_side = prev_floor_normal.cross(wall_normal); + if (floor_side != Vector3()) { + motion = floor_side * motion.dot(floor_side); + } + } + + // Stop all motion when a second wall is hit (unless sliding down or jumping), + // in order to avoid jittering in corner cases. + bool stop_all_motion = previous_state.wall && !vel_dir_facing_up; + + // Allow sliding when the body falls. + if (!collision_state.floor && motion.dot(up_direction) < 0) { + Vector3 slide_motion = motion.slide(wall_normal); + // Test again to allow sliding only if the result goes downwards. + // Fixes jittering issues at the bottom of inclined walls. + if (slide_motion.dot(up_direction) < 0) { + stop_all_motion = false; + motion = slide_motion; + } + } + + if (stop_all_motion) { + motion = Vector3(); + linear_velocity = Vector3(); + } + } } - } else { - motion = result.remainder; + + // Stop horizontal motion when under wall slide threshold. + if (p_was_on_floor && (wall_min_slide_angle > 0.0) && result_state.wall) { + Vector3 horizontal_normal = wall_normal.slide(up_direction).normalized(); + real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(motion_slide_up.normalized()))); + if (motion_angle < wall_min_slide_angle) { + motion = up_direction * motion.dot(up_direction); + linear_velocity = up_direction * linear_velocity.dot(up_direction); + + apply_default_sliding = false; + } + } + } + + if (apply_default_sliding) { + // Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling. + if ((sliding_enabled || !collision_state.floor) && (!collision_state.ceiling || slide_on_ceiling || !vel_dir_facing_up)) { + const PhysicsServer3D::MotionCollision &collision = result.collisions[0]; + Vector3 slide_motion = result.remainder.slide(collision.normal); + if (slide_motion.dot(linear_velocity) > 0.0) { + motion = slide_motion; + } else { + motion = Vector3(); + } + if (slide_on_ceiling && result_state.ceiling) { + // Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall. + if (vel_dir_facing_up) { + linear_velocity = linear_velocity.slide(collision.normal); + } else { + // Avoid acceleration in slope when falling. + linear_velocity = up_direction * up_direction.dot(linear_velocity); + } + } + } + // No sliding on first attempt to keep floor motion stable when possible. + else { + motion = result.remainder; + if (result_state.ceiling && !slide_on_ceiling && vel_dir_facing_up) { + linear_velocity = linear_velocity.slide(up_direction); + motion = motion.slide(up_direction); + } + } + } + + // Apply Constant Speed. + if (p_was_on_floor && floor_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) { + motion = motion.normalized() * MAX(0, (motion_slide_up.length() - result.travel.slide(up_direction).length() - total_travel.slide(up_direction).length())); } + + total_travel += result.travel; + } + // When you move forward in a downward slope you don’t collide because you will be in the air. + // This test ensures that constant speed is applied, only if the player is still on the ground after the snap is applied. + else if (floor_constant_speed && first_slide && _on_floor_if_snapped(p_was_on_floor, vel_dir_facing_up)) { + can_apply_constant_speed = false; + sliding_enabled = true; + Transform3D gt = get_global_transform(); + gt.origin = gt.origin - result.travel; + set_global_transform(gt); + + Vector3 motion_slide_norm = motion.slide(prev_floor_normal).normalized(); + motion = motion_slide_norm * (motion_slide_up.length()); + collided = true; } + can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled; sliding_enabled = true; + first_slide = false; + + if (!motion.is_equal_approx(Vector3())) { + last_motion = motion; + } 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(); + _snap_on_floor(p_was_on_floor, vel_dir_facing_up); + + // Reset the gravity accumulation when touching the ground. + if (collision_state.floor && !vel_dir_facing_up) { + linear_velocity = linear_velocity.slide(up_direction); + } +} + +void CharacterBody3D::_move_and_slide_free(double p_delta) { + Vector3 motion = linear_velocity * p_delta; + + platform_rid = RID(); + floor_normal = Vector3(); + platform_velocity = Vector3(); + + bool first_slide = true; + for (int iteration = 0; iteration < max_slides; ++iteration) { 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. + + bool collided = move_and_collide(motion, result, margin, false, 1, false); + + if (collided) { + motion_results.push_back(result); + + CollisionState result_state; + _set_collision_direction(result, result_state); + + if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-linear_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { + motion = Vector3(); + if (result.travel.length() < margin + CMP_EPSILON) { + Transform3D gt = get_global_transform(); + gt.origin -= result.travel; + set_global_transform(gt); } + } else if (first_slide) { + Vector3 motion_slide_norm = result.remainder.slide(wall_normal).normalized(); + motion = motion_slide_norm * (motion.length() - result.travel.length()); + } else { + motion = result.remainder.slide(wall_normal); } - if (apply) { - gt.origin += result.travel; - set_global_transform(gt); + + if (motion.dot(linear_velocity) <= 0.0) { + motion = Vector3(); } } + + first_slide = false; + + if (!collided || motion.is_equal_approx(Vector3())) { + break; + } } +} - if (!on_floor && !on_wall) { - // Add last platform velocity when just left a moving platform. - linear_velocity += current_floor_velocity; +void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) { + if (collision_state.floor || !was_on_floor || vel_dir_facing_up) { + 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); + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + + Transform3D gt = get_global_transform(); + PhysicsServer3D::MotionResult result; + if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) { + CollisionState result_state; + // Apply direction for floor only. + _set_collision_direction(result, result_state, CollisionState(true, false, false)); + + if (result_state.floor) { + 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 = up_direction * up_direction.dot(result.travel); + } else { + result.travel = Vector3(); + } + } + + gt.origin += result.travel; + set_global_transform(gt); + } } +} - return motion_results.size() > 0; +bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { + if (up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) { + return false; + } + + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + + PhysicsServer3D::MotionResult result; + if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) { + CollisionState result_state; + // Don't apply direction for any type. + _set_collision_direction(result, result_state, CollisionState()); + + return result_state.floor; + } + + return false; } -void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResult &p_result) { - if (up_direction == Vector3()) { - //all is a wall - on_wall = true; - } else { - 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 (p_result.get_angle(-up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling - on_ceiling = true; - } else { - on_wall = true; +void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResult &p_result, CollisionState &r_state, CollisionState p_apply_state) { + r_state.state = 0; + + real_t wall_depth = -1.0; + real_t floor_depth = -1.0; + + bool was_on_wall = collision_state.wall; + Vector3 prev_wall_normal = wall_normal; + int wall_collision_count = 0; + Vector3 combined_wall_normal; + + for (int i = p_result.collision_count - 1; i >= 0; i--) { + const PhysicsServer3D::MotionCollision &collision = p_result.collisions[i]; + + if (motion_mode == MOTION_MODE_GROUNDED) { + // Check if any collision is floor. + real_t floor_angle = collision.get_angle(up_direction); + if (floor_angle <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { + r_state.floor = true; + if (p_apply_state.floor && collision.depth > floor_depth) { + collision_state.floor = true; + floor_normal = collision.normal; + floor_depth = collision.depth; + _set_platform_data(collision); + } + continue; + } + + // Check if any collision is ceiling. + real_t ceiling_angle = collision.get_angle(-up_direction); + if (ceiling_angle <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { + r_state.ceiling = true; + if (p_apply_state.ceiling) { + collision_state.ceiling = true; + } + continue; + } + } + + // Collision is wall by default. + r_state.wall = true; + + if (p_apply_state.wall && collision.depth > wall_depth) { + collision_state.wall = true; + wall_depth = collision.depth; + wall_normal = collision.normal; + // 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; + if (Object::cast_to<CharacterBody3D>(ObjectDB::get_instance(collision.collider_id)) == nullptr) { + _set_platform_data(collision); } } + + // Collect normal for calculating average. + combined_wall_normal += collision.normal; + wall_collision_count++; } + + if (r_state.wall) { + if (wall_collision_count > 1 && !r_state.floor) { + // Check if wall normals cancel out to floor support. + if (!r_state.floor && motion_mode == MOTION_MODE_GROUNDED) { + combined_wall_normal.normalize(); + real_t floor_angle = Math::acos(combined_wall_normal.dot(up_direction)); + if (floor_angle <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { + r_state.floor = true; + r_state.wall = false; + if (p_apply_state.floor) { + collision_state.floor = true; + floor_normal = combined_wall_normal; + } + if (p_apply_state.wall) { + collision_state.wall = was_on_wall; + wall_normal = prev_wall_normal; + } + return; + } + } + } + } +} + +void CharacterBody3D::_set_platform_data(const PhysicsServer3D::MotionCollision &p_collision) { + platform_rid = p_collision.collider; + platform_velocity = p_collision.collider_velocity; + platform_layer = PhysicsServer3D::get_singleton()->body_get_collision_layer(platform_rid); } void CharacterBody3D::set_safe_margin(real_t p_margin) { @@ -1212,40 +1573,56 @@ void CharacterBody3D::set_linear_velocity(const Vector3 &p_velocity) { } bool CharacterBody3D::is_on_floor() const { - return on_floor; + return collision_state.floor; } bool CharacterBody3D::is_on_floor_only() const { - return on_floor && !on_wall && !on_ceiling; + return collision_state.floor && !collision_state.wall && !collision_state.ceiling; } bool CharacterBody3D::is_on_wall() const { - return on_wall; + return collision_state.wall; } bool CharacterBody3D::is_on_wall_only() const { - return on_wall && !on_floor && !on_ceiling; + return collision_state.wall && !collision_state.floor && !collision_state.ceiling; } bool CharacterBody3D::is_on_ceiling() const { - return on_ceiling; + return collision_state.ceiling; } bool CharacterBody3D::is_on_ceiling_only() const { - return on_ceiling && !on_floor && !on_wall; + return collision_state.ceiling && !collision_state.floor && !collision_state.wall; } Vector3 CharacterBody3D::get_floor_normal() const { return floor_normal; } +Vector3 CharacterBody3D::get_wall_normal() const { + return wall_normal; +} + +Vector3 CharacterBody3D::get_last_motion() const { + return last_motion; +} + +Vector3 CharacterBody3D::get_position_delta() const { + return get_transform().origin - previous_position; +} + +Vector3 CharacterBody3D::get_real_velocity() const { + return real_velocity; +}; + 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; + return platform_velocity; } int CharacterBody3D::get_slide_collision_count() const { @@ -1263,7 +1640,8 @@ Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) { slide_colliders.resize(p_bounce + 1); } - if (slide_colliders[p_bounce].is_null()) { + // Create a new instance when the cached reference is invalid or still in use in script. + if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->reference_get_count() > 1) { slide_colliders.write[p_bounce].instantiate(); slide_colliders.write[p_bounce]->owner = this; } @@ -1287,6 +1665,62 @@ void CharacterBody3D::set_floor_stop_on_slope_enabled(bool p_enabled) { floor_stop_on_slope = p_enabled; } +bool CharacterBody3D::is_floor_constant_speed_enabled() const { + return floor_constant_speed; +} + +void CharacterBody3D::set_floor_constant_speed_enabled(bool p_enabled) { + floor_constant_speed = p_enabled; +} + +bool CharacterBody3D::is_floor_block_on_wall_enabled() const { + return floor_block_on_wall; +} + +void CharacterBody3D::set_floor_block_on_wall_enabled(bool p_enabled) { + floor_block_on_wall = p_enabled; +} + +bool CharacterBody3D::is_slide_on_ceiling_enabled() const { + return slide_on_ceiling; +} + +void CharacterBody3D::set_slide_on_ceiling_enabled(bool p_enabled) { + slide_on_ceiling = p_enabled; +} + +uint32_t CharacterBody3D::get_moving_platform_floor_layers() const { + return moving_platform_floor_layers; +} + +void CharacterBody3D::set_moving_platform_floor_layers(uint32_t p_exclude_layers) { + moving_platform_floor_layers = p_exclude_layers; +} + +uint32_t CharacterBody3D::get_moving_platform_wall_layers() const { + return moving_platform_wall_layers; +} + +void CharacterBody3D::set_moving_platform_wall_layers(uint32_t p_exclude_layers) { + moving_platform_wall_layers = p_exclude_layers; +} + +void CharacterBody3D::set_motion_mode(MotionMode p_mode) { + motion_mode = p_mode; +} + +CharacterBody3D::MotionMode CharacterBody3D::get_motion_mode() const { + return motion_mode; +} + +void CharacterBody3D::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) { + moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity; +} + +CharacterBody3D::MovingPlatformApplyVelocityOnLeave CharacterBody3D::get_moving_platform_apply_velocity_on_leave() const { + return moving_platform_apply_velocity_on_leave; +} + int CharacterBody3D::get_max_slides() const { return max_slides; } @@ -1304,12 +1738,21 @@ void CharacterBody3D::set_floor_max_angle(real_t p_radians) { floor_max_angle = p_radians; } -const Vector3 &CharacterBody3D::get_snap() const { - return snap; +real_t CharacterBody3D::get_floor_snap_length() { + return floor_snap_length; } -void CharacterBody3D::set_snap(const Vector3 &p_snap) { - snap = p_snap; +void CharacterBody3D::set_floor_snap_length(real_t p_floor_snap_length) { + ERR_FAIL_COND(p_floor_snap_length < 0); + floor_snap_length = p_floor_snap_length; +} + +real_t CharacterBody3D::get_wall_min_slide_angle() const { + return wall_min_slide_angle; +} + +void CharacterBody3D::set_wall_min_slide_angle(real_t p_radians) { + wall_min_slide_angle = p_radians; } const Vector3 &CharacterBody3D::get_up_direction() const { @@ -1317,6 +1760,7 @@ const Vector3 &CharacterBody3D::get_up_direction() const { } void CharacterBody3D::set_up_direction(const Vector3 &p_up_direction) { + ERR_FAIL_COND_MSG(p_up_direction == Vector3(), "up_direction can't be equal to Vector3.ZERO, consider using Free motion mode instead."); up_direction = p_up_direction.normalized(); } @@ -1324,12 +1768,10 @@ void CharacterBody3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { // Reset move_and_slide() data. - on_floor = false; - on_floor_body = RID(); - on_ceiling = false; - on_wall = false; + collision_state.state = 0; + platform_rid = RID(); motion_results.clear(); - floor_velocity = Vector3(); + platform_velocity = Vector3(); } break; } } @@ -1344,14 +1786,32 @@ void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin); 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("set_floor_constant_speed_enabled", "enabled"), &CharacterBody3D::set_floor_constant_speed_enabled); + ClassDB::bind_method(D_METHOD("is_floor_constant_speed_enabled"), &CharacterBody3D::is_floor_constant_speed_enabled); + ClassDB::bind_method(D_METHOD("set_floor_block_on_wall_enabled", "enabled"), &CharacterBody3D::set_floor_block_on_wall_enabled); + ClassDB::bind_method(D_METHOD("is_floor_block_on_wall_enabled"), &CharacterBody3D::is_floor_block_on_wall_enabled); + ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody3D::set_slide_on_ceiling_enabled); + ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody3D::is_slide_on_ceiling_enabled); + + ClassDB::bind_method(D_METHOD("set_moving_platform_floor_layers", "exclude_layer"), &CharacterBody3D::set_moving_platform_floor_layers); + ClassDB::bind_method(D_METHOD("get_moving_platform_floor_layers"), &CharacterBody3D::get_moving_platform_floor_layers); + ClassDB::bind_method(D_METHOD("set_moving_platform_wall_layers", "exclude_layer"), &CharacterBody3D::set_moving_platform_wall_layers); + ClassDB::bind_method(D_METHOD("get_moving_platform_wall_layers"), &CharacterBody3D::get_moving_platform_wall_layers); + 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); ClassDB::bind_method(D_METHOD("set_floor_max_angle", "radians"), &CharacterBody3D::set_floor_max_angle); - ClassDB::bind_method(D_METHOD("get_snap"), &CharacterBody3D::get_snap); - ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CharacterBody3D::set_snap); + ClassDB::bind_method(D_METHOD("get_floor_snap_length"), &CharacterBody3D::get_floor_snap_length); + ClassDB::bind_method(D_METHOD("set_floor_snap_length", "floor_snap_length"), &CharacterBody3D::set_floor_snap_length); + ClassDB::bind_method(D_METHOD("get_wall_min_slide_angle"), &CharacterBody3D::get_wall_min_slide_angle); + ClassDB::bind_method(D_METHOD("set_wall_min_slide_angle", "radians"), &CharacterBody3D::set_wall_min_slide_angle); ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody3D::get_up_direction); ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody3D::set_up_direction); + ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody3D::set_motion_mode); + ClassDB::bind_method(D_METHOD("get_motion_mode"), &CharacterBody3D::get_motion_mode); + ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &CharacterBody3D::set_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &CharacterBody3D::get_moving_platform_apply_velocity_on_leave); 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); @@ -1360,21 +1820,49 @@ void CharacterBody3D::_bind_methods() { 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_wall_normal"), &CharacterBody3D::get_wall_normal); + ClassDB::bind_method(D_METHOD("get_last_motion"), &CharacterBody3D::get_last_motion); + ClassDB::bind_method(D_METHOD("get_position_delta"), &CharacterBody3D::get_position_delta); + ClassDB::bind_method(D_METHOD("get_real_velocity"), &CharacterBody3D::get_real_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_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::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::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_linear_velocity", "get_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides"); + ADD_GROUP("Free Mode", "free_mode_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle"); 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::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled"); + 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::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_floor_snap_length", "get_floor_snap_length"); + ADD_GROUP("Moving platform", "moving_platform"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); + + BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED); + BIND_ENUM_CONSTANT(MOTION_MODE_FREE); + + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER); +} + +void CharacterBody3D::_validate_property(PropertyInfo &property) const { + if (motion_mode == MOTION_MODE_FREE) { + if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") { + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + } + } } CharacterBody3D::CharacterBody3D() : @@ -1391,14 +1879,6 @@ CharacterBody3D::~CharacterBody3D() { /////////////////////////////////////// -Vector3 KinematicCollision3D::get_position() const { - return result.collision_point; -} - -Vector3 KinematicCollision3D::get_normal() const { - return result.collision_normal; -} - Vector3 KinematicCollision3D::get_travel() const { return result.travel; } @@ -1407,41 +1887,60 @@ Vector3 KinematicCollision3D::get_remainder() const { return result.remainder; } -real_t KinematicCollision3D::get_angle(const Vector3 &p_up_direction) const { +int KinematicCollision3D::get_collision_count() const { + return result.collision_count; +} + +Vector3 KinematicCollision3D::get_position(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, Vector3()); + return result.collisions[p_collision_index].position; +} + +Vector3 KinematicCollision3D::get_normal(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, Vector3()); + return result.collisions[p_collision_index].normal; +} + +real_t KinematicCollision3D::get_angle(int p_collision_index, const Vector3 &p_up_direction) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, 0.0); ERR_FAIL_COND_V(p_up_direction == Vector3(), 0); - return result.get_angle(p_up_direction); + return result.collisions[p_collision_index].get_angle(p_up_direction); } -Object *KinematicCollision3D::get_local_shape() const { +Object *KinematicCollision3D::get_local_shape(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, nullptr); if (!owner) { return nullptr; } - uint32_t ownerid = owner->shape_find_owner(result.collision_local_shape); + uint32_t ownerid = owner->shape_find_owner(result.collisions[p_collision_index].local_shape); return owner->shape_owner_get_owner(ownerid); } -Object *KinematicCollision3D::get_collider() const { - if (result.collider_id.is_valid()) { - return ObjectDB::get_instance(result.collider_id); +Object *KinematicCollision3D::get_collider(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, nullptr); + if (result.collisions[p_collision_index].collider_id.is_valid()) { + return ObjectDB::get_instance(result.collisions[p_collision_index].collider_id); } return nullptr; } -ObjectID KinematicCollision3D::get_collider_id() const { - return result.collider_id; +ObjectID KinematicCollision3D::get_collider_id(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, ObjectID()); + return result.collisions[p_collision_index].collider_id; } -RID KinematicCollision3D::get_collider_rid() const { - return result.collider; +RID KinematicCollision3D::get_collider_rid(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, RID()); + return result.collisions[p_collision_index].collider; } -Object *KinematicCollision3D::get_collider_shape() const { - Object *collider = get_collider(); +Object *KinematicCollision3D::get_collider_shape(int p_collision_index) const { + Object *collider = get_collider(p_collision_index); if (collider) { CollisionObject3D *obj2d = Object::cast_to<CollisionObject3D>(collider); if (obj2d) { - uint32_t ownerid = obj2d->shape_find_owner(result.collider_shape); + uint32_t ownerid = obj2d->shape_find_owner(result.collisions[p_collision_index].collider_shape); return obj2d->shape_owner_get_owner(ownerid); } } @@ -1449,45 +1948,101 @@ Object *KinematicCollision3D::get_collider_shape() const { return nullptr; } -int KinematicCollision3D::get_collider_shape_index() const { - return result.collider_shape; +int KinematicCollision3D::get_collider_shape_index(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, 0); + return result.collisions[p_collision_index].collider_shape; } -Vector3 KinematicCollision3D::get_collider_velocity() const { - return result.collider_velocity; +Vector3 KinematicCollision3D::get_collider_velocity(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, Vector3()); + return result.collisions[p_collision_index].collider_velocity; } -Variant KinematicCollision3D::get_collider_metadata() const { +Variant KinematicCollision3D::get_collider_metadata(int p_collision_index) const { + ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, Variant()); return Variant(); } +Vector3 KinematicCollision3D::get_best_position() const { + return result.collision_count ? get_position() : Vector3(); +} + +Vector3 KinematicCollision3D::get_best_normal() const { + return result.collision_count ? get_normal() : Vector3(); +} + +Object *KinematicCollision3D::get_best_local_shape() const { + return result.collision_count ? get_local_shape() : nullptr; +} + +Object *KinematicCollision3D::get_best_collider() const { + return result.collision_count ? get_collider() : nullptr; +} + +ObjectID KinematicCollision3D::get_best_collider_id() const { + return result.collision_count ? get_collider_id() : ObjectID(); +} + +RID KinematicCollision3D::get_best_collider_rid() const { + return result.collision_count ? get_collider_rid() : RID(); +} + +Object *KinematicCollision3D::get_best_collider_shape() const { + return result.collision_count ? get_collider_shape() : nullptr; +} + +int KinematicCollision3D::get_best_collider_shape_index() const { + return result.collision_count ? get_collider_shape_index() : 0; +} + +Vector3 KinematicCollision3D::get_best_collider_velocity() const { + return result.collision_count ? get_collider_velocity() : Vector3(); +} + +Variant KinematicCollision3D::get_best_collider_metadata() const { + return result.collision_count ? get_collider_metadata() : Variant(); +} + void KinematicCollision3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_position"), &KinematicCollision3D::get_position); - 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); - ClassDB::bind_method(D_METHOD("get_collider_rid"), &KinematicCollision3D::get_collider_rid); - ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicCollision3D::get_collider_shape); - ClassDB::bind_method(D_METHOD("get_collider_shape_index"), &KinematicCollision3D::get_collider_shape_index); - ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicCollision3D::get_collider_velocity); - ClassDB::bind_method(D_METHOD("get_collider_metadata"), &KinematicCollision3D::get_collider_metadata); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "", "get_position"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "normal"), "", "get_normal"); + ClassDB::bind_method(D_METHOD("get_collision_count"), &KinematicCollision3D::get_collision_count); + ClassDB::bind_method(D_METHOD("get_position", "collision_index"), &KinematicCollision3D::get_position, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_normal", "collision_index"), &KinematicCollision3D::get_normal, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_angle", "collision_index", "up_direction"), &KinematicCollision3D::get_angle, DEFVAL(0), DEFVAL(Vector3(0.0, 1.0, 0.0))); + ClassDB::bind_method(D_METHOD("get_local_shape", "collision_index"), &KinematicCollision3D::get_local_shape, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_collider", "collision_index"), &KinematicCollision3D::get_collider, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_collider_id", "collision_index"), &KinematicCollision3D::get_collider_id, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_collider_rid", "collision_index"), &KinematicCollision3D::get_collider_rid, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_collider_shape", "collision_index"), &KinematicCollision3D::get_collider_shape, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_collider_shape_index", "collision_index"), &KinematicCollision3D::get_collider_shape_index, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_collider_velocity", "collision_index"), &KinematicCollision3D::get_collider_velocity, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_collider_metadata", "collision_index"), &KinematicCollision3D::get_collider_metadata, DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("get_best_position"), &KinematicCollision3D::get_best_position); + ClassDB::bind_method(D_METHOD("get_best_normal"), &KinematicCollision3D::get_best_normal); + ClassDB::bind_method(D_METHOD("get_best_local_shape"), &KinematicCollision3D::get_best_local_shape); + ClassDB::bind_method(D_METHOD("get_best_collider"), &KinematicCollision3D::get_best_collider); + ClassDB::bind_method(D_METHOD("get_best_collider_id"), &KinematicCollision3D::get_best_collider_id); + ClassDB::bind_method(D_METHOD("get_best_collider_rid"), &KinematicCollision3D::get_best_collider_rid); + ClassDB::bind_method(D_METHOD("get_best_collider_shape"), &KinematicCollision3D::get_best_collider_shape); + ClassDB::bind_method(D_METHOD("get_best_collider_shape_index"), &KinematicCollision3D::get_best_collider_shape_index); + ClassDB::bind_method(D_METHOD("get_best_collider_velocity"), &KinematicCollision3D::get_best_collider_velocity); + ClassDB::bind_method(D_METHOD("get_best_collider_metadata"), &KinematicCollision3D::get_best_collider_metadata); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "travel"), "", "get_travel"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "remainder"), "", "get_remainder"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_local_shape"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_collider"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_collider_id"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_collider_rid"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_collider_shape"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_collider_shape_index"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collider_velocity"), "", "get_collider_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::NIL, "collider_metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "", "get_collider_metadata"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_count"), "", "get_collision_count"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "", "get_best_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "normal"), "", "get_best_normal"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_best_local_shape"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_best_collider"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_best_collider_id"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_best_collider_rid"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_best_collider_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_best_collider_shape_index"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collider_velocity"), "", "get_best_collider_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::NIL, "collider_metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "", "get_best_collider_metadata"); } /////////////////////////////////////// diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 8e6463f838..a53147cb8f 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -50,11 +50,11 @@ protected: uint16_t locked_axis = 0; - Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001); + Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1); 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, 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); + bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, int p_max_collisions = 1, 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, int p_max_collisions = 1); void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const; @@ -105,7 +105,7 @@ private: Vector3 linear_velocity; Vector3 angular_velocity; - bool sync_to_physics = false; + bool sync_to_physics = true; Transform3D last_valid_transform; @@ -129,15 +129,13 @@ private: bool is_sync_to_physics_enabled() const; }; -class RigidBody3D : public PhysicsBody3D { - GDCLASS(RigidBody3D, PhysicsBody3D); +class RigidDynamicBody3D : public PhysicsBody3D { + GDCLASS(RigidDynamicBody3D, PhysicsBody3D); public: - enum Mode { - MODE_DYNAMIC, - MODE_STATIC, - MODE_DYNAMIC_LOCKED, - MODE_KINEMATIC, + enum FreezeMode { + FREEZE_MODE_STATIC, + FREEZE_MODE_KINEMATIC, }; enum CenterOfMassMode { @@ -145,11 +143,11 @@ public: CENTER_OF_MASS_MODE_CUSTOM, }; - GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) - -protected: +private: bool can_sleep = true; - Mode mode = MODE_DYNAMIC; + bool lock_rotation = false; + bool freeze = false; + FreezeMode freeze_mode = FREEZE_MODE_STATIC; real_t mass = 1.0; Vector3 inertia; @@ -191,7 +189,7 @@ protected: tagged = false; } }; - struct RigidBody3D_RemoveAction { + struct RigidDynamicBody3D_RemoveAction { RID rid; ObjectID body_id; ShapePair pair; @@ -214,16 +212,28 @@ protected: void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); - virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); +protected: void _notification(int p_what); static void _bind_methods(); virtual void _validate_property(PropertyInfo &property) const override; + GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) + + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); + + void _apply_body_mode(); + public: - void set_mode(Mode p_mode); - Mode get_mode() const; + void set_lock_rotation_enabled(bool p_lock_rotation); + bool is_lock_rotation_enabled() const; + + void set_freeze_enabled(bool p_freeze); + bool is_freeze_enabled() const; + + void set_freeze_mode(FreezeMode p_freeze_mode); + FreezeMode get_freeze_mode() const; void set_mass(real_t p_mass); real_t get_mass() const; @@ -291,91 +301,163 @@ public: virtual TypedArray<String> get_configuration_warnings() const override; - RigidBody3D(); - ~RigidBody3D(); + RigidDynamicBody3D(); + ~RigidDynamicBody3D(); private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidBody3D::Mode); -VARIANT_ENUM_CAST(RigidBody3D::CenterOfMassMode); +VARIANT_ENUM_CAST(RigidDynamicBody3D::FreezeMode); +VARIANT_ENUM_CAST(RigidDynamicBody3D::CenterOfMassMode); class KinematicCollision3D; class CharacterBody3D : public PhysicsBody3D { GDCLASS(CharacterBody3D, PhysicsBody3D); +public: + enum MotionMode { + MOTION_MODE_GROUNDED, + MOTION_MODE_FREE, + }; + enum MovingPlatformApplyVelocityOnLeave { + PLATFORM_VEL_ON_LEAVE_ALWAYS, + PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY, + PLATFORM_VEL_ON_LEAVE_NEVER, + }; + 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_last_motion() const; + Vector3 get_position_delta() const; + Vector3 get_floor_normal() const; + Vector3 get_wall_normal() const; + Vector3 get_real_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_collision_count() const; + PhysicsServer3D::MotionResult get_slide_collision(int p_bounce) const; + + CharacterBody3D(); + ~CharacterBody3D(); + private: real_t margin = 0.001; + MotionMode motion_mode = MOTION_MODE_GROUNDED; + MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS; + union CollisionState { + uint32_t state = 0; + struct { + bool floor; + bool wall; + bool ceiling; + }; + + CollisionState() { + } + + CollisionState(bool p_floor, bool p_wall, bool p_ceiling) { + floor = p_floor; + wall = p_wall; + ceiling = p_ceiling; + } + }; - bool floor_stop_on_slope = false; - int max_slides = 4; + CollisionState collision_state; + bool floor_constant_speed = false; + bool floor_stop_on_slope = true; + bool floor_block_on_wall = true; + bool slide_on_ceiling = true; + int max_slides = 6; + int platform_layer; + RID platform_rid; + uint32_t moving_platform_floor_layers = UINT32_MAX; + uint32_t moving_platform_wall_layers = 0; + real_t floor_snap_length = 0.1; real_t floor_max_angle = Math::deg2rad((real_t)45.0); - Vector3 snap; + real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0); Vector3 up_direction = Vector3(0.0, 1.0, 0.0); - Vector3 linear_velocity; - Vector3 floor_normal; - Vector3 floor_velocity; - RID on_floor_body; - bool on_floor = false; - bool on_ceiling = false; - bool on_wall = false; + Vector3 wall_normal; + Vector3 last_motion; + Vector3 platform_velocity; + Vector3 previous_position; + Vector3 real_velocity; + Vector<PhysicsServer3D::MotionResult> motion_results; 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_floor_stop_on_slope_enabled() const; void set_floor_stop_on_slope_enabled(bool p_enabled); + bool is_floor_constant_speed_enabled() const; + void set_floor_constant_speed_enabled(bool p_enabled); + + bool is_floor_block_on_wall_enabled() const; + void set_floor_block_on_wall_enabled(bool p_enabled); + + bool is_slide_on_ceiling_enabled() const; + void set_slide_on_ceiling_enabled(bool p_enabled); + int get_max_slides() const; void set_max_slides(int p_max_slides); real_t get_floor_max_angle() const; void set_floor_max_angle(real_t p_radians); - const Vector3 &get_snap() const; - void set_snap(const Vector3 &p_snap); + real_t get_floor_snap_length(); + void set_floor_snap_length(real_t p_floor_snap_length); - const Vector3 &get_up_direction() const; - void set_up_direction(const Vector3 &p_up_direction); + real_t get_wall_min_slide_angle() const; + void set_wall_min_slide_angle(real_t p_radians); -protected: - void _notification(int p_what); - static void _bind_methods(); + uint32_t get_moving_platform_floor_layers() const; + void set_moving_platform_floor_layers(const uint32_t p_exclude_layer); -public: - bool move_and_slide(); + uint32_t get_moving_platform_wall_layers() const; + void set_moving_platform_wall_layers(const uint32_t p_exclude_layer); - virtual Vector3 get_linear_velocity() const override; - void set_linear_velocity(const Vector3 &p_velocity); + void set_motion_mode(MotionMode p_mode); + MotionMode get_motion_mode() const; - 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; - real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; - Vector3 get_platform_velocity() const; + void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity); + MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const; - int get_slide_collision_count() const; - PhysicsServer3D::MotionResult get_slide_collision(int p_bounce) const; + void _move_and_slide_free(double p_delta); + void _move_and_slide_grounded(double p_delta, bool p_was_on_floor); - CharacterBody3D(); - ~CharacterBody3D(); + Ref<KinematicCollision3D> _get_slide_collision(int p_bounce); + Ref<KinematicCollision3D> _get_last_slide_collision(); + const Vector3 &get_up_direction() const; + bool _on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up); + void set_up_direction(const Vector3 &p_up_direction); + void _set_collision_direction(const PhysicsServer3D::MotionResult &p_result, CollisionState &r_state, CollisionState p_apply_state = CollisionState(true, true, true)); + void _set_platform_data(const PhysicsServer3D::MotionCollision &p_collision); + void _snap_on_floor(bool was_on_floor, bool vel_dir_facing_up); + +protected: + void _notification(int p_what); + static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; }; +VARIANT_ENUM_CAST(CharacterBody3D::MotionMode); +VARIANT_ENUM_CAST(CharacterBody3D::MovingPlatformApplyVelocityOnLeave); + class KinematicCollision3D : public RefCounted { GDCLASS(KinematicCollision3D, RefCounted); @@ -388,19 +470,31 @@ protected: static void _bind_methods(); public: - Vector3 get_position() const; - 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; - RID get_collider_rid() const; - Object *get_collider_shape() const; - int get_collider_shape_index() const; - Vector3 get_collider_velocity() const; - Variant get_collider_metadata() const; + int get_collision_count() const; + Vector3 get_position(int p_collision_index = 0) const; + Vector3 get_normal(int p_collision_index = 0) const; + real_t get_angle(int p_collision_index = 0, const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; + Object *get_local_shape(int p_collision_index = 0) const; + Object *get_collider(int p_collision_index = 0) const; + ObjectID get_collider_id(int p_collision_index = 0) const; + RID get_collider_rid(int p_collision_index = 0) const; + Object *get_collider_shape(int p_collision_index = 0) const; + int get_collider_shape_index(int p_collision_index = 0) const; + Vector3 get_collider_velocity(int p_collision_index = 0) const; + Variant get_collider_metadata(int p_collision_index = 0) const; + + Vector3 get_best_position() const; + Vector3 get_best_normal() const; + Object *get_best_local_shape() const; + Object *get_best_collider() const; + ObjectID get_best_collider_id() const; + RID get_best_collider_rid() const; + Object *get_best_collider_shape() const; + int get_best_collider_shape_index() const; + Vector3 get_best_collider_velocity() const; + Variant get_best_collider_metadata() const; }; class PhysicalBone3D : public PhysicsBody3D { diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 0f5de621ea..b7a79a2645 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -520,6 +520,7 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); + ERR_FAIL_COND(p_bone == p_parent); bones.write[p_bone].parent = p_parent; process_order_dirty = true; diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 466f67afb8..8d90aabfac 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -99,7 +99,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain child_ci->current_pos = child_ci->initial_transform.origin; if (child_ci->parent_item) { - child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + child_ci->length = child_ci->parent_item->current_pos.distance_to(child_ci->current_pos); } } @@ -140,7 +140,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vec solve_simple_backwards(p_task->chain, p_solve_magnet); solve_simple_forwards(p_task->chain, p_solve_magnet, p_origin_pos); - distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + distance_to_goal = p_task->chain.tips[0].end_effector->goal_transform.origin.distance_to(p_task->chain.tips[0].chain_item->current_pos); } } diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp index 7eb189e890..a886c61263 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_dynamic_body_3d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* soft_body_3d.cpp */ +/* soft_dynamic_body_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "soft_body_3d.h" +#include "soft_dynamic_body_3d.h" #include "scene/3d/physics_body_3d.h" -SoftBodyRenderingServerHandler::SoftBodyRenderingServerHandler() {} +SoftDynamicBodyRenderingServerHandler::SoftDynamicBodyRenderingServerHandler() {} -void SoftBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) { +void SoftDynamicBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) { clear(); ERR_FAIL_COND(!p_mesh.is_valid()); @@ -56,7 +56,7 @@ void SoftBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) { offset_normal = surface_offsets[RS::ARRAY_NORMAL]; } -void SoftBodyRenderingServerHandler::clear() { +void SoftDynamicBodyRenderingServerHandler::clear() { buffer.resize(0); stride = 0; offset_vertices = 0; @@ -66,41 +66,41 @@ void SoftBodyRenderingServerHandler::clear() { mesh = RID(); } -void SoftBodyRenderingServerHandler::open() { +void SoftDynamicBodyRenderingServerHandler::open() { write_buffer = buffer.ptrw(); } -void SoftBodyRenderingServerHandler::close() { +void SoftDynamicBodyRenderingServerHandler::close() { write_buffer = nullptr; } -void SoftBodyRenderingServerHandler::commit_changes() { +void SoftDynamicBodyRenderingServerHandler::commit_changes() { RS::get_singleton()->mesh_surface_update_vertex_region(mesh, surface, 0, buffer); } -void SoftBodyRenderingServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { +void SoftDynamicBodyRenderingServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { memcpy(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); } -void SoftBodyRenderingServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { +void SoftDynamicBodyRenderingServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { memcpy(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); } -void SoftBodyRenderingServerHandler::set_aabb(const AABB &p_aabb) { +void SoftDynamicBodyRenderingServerHandler::set_aabb(const AABB &p_aabb) { RS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb); } -SoftBody3D::PinnedPoint::PinnedPoint() { +SoftDynamicBody3D::PinnedPoint::PinnedPoint() { } -SoftBody3D::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { +SoftDynamicBody3D::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { point_index = obj_tocopy.point_index; spatial_attachment_path = obj_tocopy.spatial_attachment_path; spatial_attachment = obj_tocopy.spatial_attachment; offset = obj_tocopy.offset; } -SoftBody3D::PinnedPoint &SoftBody3D::PinnedPoint::operator=(const PinnedPoint &obj) { +SoftDynamicBody3D::PinnedPoint &SoftDynamicBody3D::PinnedPoint::operator=(const PinnedPoint &obj) { point_index = obj.point_index; spatial_attachment_path = obj.spatial_attachment_path; spatial_attachment = obj.spatial_attachment; @@ -108,7 +108,7 @@ SoftBody3D::PinnedPoint &SoftBody3D::PinnedPoint::operator=(const PinnedPoint &o return *this; } -void SoftBody3D::_update_pickable() { +void SoftDynamicBody3D::_update_pickable() { if (!is_inside_tree()) { return; } @@ -116,7 +116,7 @@ void SoftBody3D::_update_pickable() { PhysicsServer3D::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable); } -bool SoftBody3D::_set(const StringName &p_name, const Variant &p_value) { +bool SoftDynamicBody3D::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; String which = name.get_slicec('/', 0); @@ -133,7 +133,7 @@ bool SoftBody3D::_set(const StringName &p_name, const Variant &p_value) { return false; } -bool SoftBody3D::_get(const StringName &p_name, Variant &r_ret) const { +bool SoftDynamicBody3D::_get(const StringName &p_name, Variant &r_ret) const { String name = p_name; String which = name.get_slicec('/', 0); @@ -160,7 +160,7 @@ bool SoftBody3D::_get(const StringName &p_name, Variant &r_ret) const { return false; } -void SoftBody3D::_get_property_list(List<PropertyInfo> *p_list) const { +void SoftDynamicBody3D::_get_property_list(List<PropertyInfo> *p_list) const { const int pinned_points_indices_size = pinned_points.size(); p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "pinned_points")); @@ -172,7 +172,7 @@ void SoftBody3D::_get_property_list(List<PropertyInfo> *p_list) const { } } -bool SoftBody3D::_set_property_pinned_points_indices(const Array &p_indices) { +bool SoftDynamicBody3D::_set_property_pinned_points_indices(const Array &p_indices) { const int p_indices_size = p_indices.size(); { // Remove the pined points on physics server that will be removed by resize @@ -201,7 +201,7 @@ bool SoftBody3D::_set_property_pinned_points_indices(const Array &p_indices) { return true; } -bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { +bool SoftDynamicBody3D::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { if (pinned_points.size() <= p_item) { return false; } @@ -220,7 +220,7 @@ bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String return true; } -bool SoftBody3D::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { +bool SoftDynamicBody3D::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { if (pinned_points.size() <= p_item) { return false; } @@ -239,15 +239,7 @@ bool SoftBody3D::_get_property_pinned_points(int p_item, const String &p_what, V return true; } -void SoftBody3D::_softbody_changed() { - prepare_physics_server(); - _reset_points_offsets(); -#ifdef TOOLS_ENABLED - update_configuration_warnings(); -#endif -} - -void SoftBody3D::_notification(int p_what) { +void SoftDynamicBody3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { if (Engine::get_singleton()->is_editor_hint()) { @@ -312,56 +304,56 @@ void SoftBody3D::_notification(int p_what) { } } -void SoftBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_physics_rid"), &SoftBody3D::get_physics_rid); +void SoftDynamicBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_physics_rid"), &SoftDynamicBody3D::get_physics_rid); - ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody3D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody3D::get_collision_mask); + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftDynamicBody3D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftDynamicBody3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody3D::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody3D::get_collision_layer); + ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftDynamicBody3D::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftDynamicBody3D::get_collision_layer); - ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &SoftBody3D::set_collision_mask_value); - ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &SoftBody3D::get_collision_mask_value); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &SoftDynamicBody3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &SoftDynamicBody3D::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &SoftBody3D::set_collision_layer_value); - ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &SoftBody3D::get_collision_layer_value); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &SoftDynamicBody3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &SoftDynamicBody3D::get_collision_layer_value); - ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody3D::set_parent_collision_ignore); - ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody3D::get_parent_collision_ignore); + ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftDynamicBody3D::set_parent_collision_ignore); + ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftDynamicBody3D::get_parent_collision_ignore); - ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &SoftBody3D::set_disable_mode); - ClassDB::bind_method(D_METHOD("get_disable_mode"), &SoftBody3D::get_disable_mode); + ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &SoftDynamicBody3D::set_disable_mode); + ClassDB::bind_method(D_METHOD("get_disable_mode"), &SoftDynamicBody3D::get_disable_mode); - ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &SoftBody3D::get_collision_exceptions); - ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody3D::add_collision_exception_with); - ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody3D::remove_collision_exception_with); + ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &SoftDynamicBody3D::get_collision_exceptions); + ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftDynamicBody3D::add_collision_exception_with); + ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftDynamicBody3D::remove_collision_exception_with); - ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody3D::set_simulation_precision); - ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody3D::get_simulation_precision); + ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftDynamicBody3D::set_simulation_precision); + ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftDynamicBody3D::get_simulation_precision); - ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody3D::set_total_mass); - ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody3D::get_total_mass); + ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftDynamicBody3D::set_total_mass); + ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftDynamicBody3D::get_total_mass); - ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody3D::set_linear_stiffness); - ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody3D::get_linear_stiffness); + ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftDynamicBody3D::set_linear_stiffness); + ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftDynamicBody3D::get_linear_stiffness); - ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody3D::set_pressure_coefficient); - ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody3D::get_pressure_coefficient); + ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftDynamicBody3D::set_pressure_coefficient); + ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftDynamicBody3D::get_pressure_coefficient); - ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody3D::set_damping_coefficient); - ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody3D::get_damping_coefficient); + ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftDynamicBody3D::set_damping_coefficient); + ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftDynamicBody3D::get_damping_coefficient); - 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("set_drag_coefficient", "drag_coefficient"), &SoftDynamicBody3D::set_drag_coefficient); + ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftDynamicBody3D::get_drag_coefficient); - ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform); + ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftDynamicBody3D::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_point_pinned", "point_index", "pinned", "attachment_path"), &SoftDynamicBody3D::pin_point, DEFVAL(NodePath())); + ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftDynamicBody3D::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); + ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftDynamicBody3D::set_ray_pickable); + ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftDynamicBody3D::is_ray_pickable); ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); @@ -383,7 +375,7 @@ void SoftBody3D::_bind_methods() { BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE); } -TypedArray<String> SoftBody3D::get_configuration_warnings() const { +TypedArray<String> SoftDynamicBody3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (get_mesh().is_null()) { @@ -392,13 +384,13 @@ TypedArray<String> SoftBody3D::get_configuration_warnings() const { Transform3D t = get_transform(); if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to SoftBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + warnings.push_back(TTR("Size changes to SoftDynamicBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } -void SoftBody3D::_update_physics_server() { +void SoftDynamicBody3D::_update_physics_server() { if (!simulation_started) { return; } @@ -414,7 +406,7 @@ void SoftBody3D::_update_physics_server() { } } -void SoftBody3D::_draw_soft_mesh() { +void SoftDynamicBody3D::_draw_soft_mesh() { if (get_mesh().is_null()) { return; } @@ -437,7 +429,7 @@ void SoftBody3D::_draw_soft_mesh() { rendering_server_handler.commit_changes(); } -void SoftBody3D::prepare_physics_server() { +void SoftDynamicBody3D::prepare_physics_server() { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { if (get_mesh().is_valid()) { @@ -453,16 +445,16 @@ void SoftBody3D::prepare_physics_server() { if (get_mesh().is_valid() && (is_enabled() || (disable_mode != DISABLE_MODE_REMOVE))) { become_mesh_owner(); PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); - RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh)); + RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh)); } else { PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, nullptr); - if (RS::get_singleton()->is_connected("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh))) { - RS::get_singleton()->disconnect("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh)); + if (RS::get_singleton()->is_connected("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh))) { + RS::get_singleton()->disconnect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh)); } } } -void SoftBody3D::become_mesh_owner() { +void SoftDynamicBody3D::become_mesh_owner() { if (mesh.is_null()) { return; } @@ -475,7 +467,7 @@ void SoftBody3D::become_mesh_owner() { ERR_FAIL_COND(!mesh->get_surface_count()); - // Get current mesh array and create new mesh array with necessary flag for softbody + // Get current mesh array and create new mesh array with necessary flag for SoftDynamicBody Array surface_arrays = mesh->surface_get_arrays(0); Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0); Dictionary surface_lods = mesh->surface_get_lods(0); @@ -496,25 +488,25 @@ void SoftBody3D::become_mesh_owner() { } } -void SoftBody3D::set_collision_mask(uint32_t p_mask) { +void SoftDynamicBody3D::set_collision_mask(uint32_t p_mask) { collision_mask = p_mask; PhysicsServer3D::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask); } -uint32_t SoftBody3D::get_collision_mask() const { +uint32_t SoftDynamicBody3D::get_collision_mask() const { return collision_mask; } -void SoftBody3D::set_collision_layer(uint32_t p_layer) { +void SoftDynamicBody3D::set_collision_layer(uint32_t p_layer) { collision_layer = p_layer; PhysicsServer3D::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer); } -uint32_t SoftBody3D::get_collision_layer() const { +uint32_t SoftDynamicBody3D::get_collision_layer() const { return collision_layer; } -void SoftBody3D::set_collision_layer_value(int p_layer_number, bool p_value) { +void SoftDynamicBody3D::set_collision_layer_value(int p_layer_number, bool p_value) { ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t collision_layer = get_collision_layer(); @@ -526,13 +518,13 @@ void SoftBody3D::set_collision_layer_value(int p_layer_number, bool p_value) { set_collision_layer(collision_layer); } -bool SoftBody3D::get_collision_layer_value(int p_layer_number) const { +bool SoftDynamicBody3D::get_collision_layer_value(int p_layer_number) const { ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); return get_collision_layer() & (1 << (p_layer_number - 1)); } -void SoftBody3D::set_collision_mask_value(int p_layer_number, bool p_value) { +void SoftDynamicBody3D::set_collision_mask_value(int p_layer_number, bool p_value) { ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); @@ -544,13 +536,13 @@ void SoftBody3D::set_collision_mask_value(int p_layer_number, bool p_value) { set_collision_mask(mask); } -bool SoftBody3D::get_collision_mask_value(int p_layer_number) const { +bool SoftDynamicBody3D::get_collision_mask_value(int p_layer_number) const { ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); return get_collision_mask() & (1 << (p_layer_number - 1)); } -void SoftBody3D::set_disable_mode(DisableMode p_mode) { +void SoftDynamicBody3D::set_disable_mode(DisableMode p_mode) { if (disable_mode == p_mode) { return; } @@ -568,30 +560,30 @@ void SoftBody3D::set_disable_mode(DisableMode p_mode) { } } -SoftBody3D::DisableMode SoftBody3D::get_disable_mode() const { +SoftDynamicBody3D::DisableMode SoftDynamicBody3D::get_disable_mode() const { return disable_mode; } -void SoftBody3D::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { +void SoftDynamicBody3D::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { parent_collision_ignore = p_parent_collision_ignore; } -const NodePath &SoftBody3D::get_parent_collision_ignore() const { +const NodePath &SoftDynamicBody3D::get_parent_collision_ignore() const { return parent_collision_ignore; } -void SoftBody3D::set_pinned_points_indices(Vector<SoftBody3D::PinnedPoint> p_pinned_points_indices) { +void SoftDynamicBody3D::set_pinned_points_indices(Vector<SoftDynamicBody3D::PinnedPoint> p_pinned_points_indices) { pinned_points = p_pinned_points_indices; for (int i = pinned_points.size() - 1; 0 <= i; --i) { pin_point(p_pinned_points_indices[i].point_index, true); } } -Vector<SoftBody3D::PinnedPoint> SoftBody3D::get_pinned_points_indices() { +Vector<SoftDynamicBody3D::PinnedPoint> SoftDynamicBody3D::get_pinned_points_indices() { return pinned_points; } -Array SoftBody3D::get_collision_exceptions() { +Array SoftDynamicBody3D::get_collision_exceptions() { List<RID> exceptions; PhysicsServer3D::get_singleton()->soft_body_get_collision_exceptions(physics_rid, &exceptions); Array ret; @@ -604,77 +596,77 @@ Array SoftBody3D::get_collision_exceptions() { return ret; } -void SoftBody3D::add_collision_exception_with(Node *p_node) { +void SoftDynamicBody3D::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node); ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two CollisionObject3Ds."); PhysicsServer3D::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid()); } -void SoftBody3D::remove_collision_exception_with(Node *p_node) { +void SoftDynamicBody3D::remove_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node); ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two CollisionObject3Ds."); PhysicsServer3D::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid()); } -int SoftBody3D::get_simulation_precision() { +int SoftDynamicBody3D::get_simulation_precision() { return PhysicsServer3D::get_singleton()->soft_body_get_simulation_precision(physics_rid); } -void SoftBody3D::set_simulation_precision(int p_simulation_precision) { +void SoftDynamicBody3D::set_simulation_precision(int p_simulation_precision) { PhysicsServer3D::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision); } -real_t SoftBody3D::get_total_mass() { +real_t SoftDynamicBody3D::get_total_mass() { return PhysicsServer3D::get_singleton()->soft_body_get_total_mass(physics_rid); } -void SoftBody3D::set_total_mass(real_t p_total_mass) { +void SoftDynamicBody3D::set_total_mass(real_t p_total_mass) { PhysicsServer3D::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass); } -void SoftBody3D::set_linear_stiffness(real_t p_linear_stiffness) { +void SoftDynamicBody3D::set_linear_stiffness(real_t p_linear_stiffness) { PhysicsServer3D::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness); } -real_t SoftBody3D::get_linear_stiffness() { +real_t SoftDynamicBody3D::get_linear_stiffness() { return PhysicsServer3D::get_singleton()->soft_body_get_linear_stiffness(physics_rid); } -real_t SoftBody3D::get_pressure_coefficient() { +real_t SoftDynamicBody3D::get_pressure_coefficient() { return PhysicsServer3D::get_singleton()->soft_body_get_pressure_coefficient(physics_rid); } -void SoftBody3D::set_pressure_coefficient(real_t p_pressure_coefficient) { +void SoftDynamicBody3D::set_pressure_coefficient(real_t p_pressure_coefficient) { PhysicsServer3D::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient); } -real_t SoftBody3D::get_damping_coefficient() { +real_t SoftDynamicBody3D::get_damping_coefficient() { return PhysicsServer3D::get_singleton()->soft_body_get_damping_coefficient(physics_rid); } -void SoftBody3D::set_damping_coefficient(real_t p_damping_coefficient) { +void SoftDynamicBody3D::set_damping_coefficient(real_t p_damping_coefficient) { PhysicsServer3D::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient); } -real_t SoftBody3D::get_drag_coefficient() { +real_t SoftDynamicBody3D::get_drag_coefficient() { return PhysicsServer3D::get_singleton()->soft_body_get_drag_coefficient(physics_rid); } -void SoftBody3D::set_drag_coefficient(real_t p_drag_coefficient) { +void SoftDynamicBody3D::set_drag_coefficient(real_t p_drag_coefficient) { PhysicsServer3D::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient); } -Vector3 SoftBody3D::get_point_transform(int p_point_index) { +Vector3 SoftDynamicBody3D::get_point_transform(int p_point_index) { return PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index); } -void SoftBody3D::pin_point_toggle(int p_point_index) { +void SoftDynamicBody3D::pin_point_toggle(int p_point_index) { pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index))); } -void SoftBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { +void SoftDynamicBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { _pin_point_on_physics_server(p_point_index, pin); if (pin) { _add_pinned_point(p_point_index, p_spatial_attachment_path); @@ -683,41 +675,33 @@ void SoftBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatia } } -bool SoftBody3D::is_point_pinned(int p_point_index) const { +bool SoftDynamicBody3D::is_point_pinned(int p_point_index) const { return -1 != _has_pinned_point(p_point_index); } -void SoftBody3D::set_ray_pickable(bool p_ray_pickable) { +void SoftDynamicBody3D::set_ray_pickable(bool p_ray_pickable) { ray_pickable = p_ray_pickable; _update_pickable(); } -bool SoftBody3D::is_ray_pickable() const { +bool SoftDynamicBody3D::is_ray_pickable() const { return ray_pickable; } -SoftBody3D::SoftBody3D() : +SoftDynamicBody3D::SoftDynamicBody3D() : physics_rid(PhysicsServer3D::get_singleton()->soft_body_create()) { PhysicsServer3D::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id()); } -SoftBody3D::~SoftBody3D() { +SoftDynamicBody3D::~SoftDynamicBody3D() { PhysicsServer3D::get_singleton()->free(physics_rid); } -void SoftBody3D::reset_softbody_pin() { - PhysicsServer3D::get_singleton()->soft_body_remove_all_pinned_points(physics_rid); - const PinnedPoint *pps = pinned_points.ptr(); - for (int i = pinned_points.size() - 1; 0 < i; --i) { - PhysicsServer3D::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true); - } -} - -void SoftBody3D::_make_cache_dirty() { +void SoftDynamicBody3D::_make_cache_dirty() { pinned_points_cache_dirty = true; } -void SoftBody3D::_update_cache_pin_points_datas() { +void SoftDynamicBody3D::_update_cache_pin_points_datas() { if (!pinned_points_cache_dirty) { return; } @@ -730,17 +714,17 @@ void SoftBody3D::_update_cache_pin_points_datas() { w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(w[i].spatial_attachment_path)); } if (!w[i].spatial_attachment) { - ERR_PRINT("Node3D node not defined in the pinned point, this is undefined behavior for SoftBody3D!"); + ERR_PRINT("Node3D node not defined in the pinned point, this is undefined behavior for SoftDynamicBody3D!"); } } } -void SoftBody3D::_pin_point_on_physics_server(int p_point_index, bool pin) { +void SoftDynamicBody3D::_pin_point_on_physics_server(int p_point_index, bool pin) { PhysicsServer3D::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin); } -void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { - SoftBody3D::PinnedPoint *pinned_point; +void SoftDynamicBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { + SoftDynamicBody3D::PinnedPoint *pinned_point; if (-1 == _get_pinned_point(p_point_index, pinned_point)) { // Create new PinnedPoint pp; @@ -765,7 +749,7 @@ void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_ } } -void SoftBody3D::_reset_points_offsets() { +void SoftDynamicBody3D::_reset_points_offsets() { if (!Engine::get_singleton()->is_editor_hint()) { return; } @@ -787,25 +771,25 @@ void SoftBody3D::_reset_points_offsets() { } } -void SoftBody3D::_remove_pinned_point(int p_point_index) { +void SoftDynamicBody3D::_remove_pinned_point(int p_point_index) { const int id(_has_pinned_point(p_point_index)); if (-1 != id) { pinned_points.remove(id); } } -int SoftBody3D::_get_pinned_point(int p_point_index, SoftBody3D::PinnedPoint *&r_point) const { +int SoftDynamicBody3D::_get_pinned_point(int p_point_index, SoftDynamicBody3D::PinnedPoint *&r_point) const { const int id = _has_pinned_point(p_point_index); if (-1 == id) { r_point = nullptr; return -1; } else { - r_point = const_cast<SoftBody3D::PinnedPoint *>(&pinned_points.ptr()[id]); + r_point = const_cast<SoftDynamicBody3D::PinnedPoint *>(&pinned_points.ptr()[id]); return id; } } -int SoftBody3D::_has_pinned_point(int p_point_index) const { +int SoftDynamicBody3D::_has_pinned_point(int p_point_index) const { const PinnedPoint *r = pinned_points.ptr(); for (int i = pinned_points.size() - 1; 0 <= i; --i) { if (p_point_index == r[i].point_index) { diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_dynamic_body_3d.h index 46b185a32c..0b4b3021cd 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_dynamic_body_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* soft_body_3d.h */ +/* soft_dynamic_body_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,16 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SOFT_PHYSICS_BODY_H -#define SOFT_PHYSICS_BODY_H +#ifndef SOFT_DYNAMIC_BODY_H +#define SOFT_DYNAMIC_BODY_H #include "scene/3d/mesh_instance_3d.h" #include "servers/physics_server_3d.h" -class SoftBody3D; +class SoftDynamicBody3D; -class SoftBodyRenderingServerHandler : public RenderingServerHandler { - friend class SoftBody3D; +class SoftDynamicBodyRenderingServerHandler : public RenderingServerHandler { + friend class SoftDynamicBody3D; RID mesh; int surface = 0; @@ -49,7 +49,7 @@ class SoftBodyRenderingServerHandler : public RenderingServerHandler { uint8_t *write_buffer = nullptr; private: - SoftBodyRenderingServerHandler(); + SoftDynamicBodyRenderingServerHandler(); bool is_ready() { return mesh.is_valid(); } void prepare(RID p_mesh_rid, int p_surface); void clear(); @@ -63,8 +63,8 @@ public: void set_aabb(const AABB &p_aabb) override; }; -class SoftBody3D : public MeshInstance3D { - GDCLASS(SoftBody3D, MeshInstance3D); +class SoftDynamicBody3D : public MeshInstance3D { + GDCLASS(SoftDynamicBody3D, MeshInstance3D); public: enum DisableMode { @@ -84,7 +84,7 @@ public: }; private: - SoftBodyRenderingServerHandler rendering_server_handler; + SoftDynamicBodyRenderingServerHandler rendering_server_handler; RID physics_rid; @@ -106,8 +106,6 @@ private: void _update_pickable(); - void _softbody_changed(); - protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -184,12 +182,10 @@ public: void set_ray_pickable(bool p_ray_pickable); bool is_ray_pickable() const; - SoftBody3D(); - ~SoftBody3D(); + SoftDynamicBody3D(); + ~SoftDynamicBody3D(); private: - void reset_softbody_pin(); - void _make_cache_dirty(); void _update_cache_pin_points_datas(); @@ -202,6 +198,6 @@ private: int _has_pinned_point(int p_point_index) const; }; -VARIANT_ENUM_CAST(SoftBody3D::DisableMode); +VARIANT_ENUM_CAST(SoftDynamicBody3D::DisableMode); -#endif // SOFT_PHYSICS_BODY_H +#endif // SOFT_DYNAMIC_BODY_H diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index daeea81891..9a2aaa8be2 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -470,7 +470,7 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) { } void VehicleBody3D::_update_suspension(PhysicsDirectBodyState3D *s) { - real_t chassisMass = mass; + real_t chassisMass = get_mass(); for (int w_it = 0; w_it < wheels.size(); w_it++) { VehicleWheel3D &wheel_info = *wheels[w_it]; @@ -558,7 +558,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const rel_pos2, normal, s->get_inverse_inertia_tensor().get_main_diagonal(), - 1.0 / mass, + 1.0 / get_mass(), b2invinertia, b2invmass); @@ -584,7 +584,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const #define ONLY_USE_LINEAR_MASS #ifdef ONLY_USE_LINEAR_MASS - real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass); + real_t massTerm = real_t(1.) / ((1.0 / get_mass()) + b2invmass); impulse = -contactDamping * rel_vel * massTerm; #else real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv; @@ -803,7 +803,7 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { } void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { - RigidBody3D::_body_state_changed(p_state); + RigidDynamicBody3D::_body_state_changed(p_state); real_t step = p_state->get_step(); diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index f29c3d89b7..a798c76c1f 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -150,8 +150,8 @@ public: VehicleWheel3D(); }; -class VehicleBody3D : public RigidBody3D { - GDCLASS(VehicleBody3D, RigidBody3D); +class VehicleBody3D : public RigidDynamicBody3D { + GDCLASS(VehicleBody3D, RigidDynamicBody3D); real_t engine_force = 0.0; real_t brake = 0.0; diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 2d32379d69..04f371f4b2 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -173,7 +173,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co //could not in any way get texture information.. so use closest point to center Face3 f(p_vtx[0], p_vtx[1], p_vtx[2]); - Vector3 inters = f.get_closest_point_to(p_aabb.position + p_aabb.size * 0.5); + Vector3 inters = f.get_closest_point_to(p_aabb.get_center()); Vector3 lnormal; Vector2 uv; @@ -434,7 +434,7 @@ void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const V } //test against original bounds - if (!Geometry3D::triangle_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs)) { + if (!Geometry3D::triangle_box_overlap(original_bounds.get_center(), original_bounds.size * 0.5, vtxs)) { continue; } //plot @@ -466,7 +466,7 @@ void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const V } //test against original bounds - if (!Geometry3D::triangle_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs)) { + if (!Geometry3D::triangle_box_overlap(original_bounds.get_center(), original_bounds.size * 0.5, vtxs)) { continue; } //plot face @@ -885,7 +885,7 @@ Vector<uint8_t> Voxelizer::get_sdf_3d_image() const { void Voxelizer::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx) { if (p_level == cell_subdiv - 1) { - Vector3 center = p_aabb.position + p_aabb.size * 0.5; + Vector3 center = p_aabb.get_center(); Transform3D xform; xform.origin = center; xform.basis.scale(p_aabb.size * 0.5); diff --git a/scene/animation/SCsub b/scene/animation/SCsub index cc33a5af84..d0aa0bc8aa 100644 --- a/scene/animation/SCsub +++ b/scene/animation/SCsub @@ -6,11 +6,8 @@ Import("env") thirdparty_obj = [] -thirdparty_sources = "#thirdparty/misc/easing_equations.cpp" - env_thirdparty = env.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) env.scene_sources += thirdparty_obj # Godot source files diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index d11387902a..af186072ac 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -248,27 +248,26 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) { if (fade_in > 0) { blend = time / fade_in; } else { - blend = 0; //wtf + blend = 0; } - - } else if (!do_start && remaining < fade_out) { - if (fade_out) { + } else if (!do_start && remaining <= fade_out) { + if (fade_out > 0) { blend = (remaining / fade_out); } else { - blend = 1.0; + blend = 0; } } else { blend = 1.0; } - float main_rem; + double main_rem; if (mix == MIX_MODE_ADD) { main_rem = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); } else { main_rem = blend_input(0, p_time, p_seek, 1.0 - blend, FILTER_BLEND, !sync); } - float os_rem = blend_input(1, os_seek ? time : p_time, os_seek, blend, FILTER_PASS, false); + double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, blend, FILTER_PASS, false); if (do_start) { remaining = os_rem; @@ -718,7 +717,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) { } else { // cross-fading from prev to current - float blend = xfade ? (prev_xfading / xfade) : 1; + float blend = xfade == 0 ? 0 : (prev_xfading / xfade); if (!p_seek && switched) { //just switched, seek to start of current diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index f6091f224c..5825a35030 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -737,7 +737,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (anim->has_loop()) { at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop } else { - at_anim_pos = MAX((double)anim->get_length(), p_time - pos); //seek to end + at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end } if (player->is_playing() || p_seeked) { @@ -765,6 +765,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } } else { player->play(anim_name); + player->seek(0.0, true); nc->animation_playing = true; playing_caches.insert(nc); } diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 88fb960164..9ca8d478b1 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -958,7 +958,7 @@ void AnimationTree::_process_graph(real_t p_delta) { Variant::interpolate(t->value, value, blend, t->value); - } else if (delta != 0) { + } else { List<int> indices; a->value_track_get_key_indices(i, time, delta, &indices); diff --git a/scene/animation/easing_equations.h b/scene/animation/easing_equations.h new file mode 100644 index 0000000000..c38d083b7f --- /dev/null +++ b/scene/animation/easing_equations.h @@ -0,0 +1,405 @@ +/*************************************************************************/ +/* easing_equations.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* + * Derived from Robert Penner's easing equations: http://robertpenner.com/easing/ + * + * Copyright (c) 2001 Robert Penner + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef EASING_EQUATIONS_H +#define EASING_EQUATIONS_H + +namespace linear { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c * t / d + b; +} +}; // namespace linear + +namespace sine { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return -c * cos(t / d * (Math_PI / 2)) + c + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + return c * sin(t / d * (Math_PI / 2)) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + return -c / 2 * (cos(Math_PI * t / d) - 1) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace sine + +namespace quint { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c * pow(t / d, 5) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + return c * (pow(t / d - 1, 5) + 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t = t / d * 2; + + if (t < 1) { + return c / 2 * pow(t, 5) + b; + } + return c / 2 * (pow(t - 2, 5) + 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace quint + +namespace quart { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c * pow(t / d, 4) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + return -c * (pow(t / d - 1, 4) - 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t = t / d * 2; + + if (t < 1) { + return c / 2 * pow(t, 4) + b; + } + return -c / 2 * (pow(t - 2, 4) - 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace quart + +namespace quad { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c * pow(t / d, 2) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t /= d; + return -c * t * (t - 2) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t = t / d * 2; + + if (t < 1) { + return c / 2 * pow(t, 2) + b; + } + return -c / 2 * ((t - 1) * (t - 3) - 1) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace quad + +namespace expo { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + if (t == d) { + return b + c; + } + return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + + if (t == d) { + return b + c; + } + + t = t / d * 2; + + if (t < 1) { + return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005; + } + return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace expo + +namespace elastic { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + + t /= d; + if (t == 1) { + return b + c; + } + + t -= 1; + float p = d * 0.3f; + float a = c * pow(2, 10 * t); + float s = p / 4; + + return -(a * sin((t * d - s) * (2 * Math_PI) / p)) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + + t /= d; + if (t == 1) { + return b + c; + } + + float p = d * 0.3f; + float s = p / 4; + + return (c * pow(2, -10 * t) * sin((t * d - s) * (2 * Math_PI) / p) + c + b); +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + + if ((t /= d / 2) == 2) { + return b + c; + } + + float p = d * (0.3f * 1.5f); + float a = c; + float s = p / 4; + + if (t < 1) { + t -= 1; + a *= pow(2, 10 * t); + return -0.5f * (a * sin((t * d - s) * (2 * Math_PI) / p)) + b; + } + + t -= 1; + a *= pow(2, -10 * t); + return a * sin((t * d - s) * (2 * Math_PI) / p) * 0.5f + c + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace elastic + +namespace cubic { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + t /= d; + return c * t * t * t + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t = t / d - 1; + return c * (t * t * t + 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t /= d / 2; + if (t < 1) { + return c / 2 * t * t * t + b; + } + + t -= 2; + return c / 2 * (t * t * t + 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace cubic + +namespace circ { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + t /= d; + return -c * (sqrt(1 - t * t) - 1) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t = t / d - 1; + return c * sqrt(1 - t * t) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t /= d / 2; + if (t < 1) { + return -c / 2 * (sqrt(1 - t * t) - 1) + b; + } + + t -= 2; + return c / 2 * (sqrt(1 - t * t) + 1) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace circ + +namespace bounce { +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t /= d; + + if (t < (1 / 2.75f)) { + return c * (7.5625f * t * t) + b; + } + + if (t < (2 / 2.75f)) { + t -= 1.5f / 2.75f; + return c * (7.5625f * t * t + 0.75f) + b; + } + + if (t < (2.5 / 2.75)) { + t -= 2.25f / 2.75f; + return c * (7.5625f * t * t + 0.9375f) + b; + } + + t -= 2.625f / 2.75f; + return c * (7.5625f * t * t + 0.984375f) + b; +} + +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c - out(d - t, 0, c, d) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return in(t * 2, b, c / 2, d); + } + return out(t * 2 - d, b + c / 2, c / 2, d); +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace bounce + +namespace back { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + float s = 1.70158f; + t /= d; + + return c * t * t * ((s + 1) * t - s) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + float s = 1.70158f; + t = t / d - 1; + + return c * (t * t * ((s + 1) * t + s) + 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + float s = 1.70158f * 1.525f; + t /= d / 2; + + if (t < 1) { + return c / 2 * (t * t * ((s + 1) * t - s)) + b; + } + + t -= 2; + return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace back + +#endif diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 3018fd3ae6..c43b83747b 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -30,8 +30,23 @@ #include "tween.h" +#include "scene/animation/easing_equations.h" #include "scene/main/node.h" +Tween::interpolater Tween::interpolaters[Tween::TRANS_MAX][Tween::EASE_MAX] = { + { &linear::in, &linear::in, &linear::in, &linear::in }, // Linear is the same for each easing. + { &sine::in, &sine::out, &sine::in_out, &sine::out_in }, + { &quint::in, &quint::out, &quint::in_out, &quint::out_in }, + { &quart::in, &quart::out, &quart::in_out, &quart::out_in }, + { &quad::in, &quad::out, &quad::in_out, &quad::out_in }, + { &expo::in, &expo::out, &expo::in_out, &expo::out_in }, + { &elastic::in, &elastic::out, &elastic::in_out, &elastic::out_in }, + { &cubic::in, &cubic::out, &cubic::in_out, &cubic::out_in }, + { &circ::in, &circ::out, &circ::in_out, &circ::out_in }, + { &bounce::in, &bounce::out, &bounce::in_out, &bounce::out_in }, + { &back::in, &back::out, &back::in_out, &back::out_in }, +}; + void Tweener::set_tween(Ref<Tween> p_tween) { tween = p_tween; } @@ -60,6 +75,11 @@ Ref<PropertyTweener> Tween::tween_property(Object *p_target, NodePath p_property ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree."); ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); +#ifdef DEBUG_ENABLED + Variant::Type property_type = p_target->get_indexed(p_property.get_as_property_path().get_subnames()).get_type(); + ERR_FAIL_COND_V_MSG(property_type != p_to.get_type(), Ref<PropertyTweener>(), "Type mismatch between property and final value: " + Variant::get_type_name(property_type) + " and " + Variant::get_type_name(p_to.get_type())); +#endif + Ref<PropertyTweener> tweener = memnew(PropertyTweener(p_target, p_property, p_to, p_duration)); append(tweener); return tweener; @@ -83,7 +103,7 @@ Ref<CallbackTweener> Tween::tween_callback(Callable p_callback) { return tweener; } -Ref<MethodTweener> Tween::tween_method(Callable p_callback, float p_from, float p_to, float p_duration) { +Ref<MethodTweener> Tween::tween_method(Callable p_callback, Variant p_from, Variant p_to, float p_duration) { ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree."); ERR_FAIL_COND_V_MSG(started, nullptr, "Can't append to a Tween that has started. Use stop() first."); @@ -312,6 +332,16 @@ bool Tween::should_pause() { return pause_mode != TWEEN_PAUSE_PROCESS; } +real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) { + if (p_duration == 0) { + // Special case to avoid dividing by 0 in equations. + return p_initial + p_delta; + } + + interpolater func = interpolaters[p_trans_type][p_ease_type]; + return func(p_time, p_initial, p_delta, p_duration); +} + Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, TransitionType p_trans, EaseType p_ease) { ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant()); ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant()); @@ -496,6 +526,8 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f } Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val) { + ERR_FAIL_COND_V_MSG(p_intial_val.get_type() != p_final_val.get_type(), p_intial_val, "Type mismatch between initial and final value: " + Variant::get_type_name(p_intial_val.get_type()) + " and " + Variant::get_type_name(p_final_val.get_type())); + switch (p_intial_val.get_type()) { case Variant::BOOL: { return (int)p_final_val - (int)p_intial_val; @@ -888,7 +920,7 @@ void MethodTweener::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ease", "ease"), &MethodTweener::set_ease); } -MethodTweener::MethodTweener(Callable p_callback, float p_from, float p_to, float p_duration) { +MethodTweener::MethodTweener(Callable p_callback, Variant p_from, Variant p_to, float p_duration) { callback = p_callback; initial_val = p_from; delta_val = tween->calculate_delta_value(p_from, p_to); diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 953d573539..6a48d332b8 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -128,7 +128,7 @@ public: Ref<PropertyTweener> tween_property(Object *p_target, NodePath p_property, Variant p_to, float p_duration); Ref<IntervalTweener> tween_interval(float p_time); Ref<CallbackTweener> tween_callback(Callable p_callback); - Ref<MethodTweener> tween_method(Callable p_callback, float p_from, float p_to, float p_duration); + Ref<MethodTweener> tween_method(Callable p_callback, Variant p_from, Variant p_to, float p_duration); void append(Ref<Tweener> p_tweener); bool custom_step(float p_delta); @@ -258,7 +258,7 @@ public: void start() override; bool step(float &r_delta) override; - MethodTweener(Callable p_callback, float p_from, float p_to, float p_duration); + MethodTweener(Callable p_callback, Variant p_from, Variant p_to, float p_duration); MethodTweener(); protected: diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index e7769f9372..c7d1d7ef82 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -227,17 +227,17 @@ void CodeEdit::_notification(int p_what) { end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), font_size).x; } - Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font_height * i + yofs); + Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent(font_size) + font_height * i + yofs); round_ofs = round_ofs.round(); draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, font_size, font_color); if (end > 0) { // Draw an underline for the currently edited function parameter. - const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + line_spacing); + const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + yofs); draw_line(b, b + Vector2(end - begin, 0), font_color, 2); // Draw a translucent text highlight as well. const Rect2 highlight_rect = Rect2( - hint_ofs + sb->get_offset() + Vector2(begin, 0), + b - Vector2(0, font_height), Vector2(end - begin, font_height)); draw_rect(highlight_rect, font_color * Color(1, 1, 1, 0.2)); } @@ -296,7 +296,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { mpos.x = get_size().x - mpos.x; } - Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + Point2i pos = get_line_column_at_pos(mpos); int line = pos.y; int col = pos.x; @@ -321,7 +321,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { mpos.x = get_size().x - mpos.x; } - Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + Point2i pos = get_line_column_at_pos(mpos); int line = pos.y; int col = pos.x; @@ -467,7 +467,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } /* MISC */ - if (k->is_action("ui_cancel", true)) { + if (!code_hint.is_empty() && k->is_action("ui_cancel", true)) { set_code_hint(""); accept_event(); return; @@ -1725,14 +1725,17 @@ bool CodeEdit::is_code_completion_enabled() const { void CodeEdit::set_code_completion_prefixes(const TypedArray<String> &p_prefixes) { code_completion_prefixes.clear(); for (int i = 0; i < p_prefixes.size(); i++) { - code_completion_prefixes.insert(p_prefixes[i]); + const String prefix = p_prefixes[i]; + + ERR_CONTINUE_MSG(prefix.is_empty(), "Code completion prefix cannot be empty."); + code_completion_prefixes.insert(prefix[0]); } } TypedArray<String> CodeEdit::get_code_completion_prefixes() const { TypedArray<String> prefixes; - for (Set<String>::Element *E = code_completion_prefixes.front(); E; E = E->next()) { - prefixes.push_back(E->get()); + for (const Set<char32_t>::Element *E = code_completion_prefixes.front(); E; E = E->next()) { + prefixes.push_back(String::chr(E->get())); } return prefixes; } @@ -1795,9 +1798,9 @@ void CodeEdit::request_code_completion(bool p_force) { String line = get_line(get_caret_line()); int ofs = CLAMP(get_caret_column(), 0, line.length()); - if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(String::chr(line[ofs - 1])))) { + if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) { emit_signal(SNAME("request_code_completion")); - } else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(String::chr(line[ofs - 2]))) { + } else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(line[ofs - 2])) { emit_signal(SNAME("request_code_completion")); } } @@ -1969,7 +1972,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) { end_complex_operation(); cancel_code_completion(); - if (code_completion_prefixes.has(String::chr(last_completion_char))) { + if (code_completion_prefixes.has(last_completion_char)) { request_code_completion(); } } @@ -2764,7 +2767,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { bool prev_is_word = false; /* Cancel if we are at the close of a string. */ - if (in_string == -1 && first_quote_col == cofs - 1) { + if (caret_column > 0 && in_string == -1 && first_quote_col == cofs - 1) { cancel_code_completion(); return; /* In a string, therefore we are trying to complete the string text. */ @@ -2790,9 +2793,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() { /* If all else fails, check for a prefix. */ /* Single space between caret and prefix is okay. */ bool prev_is_prefix = false; - if (cofs > 0 && code_completion_prefixes.has(String::chr(line[cofs - 1]))) { + if (cofs > 0 && code_completion_prefixes.has(line[cofs - 1])) { prev_is_prefix = true; - } else if (cofs > 1 && line[cofs - 1] == ' ' && code_completion_prefixes.has(String::chr(line[cofs - 2]))) { + } else if (cofs > 1 && line[cofs - 1] == ' ' && code_completion_prefixes.has(line[cofs - 2])) { prev_is_prefix = true; } diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 740548d559..d8eccb7d32 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -214,7 +214,7 @@ private: int code_completion_longest_line = 0; Rect2i code_completion_rect; - Set<String> code_completion_prefixes; + Set<char32_t> code_completion_prefixes; List<ScriptCodeCompletionOption> code_completion_option_submitted; List<ScriptCodeCompletionOption> code_completion_option_sources; String code_completion_base; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 1afb0b8e9d..611035fff9 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -827,7 +827,7 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { real_t dist = center.distance_to(bev->get_position()); if (dist <= center.x) { - real_t rad = Math::atan2(bev->get_position().y - center.y, bev->get_position().x - center.x); + real_t rad = bev->get_position().angle_to_point(center); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; s = CLAMP(dist / center.x, 0, 1); } else { @@ -844,7 +844,7 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { real_t dist = center.distance_to(bev->get_position()); if (dist >= center.x * 0.84 && dist <= center.x) { - real_t rad = Math::atan2(bev->get_position().y - center.y, bev->get_position().x - center.x); + real_t rad = bev->get_position().angle_to_point(center); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; spinning = true; } else { @@ -889,12 +889,12 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { Vector2 center = c->get_size() / 2.0; if (picker_type == SHAPE_VHS_CIRCLE) { real_t dist = center.distance_to(mev->get_position()); - real_t rad = Math::atan2(mev->get_position().y - center.y, mev->get_position().x - center.x); + real_t rad = mev->get_position().angle_to_point(center); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; s = CLAMP(dist / center.x, 0, 1); } else { if (spinning) { - real_t rad = Math::atan2(mev->get_position().y - center.y, mev->get_position().x - center.x); + real_t rad = mev->get_position().angle_to_point(center); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; } else { real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0; @@ -996,7 +996,7 @@ void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mev = p_event; if (mev.is_valid()) { Viewport *r = get_tree()->get_root(); - if (!r->get_visible_rect().has_point(Point2(mev->get_global_position().x, mev->get_global_position().y))) { + if (!r->get_visible_rect().has_point(mev->get_global_position())) { return; } @@ -1308,6 +1308,8 @@ void ColorPickerButton::_modal_closed() { void ColorPickerButton::pressed() { _update_picker(); + Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); + popup->set_as_minsize(); picker->_update_presets(); @@ -1319,13 +1321,13 @@ void ColorPickerButton::pressed() { if (i > 1) { cp_rect.position.y = get_screen_position().y - cp_rect.size.y; } else { - cp_rect.position.y = get_screen_position().y + get_size().height; + cp_rect.position.y = get_screen_position().y + size.height; } if (i & 1) { cp_rect.position.x = get_screen_position().x; } else { - cp_rect.position.x = get_screen_position().x - MAX(0, (cp_rect.size.x - get_size().x)); + cp_rect.position.x = get_screen_position().x - MAX(0, (cp_rect.size.x - size.x)); } if (usable_rect.encloses(cp_rect)) { diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index c97434f69b..11941529cd 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -96,17 +96,18 @@ void Container::fit_child_in_rect(Control *p_child, const Rect2 &p_rect) { ERR_FAIL_COND(!p_child); ERR_FAIL_COND(p_child->get_parent() != this); + bool rtl = is_layout_rtl(); Size2 minsize = p_child->get_combined_minimum_size(); Rect2 r = p_rect; if (!(p_child->get_h_size_flags() & SIZE_FILL)) { r.size.x = minsize.width; if (p_child->get_h_size_flags() & SIZE_SHRINK_END) { - r.position.x += p_rect.size.width - minsize.width; + r.position.x += rtl ? 0 : (p_rect.size.width - minsize.width); } else if (p_child->get_h_size_flags() & SIZE_SHRINK_CENTER) { r.position.x += Math::floor((p_rect.size.x - minsize.width) / 2); } else { - r.position.x += 0; + r.position.x += rtl ? (p_rect.size.width - minsize.width) : 0; } } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index cabae9feb2..d9c08ec272 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -356,16 +356,6 @@ void GraphEdit::_graph_node_raised(Node *p_gn) { } else { gn->raise(); } - int first_not_comment = 0; - for (int i = 0; i < get_child_count(); i++) { - GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i)); - if (gn2 && !gn2->is_comment()) { - first_not_comment = i; - break; - } - } - - move_child(connections_layer, first_not_comment); emit_signal(SNAME("node_selected"), p_gn); } @@ -707,7 +697,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } else if (!just_disconnected) { String from = connecting_from; int from_slot = connecting_index; - Vector2 ofs = Vector2(mb->get_position().x, mb->get_position().y); + Vector2 ofs = mb->get_position(); if (!connecting_out) { emit_signal(SNAME("connection_from_empty"), from, from_slot, ofs); @@ -1081,10 +1071,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { if (mm.is_valid() && box_selecting) { box_selecting_to = mm->get_position(); - box_selecting_rect = Rect2(MIN(box_selecting_from.x, box_selecting_to.x), - MIN(box_selecting_from.y, box_selecting_to.y), - ABS(box_selecting_from.x - box_selecting_to.x), - ABS(box_selecting_from.y - box_selecting_to.y)); + box_selecting_rect = Rect2(box_selecting_from.min(box_selecting_to), (box_selecting_from - box_selecting_to).abs()); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -2113,7 +2100,7 @@ void GraphEdit::arrange_nodes() { largest_node_size = 0.0f; } - emit_signal("begin_node_move"); + emit_signal(SNAME("begin_node_move")); for (const Set<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) { GraphNode *gn = Object::cast_to<GraphNode>(node_names[E->get()]); gn->set_drag(true); @@ -2126,7 +2113,7 @@ void GraphEdit::arrange_nodes() { gn->set_position_offset(pos); gn->set_drag(false); } - emit_signal("end_node_move"); + emit_signal(SNAME("end_node_move")); arranging_graph = false; } diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 08c8c60d7a..ecf735d7f5 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -31,6 +31,9 @@ #include "graph_node.h" #include "core/string/translation.h" +#ifdef TOOLS_ENABLED +#include "graph_edit.h" +#endif struct _MinSizeCache { int min_size; @@ -458,6 +461,27 @@ void GraphNode::_shape() { title_buf->add_string(title, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); } +#ifdef TOOLS_ENABLED +void GraphNode::_edit_set_position(const Point2 &p_position) { + GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent()); + if (graph) { + Point2 offset = (p_position + graph->get_scroll_ofs()) * graph->get_zoom(); + set_position_offset(offset); + } + set_position(p_position); +} + +void GraphNode::_validate_property(PropertyInfo &property) const { + Control::_validate_property(property); + GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent()); + if (graph) { + if (property.name == "rect_position") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } +} +#endif + void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right) { ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set slot with p_idx (%d) lesser than zero.", p_idx)); @@ -871,7 +895,7 @@ void GraphNode::gui_input(const Ref<InputEvent> &p_ev) { ERR_FAIL_COND_MSG(get_parent_control() == nullptr, "GraphNode must be the child of a GraphEdit node."); if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { - Vector2 mpos = Vector2(mb->get_position().x, mb->get_position().y); + Vector2 mpos = mb->get_position(); if (close_rect.size != Size2() && close_rect.has_point(mpos)) { //send focus to parent get_parent_control()->grab_focus(); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index c7c7006bfc..2238cfdb56 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -98,6 +98,11 @@ private: Overlay overlay = OVERLAY_DISABLED; +#ifdef TOOLS_ENABLED + void _edit_set_position(const Point2 &p_position) override; + void _validate_property(PropertyInfo &property) const override; +#endif + protected: virtual void gui_input(const Ref<InputEvent> &p_ev) override; void _notification(int p_what); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 5600816b2d..18cde25e55 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -92,8 +92,12 @@ void Label::_shape() { const Ref<Font> &font = get_theme_font(SNAME("font")); int font_size = get_theme_font_size(SNAME("font_size")); ERR_FAIL_COND(font.is_null()); - TS->shaped_text_add_string(text_rid, (uppercase) ? xl_text.to_upper() : xl_text, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); - TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, xl_text)); + String text = (uppercase) ? xl_text.to_upper() : xl_text; + if (visible_chars >= 0) { + text = text.substr(0, visible_chars); + } + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); + TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text)); dirty = false; lines_dirty = true; } @@ -258,11 +262,18 @@ void Label::_notification(int p_what) { return; // Nothing new. } xl_text = new_text; + if (percent_visible < 1) { + visible_chars = get_total_character_count() * percent_visible; + } dirty = true; update(); } + if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) { + update(); + } + if (p_what == NOTIFICATION_DRAW) { if (clip) { RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); @@ -286,6 +297,7 @@ void Label::_notification(int p_what) { int outline_size = get_theme_constant(SNAME("outline_size")); int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); bool rtl = TS->shaped_text_get_direction(text_rid); + bool rtl_layout = is_layout_rtl(); style->draw(ci, Rect2(Point2(0, 0), get_size())); @@ -342,24 +354,6 @@ void Label::_notification(int p_what) { } } - int visible_glyphs = -1; - int glyhps_drawn = 0; - if (percent_visible < 1) { - int total_glyphs = 0; - for (int i = lines_skipped; i < last_line; i++) { - const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]); - const TextServer::Glyph *glyphs = visual.ptr(); - int gl_size = visual.size(); - for (int j = 0; j < gl_size; j++) { - if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - total_glyphs++; - } - } - } - - visible_glyphs = MIN(total_glyphs, visible_chars); - } - Vector2 ofs; ofs.y = style->get_offset().y + vbegin; for (int i = lines_skipped; i < last_line; i++) { @@ -375,13 +369,21 @@ void Label::_notification(int p_what) { } break; case ALIGN_LEFT: { - ofs.x = style->get_offset().x; + if (rtl_layout) { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + ofs.x = style->get_offset().x; + } } break; case ALIGN_CENTER: { ofs.x = int(size.width - line_size.width) / 2; } break; case ALIGN_RIGHT: { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + if (rtl_layout) { + ofs.x = style->get_offset().x; + } else { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } } break; } @@ -407,14 +409,6 @@ void Label::_notification(int p_what) { // Draw main text. for (int j = 0; j < gl_size; j++) { for (int k = 0; k < glyphs[j].repeat; k++) { - if (visible_glyphs != -1) { - if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - if (glyhps_drawn >= visible_glyphs) { - return; - } - } - } - // Trim when necessary. if (trim_data.trim_pos >= 0) { if (rtl) { @@ -431,7 +425,6 @@ void Label::_notification(int p_what) { // Draw glyph outlines and shadow. draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); offset.x += glyphs[j].advance; - glyhps_drawn++; } } // Draw LTR ellipsis string when necessary. @@ -462,14 +455,6 @@ void Label::_notification(int p_what) { // Draw main text. for (int j = 0; j < gl_size; j++) { for (int k = 0; k < glyphs[j].repeat; k++) { - if (visible_glyphs != -1) { - if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - if (glyhps_drawn >= visible_glyphs) { - return; - } - } - } - // Trim when necessary. if (trim_data.trim_pos >= 0) { if (rtl) { @@ -486,7 +471,6 @@ void Label::_notification(int p_what) { // Draw glyph outlines and shadow. draw_glyph(glyphs[j], ci, font_color, ofs); ofs.x += glyphs[j].advance; - glyhps_drawn++; } } // Draw LTR ellipsis string when necessary. @@ -709,16 +693,16 @@ String Label::get_text() const { } void Label::set_visible_characters(int p_amount) { - visible_chars = p_amount; - if (get_total_character_count() > 0) { - percent_visible = (float)p_amount / (float)get_total_character_count(); - } else { - percent_visible = 1.0; - } - if (p_amount == -1) { - lines_dirty = true; + if (visible_chars != p_amount) { + visible_chars = p_amount; + if (get_total_character_count() > 0) { + percent_visible = (float)p_amount / (float)get_total_character_count(); + } else { + percent_visible = 1.0; + } + dirty = true; + update(); } - update(); } int Label::get_visible_characters() const { @@ -726,15 +710,17 @@ int Label::get_visible_characters() const { } void Label::set_percent_visible(float p_percent) { - if (p_percent < 0 || p_percent >= 1) { - visible_chars = -1; - percent_visible = 1; - lines_dirty = true; - } else { - visible_chars = get_total_character_count() * p_percent; - percent_visible = p_percent; + if (percent_visible != p_percent) { + if (p_percent < 0 || p_percent >= 1) { + visible_chars = -1; + percent_visible = 1; + } else { + visible_chars = get_total_character_count() * p_percent; + percent_visible = p_percent; + } + dirty = true; + update(); } - update(); } float Label::get_percent_visible() const { @@ -889,7 +875,7 @@ void Label::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1", PROPERTY_USAGE_EDITOR), "set_visible_characters", "get_visible_characters"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible"); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index d9acbeb828..09128770d5 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -260,24 +260,29 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { } else { if (selecting_enabled) { - if (!b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) { + const int triple_click_timeout = 600; + const int triple_click_tolerance = 5; + const bool is_triple_click = !b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && b->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance; + + if (is_triple_click && text.length()) { // Triple-click select all. selection.enabled = true; selection.begin = 0; selection.end = text.length(); selection.double_click = true; - selection.last_dblclk = 0; + last_dblclk = 0; caret_column = selection.begin; } else if (b->is_double_click()) { // Double-click select word. + last_dblclk = OS::get_singleton()->get_ticks_msec(); + last_dblclk_pos = b->get_position(); Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = 0; i < words.size(); i++) { - if (words[i].x < caret_column && words[i].y > caret_column) { + if ((words[i].x < caret_column && words[i].y > caret_column) || (i == words.size() - 1 && caret_column == words[i].y)) { selection.enabled = true; selection.begin = words[i].x; selection.end = words[i].y; selection.double_click = true; - selection.last_dblclk = OS::get_singleton()->get_ticks_msec(); caret_column = selection.end; break; } diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index e364a79c83..9f28c84f50 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -136,7 +136,6 @@ private: bool creating = false; bool double_click = false; bool drag_attempt = false; - uint64_t last_dblclk = 0; } selection; struct TextOperation { @@ -153,6 +152,9 @@ private: bool pressing_inside = false; } clear_button_status; + uint64_t last_dblclk = 0; + Vector2 last_dblclk_pos; + bool caret_blink_enabled = false; bool caret_force_displayed = false; bool draw_caret = true; diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 737ba84617..ceb2092e3a 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -87,15 +87,17 @@ void MenuButton::_popup_visibility_changed(bool p_visible) { void MenuButton::pressed() { emit_signal(SNAME("about_to_popup")); - Size2 size = get_size(); + Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); + popup->set_size(Size2(size.width, 0)); Point2 gp = get_screen_position(); - gp.y += get_size().y; - + gp.y += size.y; + if (is_layout_rtl()) { + gp.x += size.width - popup->get_size().width; + } popup->set_position(gp); + popup->set_parent_rect(Rect2(Point2(gp - popup->get_position()), size)); - popup->set_size(Size2(size.width, 0)); - popup->set_parent_rect(Rect2(Point2(gp - popup->get_position()), get_size())); popup->take_mouse_focus(); popup->popup(); } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index d16e96dbec..2adeb2d947 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -115,7 +115,7 @@ void OptionButton::_selected(int p_which) { } void OptionButton::pressed() { - Size2 size = get_size(); + Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); popup->set_size(Size2(size.width, 0)); popup->popup(); diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index e9414598a2..be05fd5a60 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -194,7 +194,7 @@ Popup::~Popup() { } Size2 PopupPanel::_get_contents_minimum_size() const { - Ref<StyleBox> p = get_theme_stylebox("panel", get_class_name()); + Ref<StyleBox> p = get_theme_stylebox(SNAME("panel"), get_class_name()); Size2 ms; @@ -217,7 +217,7 @@ Size2 PopupPanel::_get_contents_minimum_size() const { } void PopupPanel::_update_child_rects() { - Ref<StyleBox> p = get_theme_stylebox("panel", get_class_name()); + Ref<StyleBox> p = get_theme_stylebox(SNAME("panel"), get_class_name()); Vector2 cpos(p->get_offset()); Vector2 csize(get_size() - p->get_minimum_size()); @@ -244,9 +244,9 @@ void PopupPanel::_update_child_rects() { void PopupPanel::_notification(int p_what) { if (p_what == NOTIFICATION_THEME_CHANGED) { - panel->add_theme_style_override("panel", get_theme_stylebox("panel", get_class_name())); + panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); } else if (p_what == NOTIFICATION_READY || p_what == NOTIFICATION_ENTER_TREE) { - panel->add_theme_style_override("panel", get_theme_stylebox("panel", get_class_name())); + panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); _update_child_rects(); } else if (p_what == NOTIFICATION_WM_SIZE_CHANGED) { _update_child_rects(); diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp index 236d106af8..076fa132c0 100644 --- a/scene/gui/rich_text_effect.cpp +++ b/scene/gui/rich_text_effect.cpp @@ -90,6 +90,12 @@ void CharFXTransform::_bind_methods() { ClassDB::bind_method(D_METHOD("get_glyph_index"), &CharFXTransform::get_glyph_index); ClassDB::bind_method(D_METHOD("set_glyph_index", "glyph_index"), &CharFXTransform::set_glyph_index); + ClassDB::bind_method(D_METHOD("get_glyph_count"), &CharFXTransform::get_glyph_count); + ClassDB::bind_method(D_METHOD("set_glyph_count", "glyph_count"), &CharFXTransform::set_glyph_count); + + ClassDB::bind_method(D_METHOD("get_glyph_flags"), &CharFXTransform::get_glyph_flags); + ClassDB::bind_method(D_METHOD("set_glyph_flags", "glyph_flags"), &CharFXTransform::set_glyph_flags); + ClassDB::bind_method(D_METHOD("get_font"), &CharFXTransform::get_font); ClassDB::bind_method(D_METHOD("set_font", "font"), &CharFXTransform::set_font); @@ -101,5 +107,7 @@ void CharFXTransform::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "env"), "set_environment", "get_environment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_index"), "set_glyph_index", "get_glyph_index"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_count"), "set_glyph_count", "get_glyph_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_flags"), "set_glyph_flags", "get_glyph_flags"); ADD_PROPERTY(PropertyInfo(Variant::RID, "font"), "set_font", "get_font"); } diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h index f5506542bb..5681f9b193 100644 --- a/scene/gui/rich_text_effect.h +++ b/scene/gui/rich_text_effect.h @@ -50,6 +50,8 @@ public: double elapsed_time = 0.0f; Dictionary environment; uint32_t glyph_index = 0; + uint16_t glyph_flags = 0; + uint8_t glyph_count = 0; RID font; CharFXTransform(); @@ -57,19 +59,31 @@ public: Vector2i get_range() { return range; } void set_range(const Vector2i &p_range) { range = p_range; } + double get_elapsed_time() { return elapsed_time; } void set_elapsed_time(double p_elapsed_time) { elapsed_time = p_elapsed_time; } + bool is_visible() { return visibility; } void set_visibility(bool p_visibility) { visibility = p_visibility; } + bool is_outline() { return outline; } void set_outline(bool p_outline) { outline = p_outline; } + Point2 get_offset() { return offset; } void set_offset(Point2 p_offset) { offset = p_offset; } + Color get_color() { return color; } void set_color(Color p_color) { color = p_color; } uint32_t get_glyph_index() const { return glyph_index; }; void set_glyph_index(uint32_t p_glyph_index) { glyph_index = p_glyph_index; }; + + uint16_t get_glyph_flags() const { return glyph_index; }; + void set_glyph_flags(uint16_t p_glyph_flags) { glyph_flags = p_glyph_flags; }; + + uint8_t get_glyph_count() const { return glyph_count; }; + void set_glyph_count(uint8_t p_glyph_count) { glyph_count = p_glyph_count; }; + RID get_font() const { return font; }; void set_font(RID p_font) { font = p_font; }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 562bac60c2..277026cc28 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -333,7 +333,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> } else { frame->lines.write[i].offset.y = 0; } - frame->lines.write[i].offset += Vector2(offset.x, offset.y); + frame->lines.write[i].offset += offset; float h = frame->lines[i].text_buf->get_size().y; if (frame->min_size_over.y > 0) { @@ -399,8 +399,9 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Shape current paragraph. String text; Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + int remaining_characters = visible_characters - l.char_offset; for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { - if (visible_characters >= 0 && l.char_offset + l.char_count > visible_characters) { + if (visible_characters >= 0 && remaining_characters <= 0) { break; } switch (it->type) { @@ -427,7 +428,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } l.text_buf->add_string("\n", font, font_size, Dictionary(), ""); text += "\n"; - l.char_count += 1; + l.char_count++; + remaining_characters--; } break; case ITEM_TEXT: { ItemText *t = (ItemText *)it; @@ -442,9 +444,10 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> Dictionary font_ftr = _find_font_features(it); String lang = _find_language(it); String tx = t->text; - if (visible_characters >= 0 && l.char_offset + l.char_count + tx.length() > visible_characters) { - tx = tx.substr(0, l.char_offset + l.char_count + tx.length() - visible_characters); + if (visible_characters >= 0 && remaining_characters >= 0) { + tx = tx.substr(0, remaining_characters); } + remaining_characters -= tx.length(); l.text_buf->add_string(tx, font, font_size, font_ftr, lang); text += tx; @@ -454,7 +457,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> ItemImage *img = (ItemImage *)it; l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1); text += String::chr(0xfffc); - l.char_count += 1; + l.char_count++; + remaining_characters--; } break; case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); @@ -483,6 +487,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> int cell_ch = (char_offset - (l.char_offset + l.char_count)); l.char_count += cell_ch; t_char_count += cell_ch; + remaining_characters -= cell_ch; table->columns.write[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x)); table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wraped_size().x)); @@ -573,7 +578,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } else { frame->lines.write[i].offset.y = 0; } - frame->lines.write[i].offset += Vector2(offset.x, offset.y); + frame->lines.write[i].offset += offset; float h = frame->lines[i].text_buf->get_size().y; if (frame->min_size_over.y > 0) { @@ -847,6 +852,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off); RID frid = glyphs[i].font_rid; uint32_t gl = glyphs[i].index; + uint16_t gl_fl = glyphs[i].flags; + uint8_t gl_cn = glyphs[i].count; + bool cprev = false; + if (gl_cn == 0) { // Parts of the same cluster, always connected. + cprev = true; + } + if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected. + if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) { + cprev = true; + } + } else { + if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) { + cprev = true; + } + } //Apply fx. float faded_visibility = 1.0f; @@ -875,6 +895,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o charfx->outline = true; charfx->font = frid; charfx->glyph_index = gl; + charfx->glyph_flags = gl_fl; + charfx->glyph_count = gl_cn; charfx->offset = fx_offset; charfx->color = font_color; @@ -890,25 +912,34 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (item_fx->type == ITEM_SHAKE) { ItemShake *item_shake = static_cast<ItemShake *>(item_fx); - uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start); - uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start); - uint64_t max_rand = 2147483647; - double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); - n_time = (n_time > 1.0) ? 1.0 : n_time; - fx_offset += Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; + if (!cprev) { + uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start); + uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start); + uint64_t max_rand = 2147483647; + double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); + n_time = (n_time > 1.0) ? 1.0 : n_time; + item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; + } + fx_offset += item_shake->prev_off; } else if (item_fx->type == ITEM_WAVE) { ItemWave *item_wave = static_cast<ItemWave *>(item_fx); - double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f); - fx_offset += Point2(0, 1) * value; + if (!cprev) { + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_wave->amplitude / 10.0f); + item_wave->prev_off = Point2(0, 1) * value; + } + fx_offset += item_wave->prev_off; } else if (item_fx->type == ITEM_TORNADO) { ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx); - double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius); - double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius); - fx_offset += Point2(torn_x, torn_y); + if (!cprev) { + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius); + double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius); + item_tornado->prev_off = Point2(torn_x, torn_y); + } + fx_offset += item_tornado->prev_off; } else if (item_fx->type == ITEM_RAINBOW) { ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx); @@ -999,6 +1030,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off); RID frid = glyphs[i].font_rid; uint32_t gl = glyphs[i].index; + uint16_t gl_fl = glyphs[i].flags; + uint8_t gl_cn = glyphs[i].count; + bool cprev = false; + if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected. + cprev = true; + } + if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected. + if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) { + cprev = true; + } + } else { + if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) { + cprev = true; + } + } //Apply fx. float faded_visibility = 1.0f; @@ -1027,6 +1073,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o charfx->outline = false; charfx->font = frid; charfx->glyph_index = gl; + charfx->glyph_flags = gl_fl; + charfx->glyph_count = gl_cn; charfx->offset = fx_offset; charfx->color = font_color; @@ -1042,25 +1090,34 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } else if (item_fx->type == ITEM_SHAKE) { ItemShake *item_shake = static_cast<ItemShake *>(item_fx); - uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start); - uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start); - uint64_t max_rand = 2147483647; - double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); - n_time = (n_time > 1.0) ? 1.0 : n_time; - fx_offset += Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; + if (!cprev) { + uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start); + uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start); + uint64_t max_rand = 2147483647; + double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); + n_time = (n_time > 1.0) ? 1.0 : n_time; + item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; + } + fx_offset += item_shake->prev_off; } else if (item_fx->type == ITEM_WAVE) { ItemWave *item_wave = static_cast<ItemWave *>(item_fx); - double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f); - fx_offset += Point2(0, 1) * value; + if (!cprev) { + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f); + item_wave->prev_off = Point2(0, 1) * value; + } + fx_offset += item_wave->prev_off; } else if (item_fx->type == ITEM_TORNADO) { ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx); - double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius); - double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius); - fx_offset += Point2(torn_x, torn_y); + if (!cprev) { + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius); + double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius); + item_tornado->prev_off = Point2(torn_x, torn_y); + } + fx_offset += item_tornado->prev_off; } else if (item_fx->type == ITEM_RAINBOW) { ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx); @@ -1374,8 +1431,8 @@ void RichTextLabel::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { - if (bbcode != "") { - set_bbcode(bbcode); + if (text != "") { + set_text(text); } main->first_invalid_line = 0; //invalidate ALL @@ -2321,8 +2378,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, item->size.width = p_image->get_width() * p_height / p_image->get_height(); } else { // keep original width and height - item->size.height = p_image->get_height(); - item->size.width = p_image->get_width(); + item->size = p_image->get_size(); } } @@ -2767,10 +2823,10 @@ bool RichTextLabel::is_scroll_following() const { Error RichTextLabel::parse_bbcode(const String &p_bbcode) { clear(); - return append_bbcode(p_bbcode); + return append_text(p_bbcode); } -Error RichTextLabel::append_bbcode(const String &p_bbcode) { +Error RichTextLabel::append_text(const String &p_bbcode) { int pos = 0; List<String> tag_stack; @@ -3824,8 +3880,8 @@ int RichTextLabel::get_selection_to() const { return selection.to_frame->lines[selection.to_line].char_offset + selection.to_char - 1; } -void RichTextLabel::set_bbcode(const String &p_bbcode) { - bbcode = p_bbcode; +void RichTextLabel::set_text(const String &p_bbcode) { + text = p_bbcode; if (is_inside_tree() && use_bbcode) { parse_bbcode(p_bbcode); } else { // raw text @@ -3834,8 +3890,8 @@ void RichTextLabel::set_bbcode(const String &p_bbcode) { } } -String RichTextLabel::get_bbcode() const { - return bbcode; +String RichTextLabel::get_text() const { + return text; } void RichTextLabel::set_use_bbcode(bool p_enable) { @@ -3843,19 +3899,24 @@ void RichTextLabel::set_use_bbcode(bool p_enable) { return; } use_bbcode = p_enable; - set_bbcode(bbcode); notify_property_list_changed(); + set_text(text); } bool RichTextLabel::is_using_bbcode() const { return use_bbcode; } -String RichTextLabel::get_text() { +String RichTextLabel::get_parsed_text() const { String text = ""; Item *it = main; while (it) { - if (it->type == ITEM_TEXT) { + if (it->type == ITEM_DROPCAP) { + const ItemDropcap *dc = (ItemDropcap *)it; + if (dc != nullptr) { + text += dc->text; + } + } else if (it->type == ITEM_TEXT) { ItemText *t = static_cast<ItemText *>(it); text += t->text; } else if (it->type == ITEM_NEWLINE) { @@ -3870,11 +3931,6 @@ String RichTextLabel::get_text() { return text; } -void RichTextLabel::set_text(const String &p_string) { - clear(); - add_text(p_string); -} - void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); if (text_direction != p_text_direction) { @@ -3931,7 +3987,6 @@ void RichTextLabel::set_percent_visible(float p_percent) { if (p_percent < 0 || p_percent >= 1) { visible_characters = -1; percent_visible = 1; - } else { visible_characters = get_total_character_count() * p_percent; percent_visible = p_percent; @@ -3948,8 +4003,8 @@ float RichTextLabel::get_percent_visible() const { void RichTextLabel::set_effects(Array p_effects) { custom_effects = p_effects; - if ((bbcode != "") && use_bbcode) { - parse_bbcode(bbcode); + if ((text != "") && use_bbcode) { + parse_bbcode(text); } } @@ -3963,8 +4018,8 @@ void RichTextLabel::install_effect(const Variant effect) { if (rteffect.is_valid()) { custom_effects.push_back(effect); - if ((bbcode != "") && use_bbcode) { - parse_bbcode(bbcode); + if ((text != "") && use_bbcode) { + parse_bbcode(text); } } } @@ -3977,14 +4032,20 @@ int RichTextLabel::get_content_height() const { return total_height; } -void RichTextLabel::_validate_property(PropertyInfo &property) const { - if (!use_bbcode && property.name == "bbcode_text") { - property.usage = PROPERTY_USAGE_NOEDITOR; +#ifndef DISABLE_DEPRECATED +// People will be very angry, if their texts get erased, because of #39148. (3.x -> 4.0) +// Altough some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty +bool RichTextLabel::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "bbcode_text" && !((String)p_value).is_empty()) { + set_text(p_value); + return true; } + return false; } +#endif void RichTextLabel::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); + ClassDB::bind_method(D_METHOD("get_parsed_text"), &RichTextLabel::get_parsed_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGN_CENTER)); @@ -4062,10 +4123,9 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_selected_text"), &RichTextLabel::get_selected_text); ClassDB::bind_method(D_METHOD("parse_bbcode", "bbcode"), &RichTextLabel::parse_bbcode); - ClassDB::bind_method(D_METHOD("append_bbcode", "bbcode"), &RichTextLabel::append_bbcode); + ClassDB::bind_method(D_METHOD("append_text", "bbcode"), &RichTextLabel::append_text); - ClassDB::bind_method(D_METHOD("set_bbcode", "text"), &RichTextLabel::set_bbcode); - ClassDB::bind_method(D_METHOD("get_bbcode"), &RichTextLabel::get_bbcode); + ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &RichTextLabel::set_visible_characters); ClassDB::bind_method(D_METHOD("get_visible_characters"), &RichTextLabel::get_visible_characters); @@ -4092,16 +4152,13 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects); ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect); - ADD_GROUP("BBCode", "bbcode_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled"); @@ -4163,16 +4220,20 @@ void RichTextLabel::_bind_methods() { } void RichTextLabel::set_visible_characters(int p_visible) { - visible_characters = p_visible; - if (p_visible == -1) { - percent_visible = 1; - } else { - int total_char_count = get_total_character_count(); - if (total_char_count > 0) { - percent_visible = (float)p_visible / (float)total_char_count; + if (visible_characters != p_visible) { + visible_characters = p_visible; + if (p_visible == -1) { + percent_visible = 1; + } else { + int total_char_count = get_total_character_count(); + if (total_char_count > 0) { + percent_visible = (float)p_visible / (float)total_char_count; + } } + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + update(); } - update(); } int RichTextLabel::get_visible_characters() const { @@ -4180,9 +4241,19 @@ int RichTextLabel::get_visible_characters() const { } int RichTextLabel::get_total_character_count() const { + // Note: Do not use line buffer "char_count", it includes only visible characters. int tc = 0; - for (int i = 0; i < current_frame->lines.size(); i++) { - tc += current_frame->lines[i].char_count; + Item *it = main; + while (it) { + if (it->type == ITEM_TEXT) { + ItemText *t = static_cast<ItemText *>(it); + tc += t->text.length(); + } else if (it->type == ITEM_NEWLINE) { + tc++; + } else if (it->type == ITEM_IMAGE) { + tc++; + } + it = _get_next_item(it, true); } return tc; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index f25a8bf193..94f02a3989 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -85,7 +85,6 @@ public: protected: void _notification(int p_what); static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; private: struct Item; @@ -268,6 +267,7 @@ private: float rate = 0.0f; uint64_t _current_rng = 0; uint64_t _previous_rng = 0; + Vector2 prev_off; ItemShake() { type = ITEM_SHAKE; } @@ -290,6 +290,7 @@ private: struct ItemWave : public ItemFX { float frequency = 1.0f; float amplitude = 1.0f; + Vector2 prev_off; ItemWave() { type = ITEM_WAVE; } }; @@ -297,6 +298,7 @@ private: struct ItemTornado : public ItemFX { float radius = 1.0f; float frequency = 1.0f; + Vector2 prev_off; ItemTornado() { type = ITEM_TORNADO; } }; @@ -452,16 +454,19 @@ private: virtual Dictionary parse_expressions_for_values(Vector<String> p_expressions); void _draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag); - +#ifndef DISABLE_DEPRECATED + // Kept for compatibility from 3.x to 4.0. + bool _set(const StringName &p_name, const Variant &p_value); +#endif bool use_bbcode = false; - String bbcode; + String text; int fixed_width = -1; bool fit_content_height = false; public: - String get_text(); + String get_parsed_text() const; void add_text(const String &p_text); void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlign p_align = INLINE_ALIGN_CENTER); void add_newline(); @@ -548,15 +553,13 @@ public: void selection_copy(); Error parse_bbcode(const String &p_bbcode); - Error append_bbcode(const String &p_bbcode); + Error append_text(const String &p_bbcode); void set_use_bbcode(bool p_enable); bool is_using_bbcode() const; - void set_bbcode(const String &p_bbcode); - String get_bbcode() const; - - void set_text(const String &p_string); + void set_text(const String &p_bbcode); + String get_text() const; void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 4edf373fbf..4a3a6837d5 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -560,7 +560,7 @@ void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) { if (mm.is_valid()) { if (drag_node_touching && !drag_node_touching_deaccel) { - Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); + Vector2 motion = mm->get_relative(); drag_node_accum -= motion; Vector2 diff = drag_node_from + drag_node_accum; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 0c051f61e2..0c0ec39c7f 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -167,7 +167,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { if (mm.is_valid()) { if (drag_touching && !drag_touching_deaccel) { - Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); + Vector2 motion = mm->get_relative(); drag_accum -= motion; if (beyond_deadzone || (scroll_h && Math::abs(drag_accum.x) > deadzone) || (scroll_v && Math::abs(drag_accum.y) > deadzone)) { diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 1074d0d8a0..0aec017649 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -68,6 +68,15 @@ void SpinBox::_text_submitted(const String &p_string) { _value_changed(0); } +void SpinBox::_text_changed(const String &p_string) { + int cursor_pos = line_edit->get_caret_column(); + + _text_submitted(p_string); + + // Line edit 'set_text' method resets the cursor position so we need to undo that. + line_edit->set_caret_column(cursor_pos); +} + LineEdit *SpinBox::get_line_edit() { return line_edit; } @@ -244,6 +253,24 @@ String SpinBox::get_prefix() const { return prefix; } +void SpinBox::set_update_on_text_changed(bool p_update) { + if (update_on_text_changed == p_update) { + return; + } + + update_on_text_changed = p_update; + + if (p_update) { + line_edit->connect("text_changed", callable_mp(this, &SpinBox::_text_changed), Vector<Variant>(), CONNECT_DEFERRED); + } else { + line_edit->disconnect("text_changed", callable_mp(this, &SpinBox::_text_changed)); + } +} + +bool SpinBox::get_update_on_text_changed() const { + return update_on_text_changed; +} + void SpinBox::set_editable(bool p_editable) { line_edit->set_editable(p_editable); } @@ -267,11 +294,14 @@ void SpinBox::_bind_methods() { ClassDB::bind_method(D_METHOD("get_prefix"), &SpinBox::get_prefix); ClassDB::bind_method(D_METHOD("set_editable", "editable"), &SpinBox::set_editable); ClassDB::bind_method(D_METHOD("is_editable"), &SpinBox::is_editable); + ClassDB::bind_method(D_METHOD("set_update_on_text_changed"), &SpinBox::set_update_on_text_changed); + ClassDB::bind_method(D_METHOD("get_update_on_text_changed"), &SpinBox::get_update_on_text_changed); ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply); ClassDB::bind_method(D_METHOD("get_line_edit"), &SpinBox::get_line_edit); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "update_on_text_changed"), "set_update_on_text_changed", "get_update_on_text_changed"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix"); } @@ -284,7 +314,6 @@ SpinBox::SpinBox() { line_edit->set_mouse_filter(MOUSE_FILTER_PASS); line_edit->set_align(LineEdit::ALIGN_LEFT); - //connect("value_changed",this,"_value_changed"); line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), Vector<Variant>(), CONNECT_DEFERRED); line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), Vector<Variant>(), CONNECT_DEFERRED); line_edit->connect("gui_input", callable_mp(this, &SpinBox::_line_edit_input)); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 9ec3885f1f..9828b894da 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -40,6 +40,7 @@ class SpinBox : public Range { LineEdit *line_edit; int last_w = 0; + bool update_on_text_changed = false; Timer *range_click_timer; void _range_click_timeout(); @@ -47,6 +48,8 @@ class SpinBox : public Range { void _text_submitted(const String &p_string); virtual void _value_changed(double) override; + void _text_changed(const String &p_string); + String prefix; String suffix; @@ -88,6 +91,9 @@ public: void set_prefix(const String &p_prefix); String get_prefix() const; + void set_update_on_text_changed(bool p_update); + bool get_update_on_text_changed() const; + void apply(); SpinBox(); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 137ce7e96f..c8a0501d8a 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -79,7 +79,7 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { Popup *popup = get_popup(); if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { - Point2 pos(mb->get_position().x, mb->get_position().y); + Point2 pos = mb->get_position(); Size2 size = get_size(); // Click must be on tabs in the tab header area. @@ -190,7 +190,7 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - Point2 pos(mm->get_position().x, mm->get_position().y); + Point2 pos = mm->get_position(); Size2 size = get_size(); // Mouse must be on tabs in the tab header area. @@ -538,7 +538,6 @@ void TabContainer::_notification(int p_what) { void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) { Control *control = get_tab_control(p_index); RID canvas = get_canvas_item(); - Ref<Font> font = get_theme_font(SNAME("font")); Color font_outline_color = get_theme_color(SNAME("font_outline_color")); int outline_size = get_theme_constant(SNAME("outline_size")); int icon_text_distance = get_theme_constant(SNAME("icon_separation")); @@ -1134,7 +1133,6 @@ Size2 TabContainer::get_minimum_size() const { Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Ref<Font> font = get_theme_font(SNAME("font")); if (tabs_visible) { ms.y += MAX(MAX(tab_unselected->get_minimum_size().y, tab_selected->get_minimum_size().y), tab_disabled->get_minimum_size().y); @@ -1212,6 +1210,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled); ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled); + ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden); + ClassDB::bind_method(D_METHOD("get_tab_hidden", "tab_idx"), &TabContainer::get_tab_hidden); ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point); ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup); ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup); diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 3ca2d1c1e9..ef34bec347 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -98,29 +98,45 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid()) { Point2 pos = mm->get_position(); - highlight_arrow = -1; if (buttons_visible) { Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); if (is_layout_rtl()) { if (pos.x < decr->get_width()) { - highlight_arrow = 1; + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } } else if (pos.x < incr->get_width() + decr->get_width()) { - highlight_arrow = 0; + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow != -1) { + highlight_arrow = -1; + update(); } } else { int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); if (pos.x > limit_minus_buttons + decr->get_width()) { - highlight_arrow = 1; + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } } else if (pos.x > limit_minus_buttons) { - highlight_arrow = 0; + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow != -1) { + highlight_arrow = -1; + update(); } } } _update_hover(); - update(); return; } @@ -167,7 +183,7 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) { if (mb->is_pressed() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == MOUSE_BUTTON_RIGHT))) { // clicks - Point2 pos(mb->get_position().x, mb->get_position().y); + Point2 pos = mb->get_position(); if (buttons_visible) { Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); @@ -241,6 +257,7 @@ void Tabs::_shape(int p_tab) { tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); tabs.write[p_tab].text_buf->clear(); + tabs.write[p_tab].text_buf->set_width(-1); if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { tabs.write[p_tab].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { @@ -529,7 +546,6 @@ bool Tabs::get_offset_buttons_visible() const { void Tabs::set_tab_title(int p_tab, const String &p_title) { ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].text = p_title; - tabs.write[p_tab].xl_text = atr(p_title); _shape(p_tab); update(); minimum_size_changed(); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 06dfc31621..5b13e1da0b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1444,7 +1444,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { _reset_caret_blink_timer(); - Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + Point2i pos = get_line_column_at_pos(mpos); int row = pos.y; int col = pos.x; @@ -1547,7 +1547,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) { _reset_caret_blink_timer(); - Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + Point2i pos = get_line_column_at_pos(mpos); int row = pos.y; int col = pos.x; @@ -3689,7 +3689,7 @@ void TextEdit::select_word_under_caret() { int end = 0; const Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); for (int i = 0; i < words.size(); i++) { - if (words[i].x <= caret.column && words[i].y >= caret.column) { + if ((words[i].x < caret.column && words[i].y > caret.column) || (i == words.size() - 1 && caret.column == words[i].y)) { begin = words[i].x; end = words[i].y; break; @@ -5385,7 +5385,7 @@ void TextEdit::_update_selection_mode_pointer() { dragging_selection = true; Point2 mp = get_local_mouse_pos(); - Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + Point2i pos = get_line_column_at_pos(mp); int line = pos.y; int col = pos.x; @@ -5402,7 +5402,7 @@ void TextEdit::_update_selection_mode_word() { dragging_selection = true; Point2 mp = get_local_mouse_pos(); - Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + Point2i pos = get_line_column_at_pos(mp); int line = pos.y; int col = pos.x; @@ -5411,7 +5411,7 @@ void TextEdit::_update_selection_mode_word() { int end = beg; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); for (int i = 0; i < words.size(); i++) { - if (words[i].x < caret_pos && words[i].y > caret_pos) { + if ((words[i].x < caret_pos && words[i].y > caret_pos) || (i == words.size() - 1 && caret_pos == words[i].y)) { beg = words[i].x; end = words[i].y; break; @@ -5450,7 +5450,7 @@ void TextEdit::_update_selection_mode_line() { dragging_selection = true; Point2 mp = get_local_mouse_pos(); - Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + Point2i pos = get_line_column_at_pos(mp); int line = pos.y; int col = pos.x; @@ -5765,7 +5765,7 @@ void TextEdit::_update_minimap_hover() { return; } - const int row = get_minimap_line_at_pos(Point2i(mp.x, mp.y)); + const int row = get_minimap_line_at_pos(mp); const bool new_hovering_minimap = row >= get_first_visible_line() && row <= get_last_full_visible_line(); if (new_hovering_minimap != hovering_minimap) { @@ -5786,7 +5786,7 @@ void TextEdit::_update_minimap_click() { minimap_clicked = true; dragging_minimap = true; - int row = get_minimap_line_at_pos(Point2i(mp.x, mp.y)); + int row = get_minimap_line_at_pos(mp); if (row >= get_first_visible_line() && (row < get_last_full_visible_line() || row >= (text.size() - 1))) { minimap_scroll_ratio = v_scroll->get_as_ratio(); diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 286f01ee33..35a098c6fd 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -471,7 +471,7 @@ void TextureProgressBar::_notification(int p_what) { Vector<Point2> uvs; Vector<Point2> points; uvs.push_back(get_relative_center()); - points.push_back(progress_offset + Point2(s.x * get_relative_center().x, s.y * get_relative_center().y)); + points.push_back(progress_offset + s * get_relative_center()); for (int i = 0; i < pts.size(); i++) { Point2 uv = unit_val_to_uv(pts[i]); if (uvs.find(uv) >= 0) { @@ -493,8 +493,7 @@ void TextureProgressBar::_notification(int p_what) { p = progress->get_size(); } - p.x *= get_relative_center().x; - p.y *= get_relative_center().y; + p *= get_relative_center(); p = p.floor(); draw_line(p - Point2(8, 0), p + Point2(8, 0), Color(0.9, 0.5, 0.5), 2); draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index c4dfbc0d4e..7d7596635c 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -144,6 +144,7 @@ void TreeItem::_change_tree(Tree *p_tree) { /* cell mode */ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); + Cell &c = cells.write[p_column]; c.mode = p_mode; c.min = 0; @@ -155,6 +156,8 @@ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { c.text = ""; c.dirty = true; c.icon_max_w = 0; + c.cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -166,19 +169,26 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { /* check mode */ void TreeItem::set_checked(int p_column, bool p_checked) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].checked = p_checked; cells.write[p_column].indeterminate = false; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } void TreeItem::set_indeterminate(int p_column, bool p_indeterminate) { ERR_FAIL_INDEX(p_column, cells.size()); + // Prevent uncheck if indeterminate set to false twice if (p_indeterminate == cells[p_column].indeterminate) { return; } + cells.write[p_column].indeterminate = p_indeterminate; cells.write[p_column].checked = false; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -211,6 +221,9 @@ void TreeItem::set_text(int p_column, String p_text) { } cells.write[p_column].step = 0; } + + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -227,6 +240,7 @@ void TreeItem::set_text_direction(int p_column, Control::TextDirection p_text_di cells.write[p_column].dirty = true; _changed_notify(p_column); } + cells.write[p_column].cached_minimum_size_dirty = true; } Control::TextDirection TreeItem::get_text_direction(int p_column) const { @@ -236,8 +250,11 @@ Control::TextDirection TreeItem::get_text_direction(int p_column) const { void TreeItem::clear_opentype_features(int p_column) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].opentype_features.clear(); cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -247,6 +264,8 @@ void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_va if (!cells[p_column].opentype_features.has(tag) || (int)cells[p_column].opentype_features[tag] != p_value) { cells.write[p_column].opentype_features[tag] = p_value; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } } @@ -262,9 +281,12 @@ int TreeItem::get_opentype_feature(int p_column, const String &p_name) const { void TreeItem::set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].st_parser != p_parser) { cells.write[p_column].st_parser = p_parser; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } } @@ -276,8 +298,11 @@ Control::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_ void TreeItem::set_structured_text_bidi_override_options(int p_column, Array p_args) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].st_args = p_args; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -288,9 +313,12 @@ Array TreeItem::get_structured_text_bidi_override_options(int p_column) const { void TreeItem::set_language(int p_column, const String &p_language) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].language != p_language) { cells.write[p_column].language = p_language; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } } @@ -302,7 +330,9 @@ String TreeItem::get_language(int p_column) const { void TreeItem::set_suffix(int p_column, String p_suffix) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].suffix = p_suffix; + cells.write[p_column].cached_minimum_size_dirty = true; _changed_notify(p_column); } @@ -314,7 +344,10 @@ String TreeItem::get_suffix(int p_column) const { void TreeItem::set_icon(int p_column, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon = p_icon; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -325,7 +358,10 @@ Ref<Texture2D> TreeItem::get_icon(int p_column) const { void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon_region = p_icon_region; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -347,7 +383,10 @@ Color TreeItem::get_icon_modulate(int p_column) const { void TreeItem::set_icon_max_width(int p_column, int p_max) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon_max_w = p_max; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -460,6 +499,10 @@ void TreeItem::uncollapse_tree() { void TreeItem::set_custom_minimum_height(int p_height) { custom_min_height = p_height; + + for (Cell &c : cells) + c.cached_minimum_size_dirty = true; + _changed_notify(); } @@ -784,6 +827,8 @@ void TreeItem::add_button(int p_column, const Ref<Texture2D> &p_button, int p_id button.disabled = p_disabled; button.tooltip = p_tooltip; cells.write[p_column].buttons.push_back(button); + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -827,6 +872,8 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_butto ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); cells.write[p_column].buttons.write[p_idx].texture = p_button; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -842,6 +889,8 @@ void TreeItem::set_button_disabled(int p_column, int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); cells.write[p_column].buttons.write[p_idx].disabled = p_disabled; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -854,7 +903,10 @@ bool TreeItem::is_button_disabled(int p_column, int p_idx) const { void TreeItem::set_editable(int p_column, bool p_editable) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].editable = p_editable; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -887,7 +939,9 @@ void TreeItem::clear_custom_color(int p_column) { void TreeItem::set_custom_font(int p_column, const Ref<Font> &p_font) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_font = p_font; + cells.write[p_column].cached_minimum_size_dirty = true; } Ref<Font> TreeItem::get_custom_font(int p_column) const { @@ -897,7 +951,9 @@ Ref<Font> TreeItem::get_custom_font(int p_column) const { void TreeItem::set_custom_font_size(int p_column, int p_font_size) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_font_size = p_font_size; + cells.write[p_column].cached_minimum_size_dirty = true; } int TreeItem::get_custom_font_size(int p_column) const { @@ -940,7 +996,9 @@ Color TreeItem::get_custom_bg_color(int p_column) const { void TreeItem::set_custom_as_button(int p_column, bool p_button) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_button = p_button; + cells.write[p_column].cached_minimum_size_dirty = true; } bool TreeItem::is_custom_set_as_button(int p_column) const { @@ -950,7 +1008,10 @@ bool TreeItem::is_custom_set_as_button(int p_column) const { void TreeItem::set_text_align(int p_column, TextAlign p_align) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].text_align = p_align; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -961,7 +1022,10 @@ TreeItem::TextAlign TreeItem::get_text_align(int p_column) const { void TreeItem::set_expand_right(int p_column, bool p_enable) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].expand_right = p_enable; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } @@ -972,6 +1036,10 @@ bool TreeItem::get_expand_right(int p_column) const { void TreeItem::set_disable_folding(bool p_disable) { disable_folding = p_disable; + + for (Cell &c : cells) + c.cached_minimum_size_dirty = true; + _changed_notify(0); } @@ -984,49 +1052,52 @@ Size2 TreeItem::get_minimum_size(int p_column) { Tree *tree = get_tree(); ERR_FAIL_COND_V(!tree, Size2()); - Size2 size; + const TreeItem::Cell &cell = cells[p_column]; - // Default offset? - //size.width += (disable_folding || tree->hide_folding) ? tree->cache.hseparation : tree->cache.item_margin; + if (cell.cached_minimum_size_dirty) { + Size2 size; - // Text. - const TreeItem::Cell &cell = cells[p_column]; - if (!cell.text.is_empty()) { - if (cell.dirty) { - tree->update_item_cell(this, p_column); + // Text. + if (!cell.text.is_empty()) { + if (cell.dirty) { + tree->update_item_cell(this, p_column); + } + Size2 text_size = cell.text_buf->get_size(); + size.width += text_size.width; + size.height = MAX(size.height, text_size.height); } - Size2 text_size = cell.text_buf->get_size(); - size.width += text_size.width; - size.height = MAX(size.height, text_size.height); - } - // Icon. - if (cell.mode == CELL_MODE_CHECK) { - size.width += tree->cache.checked->get_width() + tree->cache.hseparation; - } - if (cell.icon.is_valid()) { - Size2i icon_size = cell.get_icon_size(); - if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) { - icon_size.width = cell.icon_max_w; + // Icon. + if (cell.mode == CELL_MODE_CHECK) { + size.width += tree->cache.checked->get_width() + tree->cache.hseparation; + } + if (cell.icon.is_valid()) { + Size2i icon_size = cell.get_icon_size(); + if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) { + icon_size.width = cell.icon_max_w; + } + size.width += icon_size.width + tree->cache.hseparation; + size.height = MAX(size.height, icon_size.height); } - size.width += icon_size.width + tree->cache.hseparation; - size.height = MAX(size.height, icon_size.height); - } - // Buttons. - for (int i = 0; i < cell.buttons.size(); i++) { - Ref<Texture2D> texture = cell.buttons[i].texture; - if (texture.is_valid()) { - Size2 button_size = texture->get_size() + tree->cache.button_pressed->get_minimum_size(); - size.width += button_size.width; - size.height = MAX(size.height, button_size.height); + // Buttons. + for (int i = 0; i < cell.buttons.size(); i++) { + Ref<Texture2D> texture = cell.buttons[i].texture; + if (texture.is_valid()) { + Size2 button_size = texture->get_size() + tree->cache.button_pressed->get_minimum_size(); + size.width += button_size.width; + size.height = MAX(size.height, button_size.height); + } } - } - if (cell.buttons.size() >= 2) { - size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; + if (cell.buttons.size() >= 2) { + size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; + } + + cells.write[p_column].cached_minimum_size = size; + cells.write[p_column].cached_minimum_size_dirty = false; } - return size; + return cell.cached_minimum_size; } Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -1678,7 +1749,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected || !p_item->has_meta("__focus_rect")) { - Rect2i r(cell_rect.position, cell_rect.size); + Rect2i r = cell_rect; p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); @@ -1934,7 +2005,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 arrow = cache.arrow; } - Point2 apos = p_pos + p_draw_ofs + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset; + Point2 apos = p_pos + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset + p_draw_ofs; + apos.x += cache.item_margin - arrow->get_width(); if (rtl) { apos.x = get_size().width - apos.x - arrow->get_width(); @@ -3966,10 +4038,12 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { int Tree::get_column_minimum_width(int p_column) const { ERR_FAIL_INDEX_V(p_column, columns.size(), -1); + // Use the custom minimum width. int min_width = columns[p_column].custom_min_width; + // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(cache.font->get_string_size(columns[p_column].title).width, min_width); + min_width = MAX(cache.font->get_string_size(columns[p_column].title).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); } if (!columns[p_column].clip_content) { @@ -3994,7 +4068,11 @@ int Tree::get_column_minimum_width(int p_column) const { Size2 item_size = item->get_minimum_size(p_column); if (p_column == 0) { item_size.width += cache.item_margin * depth; + } else { + item_size.width += cache.hseparation; } + + // Check if the item is wider. min_width = MAX(min_width, item_size.width); } } @@ -4034,9 +4112,6 @@ int Tree::get_column_width(int p_column) const { } } - if (p_column < columns.size() - 1) { - column_width += cache.hseparation; - } return column_width; } diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 8b7ddc3faf..c4a6b6b058 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -95,6 +95,9 @@ private: bool expand_right = false; Color icon_color = Color(1, 1, 1); + Size2i cached_minimum_size; + bool cached_minimum_size_dirty = true; + TextAlign text_align = ALIGN_LEFT; Variant meta; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 05409b7bfe..f5d2d2f2a2 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -70,7 +70,9 @@ void Node::_notification(int p_notification) { if (data.parent) { data.process_owner = data.parent->data.process_owner; } else { - data.process_owner = nullptr; + ERR_PRINT("The root node can't be set to Inherit process mode, reverting to Pausable instead."); + data.process_mode = PROCESS_MODE_PAUSABLE; + data.process_owner = this; } } else { data.process_owner = this; @@ -436,18 +438,18 @@ void Node::set_process_mode(ProcessMode p_mode) { bool prev_can_process = can_process(); bool prev_enabled = _is_enabled(); - data.process_mode = p_mode; - - if (data.process_mode == PROCESS_MODE_INHERIT) { + if (p_mode == PROCESS_MODE_INHERIT) { if (data.parent) { - data.process_owner = data.parent->data.owner; + data.process_owner = data.parent->data.process_owner; } else { - data.process_owner = nullptr; + ERR_FAIL_MSG("The root node can't be set to Inherit process mode."); } } else { data.process_owner = this; } + data.process_mode = p_mode; + bool next_can_process = can_process(); bool next_enabled = _is_enabled(); @@ -702,6 +704,9 @@ bool Node::_can_process(bool p_paused) const { process_mode = data.process_mode; } + // The owner can't be set to inherit, must be a bug. + ERR_FAIL_COND_V(process_mode == PROCESS_MODE_INHERIT, false); + if (process_mode == PROCESS_MODE_DISABLED) { return false; } else if (process_mode == PROCESS_MODE_ALWAYS) { diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index e1b1b356a9..6e78193717 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1322,6 +1322,7 @@ SceneTree::SceneTree() { // Create with mainloop. root = memnew(Window); + root->set_process_mode(Node::PROCESS_MODE_PAUSABLE); root->set_name("root"); #ifndef _3D_DISABLED if (!root->get_world_3d().is_valid()) { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index fb86d37280..b3b743370b 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -35,13 +35,13 @@ #include "core/object/message_queue.h" #include "core/string/translation.h" #include "core/templates/pair.h" +#include "scene/2d/audio_listener_2d.h" #include "scene/2d/camera_2d.h" #include "scene/2d/collision_object_2d.h" -#include "scene/2d/listener_2d.h" #ifndef _3D_DISABLED +#include "scene/3d/audio_listener_3d.h" #include "scene/3d/camera_3d.h" #include "scene/3d/collision_object_3d.h" -#include "scene/3d/listener_3d.h" #include "scene/3d/world_environment.h" #endif // _3D_DISABLED #include "scene/gui/control.h" @@ -381,10 +381,10 @@ void Viewport::_notification(int p_what) { current_canvas = find_world_2d()->get_canvas(); RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas); - _update_listener_2d(); + _update_audio_listener_2d(); #ifndef _3D_DISABLED RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario()); - _update_listener_3d(); + _update_audio_listener_3d(); #endif // _3D_DISABLED add_to_group("_viewports"); @@ -408,9 +408,9 @@ void Viewport::_notification(int p_what) { } break; case NOTIFICATION_READY: { #ifndef _3D_DISABLED - if (listener_3d_set.size() && !listener_3d) { - Listener3D *first = nullptr; - for (Set<Listener3D *>::Element *E = listener_3d_set.front(); E; E = E->next()) { + if (audio_listener_3d_set.size() && !audio_listener_3d) { + AudioListener3D *first = nullptr; + for (Set<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) { if (first == nullptr || first->is_greater_than(E->get())) { first = E->get(); } @@ -822,27 +822,27 @@ Rect2 Viewport::get_visible_rect() const { return r; } -void Viewport::_update_listener_2d() { +void Viewport::_update_audio_listener_2d() { if (AudioServer::get_singleton()) { AudioServer::get_singleton()->notify_listener_changed(); } } void Viewport::set_as_audio_listener_2d(bool p_enable) { - if (p_enable == audio_listener_2d) { + if (p_enable == is_audio_listener_2d_enabled) { return; } - audio_listener_2d = p_enable; - _update_listener_2d(); + is_audio_listener_2d_enabled = p_enable; + _update_audio_listener_2d(); } bool Viewport::is_audio_listener_2d() const { - return audio_listener_2d; + return is_audio_listener_2d_enabled; } -Listener2D *Viewport::get_listener_2d() const { - return listener_2d; +AudioListener2D *Viewport::get_audio_listener_2d() const { + return audio_listener_2d; } void Viewport::enable_canvas_transform_override(bool p_enable) { @@ -909,18 +909,18 @@ void Viewport::_camera_2d_set(Camera2D *p_camera_2d) { camera_2d = p_camera_2d; } -void Viewport::_listener_2d_set(Listener2D *p_listener) { - if (listener_2d == p_listener) { +void Viewport::_audio_listener_2d_set(AudioListener2D *p_listener) { + if (audio_listener_2d == p_listener) { return; - } else if (listener_2d) { - listener_2d->clear_current(); + } else if (audio_listener_2d) { + audio_listener_2d->clear_current(); } - listener_2d = p_listener; + audio_listener_2d = p_listener; } -void Viewport::_listener_2d_remove(Listener2D *p_listener) { - if (listener_2d == p_listener) { - listener_2d = nullptr; +void Viewport::_audio_listener_2d_remove(AudioListener2D *p_listener) { + if (audio_listener_2d == p_listener) { + audio_listener_2d = nullptr; } } @@ -962,7 +962,7 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) { world_2d = Ref<World2D>(memnew(World2D)); } - _update_listener_2d(); + _update_audio_listener_2d(); if (is_inside_tree()) { current_canvas = find_world_2d()->get_canvas(); @@ -1972,35 +1972,30 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *from = gui.key_focus ? gui.key_focus : nullptr; - // Keyboard focus. - Ref<InputEventKey> k = p_event; - // Need to check for mods, otherwise any combination of alt/ctrl/shift+<up/down/left/right/etc> is handled here when it shouldn't be. - bool mods = k.is_valid() && (k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_shift_pressed() || k->is_meta_pressed()); - if (from && p_event->is_pressed()) { Control *next = nullptr; - if (p_event->is_action_pressed("ui_focus_next", true)) { + if (p_event->is_action_pressed("ui_focus_next", true, true)) { next = from->find_next_valid_focus(); } - if (p_event->is_action_pressed("ui_focus_prev", true)) { + if (p_event->is_action_pressed("ui_focus_prev", true, true)) { next = from->find_prev_valid_focus(); } - if (!mods && p_event->is_action_pressed("ui_up", true)) { + if (p_event->is_action_pressed("ui_up", true, true)) { next = from->_get_focus_neighbor(SIDE_TOP); } - if (!mods && p_event->is_action_pressed("ui_left", true)) { + if (p_event->is_action_pressed("ui_left", true, true)) { next = from->_get_focus_neighbor(SIDE_LEFT); } - if (!mods && p_event->is_action_pressed("ui_right", true)) { + if (p_event->is_action_pressed("ui_right", true, true)) { next = from->_get_focus_neighbor(SIDE_RIGHT); } - if (!mods && p_event->is_action_pressed("ui_down", true)) { + if (p_event->is_action_pressed("ui_down", true, true)) { next = from->_get_focus_neighbor(SIDE_BOTTOM); } @@ -3047,24 +3042,24 @@ Viewport::SDFScale Viewport::get_sdf_scale() const { } #ifndef _3D_DISABLED -Listener3D *Viewport::get_listener_3d() const { - return listener_3d; +AudioListener3D *Viewport::get_audio_listener_3d() const { + return audio_listener_3d; } void Viewport::set_as_audio_listener_3d(bool p_enable) { - if (p_enable == audio_listener_3d) { + if (p_enable == is_audio_listener_3d_enabled) { return; } - audio_listener_3d = p_enable; - _update_listener_3d(); + is_audio_listener_3d_enabled = p_enable; + _update_audio_listener_3d(); } bool Viewport::is_audio_listener_3d() const { - return audio_listener_3d; + return is_audio_listener_3d_enabled; } -void Viewport::_update_listener_3d() { +void Viewport::_update_audio_listener_3d() { if (AudioServer::get_singleton()) { AudioServer::get_singleton()->notify_listener_changed(); } @@ -3073,39 +3068,39 @@ void Viewport::_update_listener_3d() { void Viewport::_listener_transform_3d_changed_notify() { } -void Viewport::_listener_3d_set(Listener3D *p_listener) { - if (listener_3d == p_listener) { +void Viewport::_audio_listener_3d_set(AudioListener3D *p_listener) { + if (audio_listener_3d == p_listener) { return; } - listener_3d = p_listener; + audio_listener_3d = p_listener; - _update_listener_3d(); + _update_audio_listener_3d(); _listener_transform_3d_changed_notify(); } -bool Viewport::_listener_3d_add(Listener3D *p_listener) { - listener_3d_set.insert(p_listener); - return listener_3d_set.size() == 1; +bool Viewport::_audio_listener_3d_add(AudioListener3D *p_listener) { + audio_listener_3d_set.insert(p_listener); + return audio_listener_3d_set.size() == 1; } -void Viewport::_listener_3d_remove(Listener3D *p_listener) { - listener_3d_set.erase(p_listener); - if (listener_3d == p_listener) { - listener_3d = nullptr; +void Viewport::_audio_listener_3d_remove(AudioListener3D *p_listener) { + audio_listener_3d_set.erase(p_listener); + if (audio_listener_3d == p_listener) { + audio_listener_3d = nullptr; } } -void Viewport::_listener_3d_make_next_current(Listener3D *p_exclude) { - if (listener_3d_set.size() > 0) { - for (Set<Listener3D *>::Element *E = listener_3d_set.front(); E; E = E->next()) { +void Viewport::_audio_listener_3d_make_next_current(AudioListener3D *p_exclude) { + if (audio_listener_3d_set.size() > 0) { + for (Set<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) { if (p_exclude == E->get()) { continue; } if (!E->get()->is_inside_tree()) { continue; } - if (listener_3d != nullptr) { + if (audio_listener_3d != nullptr) { return; } @@ -3114,7 +3109,7 @@ void Viewport::_listener_3d_make_next_current(Listener3D *p_exclude) { } else { // Attempt to reset listener to the camera position. if (camera_3d != nullptr) { - _update_listener_3d(); + _update_audio_listener_3d(); _camera_3d_transform_changed_notify(); } } @@ -3168,7 +3163,7 @@ void Viewport::_camera_3d_set(Camera3D *p_camera) { camera_3d->notification(Camera3D::NOTIFICATION_BECAME_CURRENT); } - _update_listener_3d(); + _update_audio_listener_3d(); _camera_3d_transform_changed_notify(); } @@ -3330,7 +3325,7 @@ void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) { RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario()); } - _update_listener_3d(); + _update_audio_listener_3d(); } void Viewport::_own_world_3d_changed() { @@ -3351,7 +3346,7 @@ void Viewport::_own_world_3d_changed() { RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario()); } - _update_listener_3d(); + _update_audio_listener_3d(); } void Viewport::set_use_own_world_3d(bool p_world_3d) { @@ -3385,7 +3380,7 @@ void Viewport::set_use_own_world_3d(bool p_world_3d) { RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario()); } - _update_listener_3d(); + _update_audio_listener_3d(); } bool Viewport::is_using_own_world_3d() const { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index bfb52c4b98..3732f9cfd1 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -37,11 +37,11 @@ #ifndef _3D_DISABLED class Camera3D; class CollisionObject3D; -class Listener3D; +class AudioListener3D; class World3D; #endif // _3D_DISABLED -class Listener2D; +class AudioListener2D; class Camera2D; class CanvasItem; class CanvasLayer; @@ -202,7 +202,7 @@ private: Viewport *parent = nullptr; - Listener2D *listener_2d = nullptr; + AudioListener2D *audio_listener_2d = nullptr; Camera2D *camera_2d = nullptr; Set<CanvasLayer *> canvas_layers; @@ -210,8 +210,8 @@ private: RID current_canvas; RID subwindow_canvas; - bool audio_listener_2d = false; - RID internal_listener_2d; + bool is_audio_listener_2d_enabled = false; + RID internal_audio_listener_2d; bool override_canvas_transform = false; @@ -274,7 +274,7 @@ private: StringName unhandled_input_group; StringName unhandled_key_input_group; - void _update_listener_2d(); + void _update_audio_listener_2d(); bool disable_3d = false; @@ -418,9 +418,9 @@ private: bool _gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check); - friend class Listener2D; - void _listener_2d_set(Listener2D *p_listener); - void _listener_2d_remove(Listener2D *p_listener); + friend class AudioListener2D; + void _audio_listener_2d_set(AudioListener2D *p_listener); + void _audio_listener_2d_remove(AudioListener2D *p_listener); friend class Camera2D; void _camera_2d_set(Camera2D *p_camera_2d); @@ -463,7 +463,7 @@ protected: public: uint64_t get_processed_events_count() const { return event_count; } - Listener2D *get_listener_2d() const; + AudioListener2D *get_audio_listener_2d() const; Camera2D *get_camera_2d() const; void set_as_audio_listener_2d(bool p_enable); bool is_audio_listener_2d() const; @@ -593,20 +593,20 @@ public: #ifndef _3D_DISABLED bool use_xr = false; Scale3D scale_3d = SCALE_3D_DISABLED; - friend class Listener3D; - Listener3D *listener_3d = nullptr; - Set<Listener3D *> listener_3d_set; - bool audio_listener_3d = false; - RID internal_listener_3d; - Listener3D *get_listener_3d() const; + friend class AudioListener3D; + AudioListener3D *audio_listener_3d = nullptr; + Set<AudioListener3D *> audio_listener_3d_set; + bool is_audio_listener_3d_enabled = false; + RID internal_audio_listener_3d; + AudioListener3D *get_audio_listener_3d() const; void set_as_audio_listener_3d(bool p_enable); bool is_audio_listener_3d() const; - void _update_listener_3d(); + void _update_audio_listener_3d(); void _listener_transform_3d_changed_notify(); - void _listener_3d_set(Listener3D *p_listener); - bool _listener_3d_add(Listener3D *p_listener); //true if first - void _listener_3d_remove(Listener3D *p_listener); - void _listener_3d_make_next_current(Listener3D *p_exclude); + void _audio_listener_3d_set(AudioListener3D *p_listener); + bool _audio_listener_3d_add(AudioListener3D *p_listener); //true if first + void _audio_listener_3d_remove(AudioListener3D *p_listener); + void _audio_listener_3d_make_next_current(AudioListener3D *p_exclude); void _collision_object_3d_input_event(CollisionObject3D *p_object, Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 015a4d5dba..1e89b17044 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -36,6 +36,7 @@ #include "core/os/os.h" #include "scene/2d/animated_sprite_2d.h" #include "scene/2d/area_2d.h" +#include "scene/2d/audio_listener_2d.h" #include "scene/2d/audio_stream_player_2d.h" #include "scene/2d/back_buffer_copy.h" #include "scene/2d/camera_2d.h" @@ -49,7 +50,6 @@ #include "scene/2d/light_2d.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/line_2d.h" -#include "scene/2d/listener_2d.h" #include "scene/2d/mesh_instance_2d.h" #include "scene/2d/multimesh_instance_2d.h" #include "scene/2d/navigation_agent_2d.h" @@ -197,14 +197,15 @@ #include "scene/resources/visual_shader_sdf_nodes.h" #include "scene/resources/world_2d.h" #include "scene/resources/world_3d.h" -#include "scene/resources/world_margin_shape_2d.h" -#include "scene/resources/world_margin_shape_3d.h" +#include "scene/resources/world_boundary_shape_2d.h" +#include "scene/resources/world_boundary_shape_3d.h" #include "scene/scene_string_names.h" #include "scene/main/shader_globals_override.h" #ifndef _3D_DISABLED #include "scene/3d/area_3d.h" +#include "scene/3d/audio_listener_3d.h" #include "scene/3d/audio_stream_player_3d.h" #include "scene/3d/bone_attachment_3d.h" #include "scene/3d/camera_3d.h" @@ -217,7 +218,6 @@ #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" -#include "scene/3d/listener_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" #include "scene/3d/navigation_agent_3d.h" @@ -235,7 +235,7 @@ #include "scene/3d/remote_transform_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/3d/skeleton_ik_3d.h" -#include "scene/3d/soft_body_3d.h" +#include "scene/3d/soft_dynamic_body_3d.h" #include "scene/3d/spring_arm_3d.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body_3d.h" @@ -445,7 +445,7 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(GeometryInstance3D); GDREGISTER_CLASS(Camera3D); GDREGISTER_CLASS(ClippedCamera3D); - GDREGISTER_CLASS(Listener3D); + GDREGISTER_CLASS(AudioListener3D); GDREGISTER_CLASS(XRCamera3D); GDREGISTER_CLASS(XRController3D); GDREGISTER_CLASS(XRAnchor3D); @@ -490,13 +490,13 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(PhysicsBody3D); GDREGISTER_CLASS(StaticBody3D); GDREGISTER_CLASS(AnimatableBody3D); - GDREGISTER_CLASS(RigidBody3D); + GDREGISTER_CLASS(RigidDynamicBody3D); GDREGISTER_CLASS(KinematicCollision3D); GDREGISTER_CLASS(CharacterBody3D); GDREGISTER_CLASS(SpringArm3D); GDREGISTER_CLASS(PhysicalBone3D); - GDREGISTER_CLASS(SoftBody3D); + GDREGISTER_CLASS(SoftDynamicBody3D); GDREGISTER_CLASS(SkeletonIK3D); GDREGISTER_CLASS(BoneAttachment3D); @@ -650,7 +650,7 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(PhysicsBody2D); GDREGISTER_CLASS(StaticBody2D); GDREGISTER_CLASS(AnimatableBody2D); - GDREGISTER_CLASS(RigidBody2D); + GDREGISTER_CLASS(RigidDynamicBody2D); GDREGISTER_CLASS(CharacterBody2D); GDREGISTER_CLASS(KinematicCollision2D); GDREGISTER_CLASS(Area2D); @@ -672,7 +672,7 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init GDREGISTER_CLASS(Camera2D); - GDREGISTER_CLASS(Listener2D); + GDREGISTER_CLASS(AudioListener2D); GDREGISTER_VIRTUAL_CLASS(Joint2D); GDREGISTER_CLASS(PinJoint2D); GDREGISTER_CLASS(GrooveJoint2D); @@ -750,7 +750,7 @@ void register_scene_types() { GDREGISTER_CLASS(CapsuleShape3D); GDREGISTER_CLASS(CylinderShape3D); GDREGISTER_CLASS(HeightMapShape3D); - GDREGISTER_CLASS(WorldMarginShape3D); + GDREGISTER_CLASS(WorldBoundaryShape3D); GDREGISTER_CLASS(ConvexPolygonShape3D); GDREGISTER_CLASS(ConcavePolygonShape3D); @@ -831,7 +831,7 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init GDREGISTER_VIRTUAL_CLASS(Shape2D); - GDREGISTER_CLASS(WorldMarginShape2D); + GDREGISTER_CLASS(WorldBoundaryShape2D); GDREGISTER_CLASS(SegmentShape2D); GDREGISTER_CLASS(SeparationRayShape2D); GDREGISTER_CLASS(CircleShape2D); @@ -919,8 +919,8 @@ void register_scene_types() { ClassDB::add_compatibility_class("KinematicBody2D", "CharacterBody2D"); ClassDB::add_compatibility_class("KinematicCollision", "KinematicCollision3D"); ClassDB::add_compatibility_class("Light", "Light3D"); - ClassDB::add_compatibility_class("LineShape2D", "WorldMarginShape2D"); - ClassDB::add_compatibility_class("Listener", "Listener3D"); + ClassDB::add_compatibility_class("LineShape2D", "WorldBoundaryShape2D"); + ClassDB::add_compatibility_class("Listener", "AudioListener3D"); ClassDB::add_compatibility_class("MeshInstance", "MeshInstance3D"); ClassDB::add_compatibility_class("MultiMeshInstance", "MultiMeshInstance3D"); ClassDB::add_compatibility_class("NavigationAgent", "NavigationAgent3D"); @@ -950,20 +950,21 @@ void register_scene_types() { ClassDB::add_compatibility_class("PhysicsServer", "PhysicsServer3D"); ClassDB::add_compatibility_class("PhysicsShapeQueryParameters", "PhysicsShapeQueryParameters3D"); ClassDB::add_compatibility_class("PinJoint", "PinJoint3D"); - ClassDB::add_compatibility_class("PlaneShape", "WorldMarginShape3D"); + ClassDB::add_compatibility_class("PlaneShape", "WorldBoundaryShape3D"); ClassDB::add_compatibility_class("ProceduralSky", "Sky"); ClassDB::add_compatibility_class("ProximityGroup", "ProximityGroup3D"); ClassDB::add_compatibility_class("RayCast", "RayCast3D"); ClassDB::add_compatibility_class("RayShape", "SeparationRayShape3D"); ClassDB::add_compatibility_class("RayShape2D", "SeparationRayShape2D"); ClassDB::add_compatibility_class("RemoteTransform", "RemoteTransform3D"); - ClassDB::add_compatibility_class("RigidBody", "RigidBody3D"); + ClassDB::add_compatibility_class("RigidBody", "RigidDynamicBody3D"); + ClassDB::add_compatibility_class("RigidBody2D", "RigidDynamicBody2D"); ClassDB::add_compatibility_class("Shape", "Shape3D"); ClassDB::add_compatibility_class("ShortCut", "Shortcut"); ClassDB::add_compatibility_class("Skeleton", "Skeleton3D"); ClassDB::add_compatibility_class("SkeletonIK", "SkeletonIK3D"); ClassDB::add_compatibility_class("SliderJoint", "SliderJoint3D"); - ClassDB::add_compatibility_class("SoftBody", "SoftBody3D"); + ClassDB::add_compatibility_class("SoftBody", "SoftDynamicBody3D"); ClassDB::add_compatibility_class("Spatial", "Node3D"); ClassDB::add_compatibility_class("SpatialGizmo", "Node3DGizmo"); ClassDB::add_compatibility_class("SpatialMaterial", "StandardMaterial3D"); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index de557494c3..49ed9dcb82 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -48,7 +48,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol img->convert(Image::FORMAT_LA8); ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); - create(Size2(img->get_width(), img->get_height())); + create(img->get_size()); const uint8_t *r = img->get_data().ptr(); uint8_t *w = bitmask.ptrw(); diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index 7501efea9e..fa95ab0e79 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -161,7 +161,7 @@ void CanvasItemMaterial::flush_changes() { void CanvasItemMaterial::_queue_shader_change() { MutexLock lock(material_mutex); - if (!element.in_list()) { + if (is_initialized && !element.in_list()) { dirty_materials->add(&element); } } @@ -287,6 +287,7 @@ CanvasItemMaterial::CanvasItemMaterial() : set_particles_anim_loop(false); current_key.invalid_key = 1; + is_initialized = true; _queue_shader_change(); } diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h index 0a813e0ae5..37cd4de136 100644 --- a/scene/resources/canvas_item_material.h +++ b/scene/resources/canvas_item_material.h @@ -102,6 +102,7 @@ private: _FORCE_INLINE_ void _queue_shader_change(); _FORCE_INLINE_ bool _is_shader_dirty() const; + bool is_initialized = false; BlendMode blend_mode = BLEND_MODE_MIX; LightMode light_mode = LIGHT_MODE_NORMAL; bool particles_animation = false; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index fa3824e6eb..492e0512e4 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -51,7 +51,7 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, fl } else { texture = Ref<ImageTexture>(memnew(ImageTexture)); Ref<Image> img = memnew(Image(p_src)); - const Size2 orig_size = Size2(img->get_width(), img->get_height()); + const Size2 orig_size = img->get_size(); img->convert(Image::FORMAT_RGBA8); img->resize(orig_size.x * scale, orig_size.y * scale); @@ -97,7 +97,7 @@ template <class T> static Ref<Texture2D> make_icon(T p_src) { Ref<ImageTexture> texture(memnew(ImageTexture)); Ref<Image> img = memnew(Image(p_src)); - const Size2 orig_size = Size2(img->get_width(), img->get_height()); + const Size2 orig_size = img->get_size(); img->convert(Image::FORMAT_RGBA8); img->resize(orig_size.x * scale, orig_size.y * scale); texture->create_from_image(img); @@ -438,6 +438,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("caret_color", "TextEdit", control_font_color); theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0)); theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15)); + theme->set_color("search_result_color", "TextEdit", Color(0.3, 0.3, 0.3)); + theme->set_color("search_result_border_color", "TextEdit", Color(0.3, 0.3, 0.3, 0.4)); theme->set_constant("line_spacing", "TextEdit", 4 * scale); theme->set_constant("outline_size", "TextEdit", 0); @@ -483,6 +485,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("line_number_color", "CodeEdit", Color(0.67, 0.67, 0.67, 0.4)); theme->set_color("word_highlighted_color", "CodeEdit", Color(0.8, 0.9, 0.9, 0.15)); theme->set_color("line_length_guideline_color", "CodeEdit", Color(0.3, 0.5, 0.8, 0.1)); + theme->set_color("search_result_color", "CodeEdit", Color(0.3, 0.3, 0.3)); + theme->set_color("search_result_border_color", "CodeEdit", Color(0.3, 0.3, 0.3, 0.4)); theme->set_constant("completion_lines", "CodeEdit", 7); theme->set_constant("completion_max_width", "CodeEdit", 50); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 7af8e900fd..9b403a18f0 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -689,227 +689,272 @@ void FontData::remove_cache(int p_cache_index) { } Array FontData::get_size_cache_list(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_size_cache_list(cache[p_cache_index]); } void FontData::clear_size_cache(int p_cache_index) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_size_cache(cache[p_cache_index]); } void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_size_cache(cache[p_cache_index], p_size); } void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates); emit_changed(); } Dictionary FontData::get_variation_coordinates(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Dictionary()); _ensure_rid(p_cache_index); return TS->font_get_variation_coordinates(cache[p_cache_index]); } void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent); } real_t FontData::get_ascent(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_ascent(cache[p_cache_index], p_size); } void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_descent(cache[p_cache_index], p_size, p_descent); } real_t FontData::get_descent(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_descent(cache[p_cache_index], p_size); } void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position); } real_t FontData::get_underline_position(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_underline_position(cache[p_cache_index], p_size); } void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness); } real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_underline_thickness(cache[p_cache_index], p_size); } void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_scale(cache[p_cache_index], p_size, p_scale); } real_t FontData::get_scale(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_scale(cache[p_cache_index], p_size); } void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value); } int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing); } int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_texture_count(cache[p_cache_index], p_size); } void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_textures(cache[p_cache_index], p_size); } void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index); } void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image); } Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Ref<Image>()); _ensure_rid(p_cache_index); return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index); } void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset); } PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, PackedInt32Array()); _ensure_rid(p_cache_index); return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index); } Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_glyph_list(cache[p_cache_index], p_size); } void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_glyphs(cache[p_cache_index], p_size); } void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph); } void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance); } Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph); } void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset); } Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph); } void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size); } Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph); } void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect); } Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, Rect2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph); } void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx); } int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph); } Array FontData::get_kerning_list(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_kerning_list(cache[p_cache_index], p_size); } void FontData::clear_kerning_map(int p_cache_index, int p_size) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_kerning_map(cache[p_cache_index], p_size); } void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair); } void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning); } Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const { + ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair); } void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end); } void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) { + ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_render_glyph(cache[p_cache_index], p_size, p_index); } RID FontData::get_cache_rid(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, RID()); _ensure_rid(p_cache_index); return cache[p_cache_index]; } @@ -1008,10 +1053,8 @@ void Font::_data_changed() { void Font::_ensure_rid(int p_index) const { // Find or create cache record. - for (int i = 0; i < rids.size(); i++) { - if (!rids[i].is_valid() && data[i].is_valid()) { - rids.write[i] = data[i]->find_cache(variation_coordinates); - } + if (!rids[p_index].is_valid() && data[p_index].is_valid()) { + rids.write[p_index] = data[p_index]->find_cache(variation_coordinates); } } @@ -1171,6 +1214,14 @@ void Font::add_data(const Ref<FontData> &p_data) { if (data[data.size() - 1].is_valid()) { data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); + Dictionary data_var_list = p_data->get_supported_variation_list(); + for (int j = 0; j < data_var_list.size(); j++) { + int32_t tag = data_var_list.get_key_at_index(j); + Vector3i value = data_var_list.get_value_at_index(j); + if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { + variation_coordinates[TS->tag_to_name(tag)] = value.z; + } + } } cache.clear(); @@ -1190,6 +1241,14 @@ void Font::set_data(int p_idx, const Ref<FontData> &p_data) { data.write[p_idx] = p_data; rids.write[p_idx] = RID(); + Dictionary data_var_list = p_data->get_supported_variation_list(); + for (int j = 0; j < data_var_list.size(); j++) { + int32_t tag = data_var_list.get_key_at_index(j); + Vector3i value = data_var_list.get_value_at_index(j); + if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { + variation_coordinates[TS->tag_to_name(tag)] = value.z; + } + } if (data[p_idx].is_valid()) { data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 05d1a7bf94..fe7124de9e 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -335,9 +335,6 @@ int ImmediateMesh::surface_get_array_len(int p_idx) const { int ImmediateMesh::surface_get_array_index_len(int p_idx) const { return 0; } -bool ImmediateMesh::surface_is_softbody_friendly(int p_idx) const { - return false; -} Array ImmediateMesh::surface_get_arrays(int p_surface) const { ERR_FAIL_INDEX_V(p_surface, int(surfaces.size()), Array()); return RS::get_singleton()->mesh_surface_get_arrays(mesh, p_surface); diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h index 7010d40719..6673ee6f3d 100644 --- a/scene/resources/immediate_mesh.h +++ b/scene/resources/immediate_mesh.h @@ -94,7 +94,6 @@ public: virtual int get_surface_count() const override; virtual int surface_get_array_len(int p_idx) const override; virtual int surface_get_array_index_len(int p_idx) const override; - virtual bool surface_is_softbody_friendly(int p_idx) const override; virtual Array surface_get_arrays(int p_surface) const override; virtual Array surface_get_blend_shape_arrays(int p_surface) const override; virtual Dictionary surface_get_lods(int p_surface) const override; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 77a68151c4..3a6af3afb0 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -790,12 +790,10 @@ void BaseMaterial3D::_update_shader() { } } break; case BILLBOARD_FIXED_Y: { - code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)), 0.0),WORLD_MATRIX[3]);\n"; + code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), CAMERA_MATRIX[2].xyz)),0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(normalize(cross(CAMERA_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))),0.0),WORLD_MATRIX[3]);\n"; if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { - code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; - } else { - code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1.0, 0.0, 0.0, 0.0),vec4(0.0, 1.0/length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0 ,1.0));\n"; + code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n"; } } break; case BILLBOARD_PARTICLES: { @@ -1303,7 +1301,7 @@ void BaseMaterial3D::flush_changes() { void BaseMaterial3D::_queue_shader_change() { MutexLock lock(material_mutex); - if (!element.in_list()) { + if (is_initialized && !element.in_list()) { dirty_materials->add(&element); } } @@ -2779,6 +2777,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : flags[FLAG_USE_TEXTURE_REPEAT] = true; + is_initialized = true; _queue_shader_change(); } diff --git a/scene/resources/material.h b/scene/resources/material.h index e2838e1399..5d7a5324ca 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -440,6 +440,7 @@ private: _FORCE_INLINE_ void _queue_shader_change(); _FORCE_INLINE_ bool _is_shader_dirty() const; + bool is_initialized = false; bool orm; Color albedo; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 1edc3ff38b..67db8c1a10 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -38,7 +38,7 @@ #include <stdlib.h> -Mesh::ConvexDecompositionFunc Mesh::convex_composition_function = nullptr; +Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr; Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { if (triangle_mesh.is_valid()) { @@ -156,70 +156,12 @@ void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) { } } -bool Mesh::surface_is_softbody_friendly(int p_idx) const { - const uint32_t surface_format = surface_get_format(p_idx); - return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE); -} - Vector<Face3> Mesh::get_faces() const { Ref<TriangleMesh> tm = generate_triangle_mesh(); if (tm.is_valid()) { return tm->get_faces(); } return Vector<Face3>(); - /* - for (int i=0;i<surfaces.size();i++) { - if (RenderingServer::get_singleton()->mesh_surface_get_primitive_type( mesh, i ) != RenderingServer::PRIMITIVE_TRIANGLES ) - continue; - - Vector<int> indices; - Vector<Vector3> vertices; - - vertices=RenderingServer::get_singleton()->mesh_surface_get_array(mesh, i,RenderingServer::ARRAY_VERTEX); - - int len=RenderingServer::get_singleton()->mesh_surface_get_array_index_len(mesh, i); - bool has_indices; - - if (len>0) { - indices=RenderingServer::get_singleton()->mesh_surface_get_array(mesh, i,RenderingServer::ARRAY_INDEX); - has_indices=true; - - } else { - len=vertices.size(); - has_indices=false; - } - - if (len<=0) - continue; - - const int* indicesr = indices.ptr(); - const int *indicesptr = indicesr.ptr(); - - const Vector3* verticesr = vertices.ptr(); - const Vector3 *verticesptr = verticesr.ptr(); - - int old_faces=faces.size(); - int new_faces=old_faces+(len/3); - - faces.resize(new_faces); - - Face3* facesw = faces.ptrw(); - Face3 *facesptr=facesw.ptr(); - - - for (int i=0;i<len/3;i++) { - Face3 face; - - for (int j=0;j<3;j++) { - int idx=i*3+j; - face.vertex[j] = has_indices ? verticesptr[ indicesptr[ idx ] ] : verticesptr[idx]; - } - - facesptr[i+old_faces]=face; - } - - } -*/ } Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { @@ -568,35 +510,36 @@ void Mesh::clear_cache() const { } Vector<Ref<Shape3D>> Mesh::convex_decompose(const ConvexDecompositionSettings &p_settings) const { - ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape3D>>()); + ERR_FAIL_COND_V(!convex_decomposition_function, Vector<Ref<Shape3D>>()); - const Vector<Face3> faces = get_faces(); - - const Vector<Vector<Face3>> decomposed = convex_composition_function(faces, p_settings); + Ref<TriangleMesh> tm = generate_triangle_mesh(); + ERR_FAIL_COND_V(!tm.is_valid(), Vector<Ref<Shape3D>>()); - Vector<Ref<Shape3D>> ret; + const Vector<TriangleMesh::Triangle> &triangles = tm->get_triangles(); + int triangle_count = triangles.size(); - for (int i = 0; i < decomposed.size(); i++) { - Set<Vector3> points; - for (int j = 0; j < decomposed[i].size(); j++) { - points.insert(decomposed[i][j].vertex[0]); - points.insert(decomposed[i][j].vertex[1]); - points.insert(decomposed[i][j].vertex[2]); - } - - Vector<Vector3> convex_points; - convex_points.resize(points.size()); - { - Vector3 *w = convex_points.ptrw(); - int idx = 0; - for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) { - w[idx++] = E->get(); + Vector<uint32_t> indices; + { + indices.resize(triangle_count * 3); + uint32_t *w = indices.ptrw(); + for (int i = 0; i < triangle_count; i++) { + for (int j = 0; j < 3; j++) { + w[i * 3 + j] = triangles[i].indices[j]; } } + } + + const Vector<Vector3> &vertices = tm->get_vertices(); + int vertex_count = vertices.size(); + Vector<Vector<Vector3>> decomposed = convex_decomposition_function((real_t *)vertices.ptr(), vertex_count, indices.ptr(), triangle_count, p_settings, nullptr); + + Vector<Ref<Shape3D>> ret; + + for (int i = 0; i < decomposed.size(); i++) { Ref<ConvexPolygonShape3D> shape; shape.instantiate(); - shape->set_points(convex_points); + shape->set_points(decomposed[i]); ret.push_back(shape); } diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 4d0ee0f247..8d5571d67c 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -132,7 +132,6 @@ public: virtual int get_surface_count() const = 0; virtual int surface_get_array_len(int p_idx) const = 0; virtual int surface_get_array_index_len(int p_idx) const = 0; - virtual bool surface_is_softbody_friendly(int p_idx) const; virtual Array surface_get_arrays(int p_surface) const = 0; virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0; virtual Dictionary surface_get_lods(int p_surface) const = 0; @@ -191,9 +190,9 @@ public: uint32_t max_convex_hulls = 1; bool project_hull_vertices = true; }; - typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &p_faces, const ConvexDecompositionSettings &p_settings); + typedef Vector<Vector<Vector3>> (*ConvexDecompositionFunc)(const real_t *p_vertices, int p_vertex_count, const uint32_t *p_triangles, int p_triangle_count, const ConvexDecompositionSettings &p_settings, Vector<Vector<uint32_t>> *r_convex_indices); - static ConvexDecompositionFunc convex_composition_function; + static ConvexDecompositionFunc convex_decomposition_function; Vector<Ref<Shape3D>> convex_decompose(const ConvexDecompositionSettings &p_settings) const; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index e74f759855..59faa50114 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -99,8 +99,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { #endif parent = nparent; } else { - // i == 0 is root node. Confirm that it doesn't have a parent defined. + // i == 0 is root node. ERR_FAIL_COND_V_MSG(n.parent != -1, nullptr, vformat("Invalid scene: root node %s cannot specify a parent node.", snames[n.name])); + ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANCED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); } Node *node = nullptr; diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 0495a9e92c..d9ec0bfd69 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -741,7 +741,7 @@ void ParticlesMaterial::flush_changes() { void ParticlesMaterial::_queue_shader_change() { MutexLock lock(material_mutex); - if (!element.in_list()) { + if (is_initialized && !element.in_list()) { dirty_materials->add(&element); } } @@ -1533,6 +1533,7 @@ ParticlesMaterial::ParticlesMaterial() : current_key.invalid_key = 1; + is_initialized = true; _queue_shader_change(); } diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h index 8ab26aff77..36bc456978 100644 --- a/scene/resources/particles_material.h +++ b/scene/resources/particles_material.h @@ -226,6 +226,7 @@ private: _FORCE_INLINE_ void _queue_shader_change(); _FORCE_INLINE_ bool _is_shader_dirty() const; + bool is_initialized = false; Vector3 direction; float spread; float flatness; diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index e7da41db9d..f8be00f5fb 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -1390,6 +1390,12 @@ void QuadMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset"); } +uint32_t QuadMesh::surface_get_format(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, 1, 0); + + return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV; +} + QuadMesh::QuadMesh() { primitive_type = PRIMITIVE_TRIANGLES; } @@ -1460,7 +1466,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const { } else { Vector3 p = Vector3(x * radius * w, y, z * radius * w); points.push_back(p); - Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale); + Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale); normals.push_back(normal.normalized()); }; ADD_TANGENT(z, 0.0, -x, 1.0) diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 7915cb0028..d447dad97a 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -285,6 +285,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + virtual uint32_t surface_get_format(int p_idx) const override; + QuadMesh(); void set_size(const Size2 &p_size); diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index e533fb054a..7ac40b497d 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -96,37 +96,25 @@ float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, floa p_max_bound = Math_TAU + p_max_bound; } if (p_min_bound > p_max_bound) { - float tmp = p_min_bound; - p_min_bound = p_max_bound; - p_max_bound = tmp; + SWAP(p_min_bound, p_max_bound); } + bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound); + bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound); + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (p_invert == false) { - if (p_angle < p_min_bound || p_angle > p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } - } - } else { - if (p_angle > p_min_bound && p_angle < p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); + if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) { + Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); + Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + p_angle = p_min_bound; + } else { + p_angle = p_max_bound; } } + return p_angle; } @@ -152,9 +140,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b arc_angle_max = (Math_PI * 2) + arc_angle_max; } if (arc_angle_min > arc_angle_max) { - float tmp = arc_angle_min; - arc_angle_min = arc_angle_max; - arc_angle_max = tmp; + SWAP(arc_angle_min, arc_angle_max); } arc_angle_min += p_operation_bone->get_bone_angle(); arc_angle_max += p_operation_bone->get_bone_angle(); diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp index 6e9429034f..3b5c555f89 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.cpp +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -247,7 +247,7 @@ void SkeletonModification2DFABRIK::chain_backwards() { } float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); - float length = current_bone2d_node_length / (previous_pose.get_origin() - current_pose.get_origin()).length(); + float length = current_bone2d_node_length / (current_pose.get_origin().distance_to(previous_pose.get_origin())); Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length); current_pose.set_origin(finish_position); @@ -268,7 +268,7 @@ void SkeletonModification2DFABRIK::chain_forwards() { Transform2D next_pose = fabrik_transform_chain[i + 1]; float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); - float length = current_bone2d_node_length / (current_pose.get_origin() - next_pose.get_origin()).length(); + float length = current_bone2d_node_length / (next_pose.get_origin().distance_to(current_pose.get_origin())); Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length); current_pose.set_origin(finish_position); diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp index 88d80a501f..4f752896a9 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -144,7 +144,7 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) { // With modifications by TwistedTwigleg Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position(); float joint_one_to_target = target_difference.length(); - float angle_atan = Math::atan2(target_difference.y, target_difference.x); + float angle_atan = target_difference.angle(); float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp index ee02ede2d5..b476952d86 100644 --- a/scene/resources/skeleton_modification_3d.cpp +++ b/scene/resources/skeleton_modification_3d.cpp @@ -72,37 +72,25 @@ real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, r p_max_bound = Math_TAU + p_max_bound; } if (p_min_bound > p_max_bound) { - real_t tmp = p_min_bound; - p_min_bound = p_max_bound; - p_max_bound = tmp; + SWAP(p_min_bound, p_max_bound); } + bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound); + bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound); + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (p_invert == false) { - if (p_angle < p_min_bound || p_angle > p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } - } - } else { - if (p_angle > p_min_bound && p_angle < p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } + if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) { + Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); + Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); + + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + p_angle = p_min_bound; + } else { + p_angle = p_max_bound; } } + return p_angle; } diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp index 69f75eb7b5..e615615924 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.cpp +++ b/scene/resources/skeleton_modification_3d_fabrik.cpp @@ -232,7 +232,7 @@ void SkeletonModification3DFABRIK::chain_backwards() { int current_bone_idx = fabrik_data_chain[i].bone_idx; Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx)); - real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length(); + real_t length = fabrik_data_chain[i].length / (current_trans.origin.distance_to(next_bone_trans.origin)); current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length); // Apply it back to the skeleton @@ -253,7 +253,7 @@ void SkeletonModification3DFABRIK::chain_forwards() { int next_bone_idx = fabrik_data_chain[i + 1].bone_idx; Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx)); - real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length(); + real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin.distance_to(current_trans.origin)); next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length); // Apply it back to the skeleton diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index d5e370568d..a8cd872408 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -1176,6 +1176,7 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun ERR_FAIL_COND_V(simplify_func == nullptr, lod); ERR_FAIL_COND_V(vertex_array.size() == 0, lod); ERR_FAIL_COND_V(index_array.size() == 0, lod); + ERR_FAIL_COND_V(index_array.size() % 3 != 0, lod); lod.resize(index_array.size()); LocalVector<float> vertices; //uses floats diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 3dc32632cc..80cab99373 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1248,6 +1248,14 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { return atlas->is_pixel_opaque(x, y); } +Ref<Image> AtlasTexture::get_image() const { + if (!atlas.is_valid()) { + return Ref<Image>(); + } + + return atlas->get_image()->get_rect(region); +} + AtlasTexture::AtlasTexture() {} ///////////////////////////////////////// diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 93f4e2de5a..862b9a47a6 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -250,6 +250,8 @@ public: bool is_pixel_opaque(int p_x, int p_y) const override; + virtual Ref<Image> get_image() const override; + AtlasTexture(); }; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 918fc3fe9c..5a8c5b3782 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -1019,20 +1019,25 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() { void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { if (tile_meshes_dirty) { - Vector<Vector2> uvs = get_tile_shape_polygon(); + Vector<Vector2> shape = get_tile_shape_polygon(); + Vector<Vector2> uvs; + uvs.resize(shape.size()); + for (int i = 0; i < shape.size(); i++) { + uvs.write[i] = shape[i] + Vector2(0.5, 0.5); + } Vector<Color> colors; - colors.resize(uvs.size()); + colors.resize(shape.size()); colors.fill(Color(1.0, 1.0, 1.0, 1.0)); // Filled mesh. tile_filled_mesh->clear_surfaces(); Array a; a.resize(Mesh::ARRAY_MAX); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_TEX_UV] = uvs; a[Mesh::ARRAY_COLOR] = colors; - a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(shape); tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); // Lines mesh. @@ -1040,9 +1045,9 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform a.clear(); a.resize(Mesh::ARRAY_MAX); // Add the first point again when drawing lines. - uvs.push_back(uvs[0]); + shape.push_back(shape[0]); colors.push_back(colors[0]); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_COLOR] = colors; tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); @@ -3119,7 +3124,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const { } bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> components = String(p_name).split("/", true, 2); + Vector<String> components = String(p_name).split("/", true, 3); // Compute the vector2i if we have coordinates. Vector<String> coords_split = components[0].split(":"); @@ -3138,8 +3143,32 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) // Properties. if (components[1] == "size_in_atlas") { move_tile_in_atlas(coords, coords, p_value); + return true; } else if (components[1] == "next_alternative_id") { tiles[coords].next_alternative_id = p_value; + return true; + } else if (components[1] == "animation_columns") { + set_tile_animation_columns(coords, p_value); + return true; + } else if (components[1] == "animation_separation") { + set_tile_animation_separation(coords, p_value); + return true; + } else if (components[1] == "animation_speed") { + set_tile_animation_speed(coords, p_value); + return true; + } else if (components[1] == "animation_frames_count") { + set_tile_animation_frames_count(coords, p_value); + return true; + } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[1].trim_prefix("animation_frame_").to_int(); + if (components[2] == "duration") { + if (frame >= get_tile_animation_frames_count(coords)) { + set_tile_animation_frames_count(coords, frame + 1); + } + set_tile_animation_frame_duration(coords, frame, p_value); + return true; + } + return false; } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { @@ -3185,6 +3214,28 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "next_alternative_id") { r_ret = tiles[coords].next_alternative_id; return true; + } else if (components[1] == "animation_columns") { + r_ret = get_tile_animation_columns(coords); + return true; + } else if (components[1] == "animation_separation") { + r_ret = get_tile_animation_separation(coords); + return true; + } else if (components[1] == "animation_speed") { + r_ret = get_tile_animation_speed(coords); + return true; + } else if (components[1] == "animation_frames_count") { + r_ret = get_tile_animation_frames_count(coords); + return true; + } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[1].trim_prefix("animation_frame_").to_int(); + if (frame < 0 || frame >= get_tile_animation_frames_count(coords)) { + return false; + } + if (components[2] == "duration") { + r_ret = get_tile_animation_frame_duration(coords, frame); + return true; + } + return false; } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) { @@ -3226,6 +3277,40 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { } tile_property_list.push_back(property_info); + // animation_columns. + property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().animation_columns == 0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_separation. + property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().animation_separation == Vector2i()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_speed. + property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().animation_speed == 1.0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_frames_count. + tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK)); + + // animation_frame_*. + bool store_durations = tiles[E_tile->key()].animation_frames_durations.size() >= 2; + for (int i = 0; i < (int)tiles[E_tile->key()].animation_frames_durations.size(); i++) { + property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (!store_durations) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + } + for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { // Add a dummy property to show the alternative exists. tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); @@ -3234,11 +3319,10 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> alternative_property_list; E_alternative->get()->get_property_list(&alternative_property_list); for (PropertyInfo &alternative_property_info : alternative_property_list) { - bool valid; - Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid); + Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name); Variant value = E_alternative->get()->get(alternative_property_info.name); - if (valid && value == default_value) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { + alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE; } alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name); tile_property_list.push_back(alternative_property_info); @@ -3257,33 +3341,27 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector // Create a tile if it does not exists. ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0); ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords)); - } - } + + bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover."); + + // Initialize the tile data. + TileAlternativesData tad; + tad.size_in_atlas = p_size; + tad.animation_frames_durations.push_back(1.0); + tad.alternatives[0] = memnew(TileData); + tad.alternatives[0]->set_tile_set(tile_set); + tad.alternatives[0]->set_allow_transform(false); + tad.alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); + tad.alternatives[0]->notify_property_list_changed(); + tad.alternatives_ids.append(0); // Create and resize the tile. - tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData()); + tiles.insert(p_atlas_coords, tad); tiles_ids.append(p_atlas_coords); tiles_ids.sort(); - tiles[p_atlas_coords].size_in_atlas = p_size; - tiles[p_atlas_coords].alternatives[0] = memnew(TileData); - tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set); - tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false); - tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); - tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed(); - tiles[p_atlas_coords].alternatives_ids.append(0); - - // Add all covered positions to the mapping cache - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = p_atlas_coords; - } - } + _create_coords_mapping_cache(p_atlas_coords); emit_signal(SNAME("changed")); } @@ -3292,14 +3370,7 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); // Remove all covered positions from the mapping cache - Size2i size = tiles[p_atlas_coords].size_in_atlas; - - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); - } - } + _clear_coords_mapping_cache(p_atlas_coords); // Free tile data. for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { @@ -3326,6 +3397,118 @@ Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const { return _coords_mapping_cache[p_atlas_coords]; } +void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_frame_columns < 0); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, p_frame_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + tiles[p_atlas_coords].animation_columns = p_frame_columns; + + _create_coords_mapping_cache(p_atlas_coords); + + emit_signal(SNAME("changed")); +} + +int TileSetAtlasSource::get_tile_animation_columns(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_columns; +} + +void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_separation.x < 0 || p_separation.y < 0); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, p_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + tiles[p_atlas_coords].animation_separation = p_separation; + + _create_coords_mapping_cache(p_atlas_coords); + + emit_signal(SNAME("changed")); +} + +Vector2i TileSetAtlasSource::get_tile_animation_separation(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_separation; +} + +void TileSetAtlasSource::set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_speed <= 0); + + tiles[p_atlas_coords].animation_speed = p_speed; + + emit_signal(SNAME("changed")); +} + +real_t TileSetAtlasSource::get_tile_animation_speed(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1.0, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_speed; +} + +void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_frames_count < 1); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, tad.animation_separation, p_frames_count, p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + int old_size = tiles[p_atlas_coords].animation_frames_durations.size(); + tiles[p_atlas_coords].animation_frames_durations.resize(p_frames_count); + for (int i = old_size; i < p_frames_count; i++) { + tiles[p_atlas_coords].animation_frames_durations[i] = 1.0; + } + + _create_coords_mapping_cache(p_atlas_coords); + + notify_property_list_changed(); + + emit_signal(SNAME("changed")); +} + +int TileSetAtlasSource::get_tile_animation_frames_count(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_frames_durations.size(); +} + +void TileSetAtlasSource::set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_INDEX(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size()); + ERR_FAIL_COND(p_duration <= 0.0); + + tiles[p_atlas_coords].animation_frames_durations[p_frame_index] = p_duration; + + emit_signal(SNAME("changed")); +} + +real_t TileSetAtlasSource::get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size(), 0.0); + return tiles[p_atlas_coords].animation_frames_durations[p_frame_index]; +} + +real_t TileSetAtlasSource::get_tile_animation_total_duration(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + real_t sum = 0.0; + for (int frame = 0; frame < (int)tiles[p_atlas_coords].animation_frames_durations.size(); frame++) { + sum += tiles[p_atlas_coords].animation_frames_durations[frame]; + } + return sum; +} + Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); @@ -3341,16 +3524,46 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) const { return tiles_ids[p_index]; } -Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const { +bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile) const { + if (p_atlas_coords.x < 0 || p_atlas_coords.y < 0) { + return false; + } + if (p_size.x <= 0 || p_size.y <= 0) { + return false; + } + Size2i atlas_grid_size = get_atlas_grid_size(); + for (int frame = 0; frame < p_frames_count; frame++) { + Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < p_size.x; x++) { + for (int y = 0; y < p_size.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] != p_ignored_tile) { + return false; + } + if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) { + if (!(_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] == p_ignored_tile)) { + return false; // Only accept tiles outside the atlas if they are part of the ignored tile. + } + } + } + } + } + return true; +} + +Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i()); + + const TileAlternativesData &tad = tiles[p_atlas_coords]; - Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas; + Vector2i size_in_atlas = tad.size_in_atlas; Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1)); - Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation)); + Vector2i frame_coords = p_atlas_coords + (size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(p_frame % tad.animation_columns, p_frame / tad.animation_columns) : Vector2i(p_frame, 0)); + Vector2 origin = margins + (frame_coords * (texture_region_size + separation)); return Rect2(origin, region_size); - ; } Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const { @@ -3362,63 +3575,29 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_ margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset(); if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { - effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x); - effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y); + effective_texture_offset = effective_texture_offset.clamp(-margin, margin); } return effective_texture_offset; } -bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - - Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) { - return false; - } - - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; - ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false); - - Size2i grid_size = get_atlas_grid_size(); - if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) { - return false; - } - - Rect2i new_rect = Rect2i(new_atlas_coords, size); - // Check if the new tile can fit in the new rect. - for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) { - for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) { - Vector2i coords = get_tile_at_coords(Vector2i(x, y)); - if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) { - return false; - } - } - } - - return true; -} - void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) { - bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size); - ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size)); + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + TileAlternativesData &tad = tiles[p_atlas_coords]; // Compute the actual new rect from arguments. Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; + Vector2i new_size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tad.size_in_atlas; - if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) { + if (new_atlas_coords == p_atlas_coords && new_size == tad.size_in_atlas) { return; } - // Remove all covered positions from the mapping cache. - Size2i old_size = tiles[p_atlas_coords].size_in_atlas; - for (int x = 0; x < old_size.x; x++) { - for (int y = 0; y < old_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); - } - } + bool room_for_tile = has_room_for_tile(new_atlas_coords, new_size, tad.animation_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, vformat("Cannot move tile at position %s with size %s. Tile already present.", new_atlas_coords, new_size)); + + _clear_coords_mapping_cache(p_atlas_coords); // Move the tile and update its size. if (new_atlas_coords != p_atlas_coords) { @@ -3429,15 +3608,9 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ tiles_ids.append(new_atlas_coords); tiles_ids.sort(); } - tiles[new_atlas_coords].size_in_atlas = size; + tiles[new_atlas_coords].size_in_atlas = new_size; - // Add all covered positions to the mapping cache again. - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = new_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = new_atlas_coords; - } - } + _create_coords_mapping_cache(new_atlas_coords); emit_signal(SNAME("changed")); } @@ -3566,12 +3739,25 @@ void TileSetAtlasSource::_bind_methods() { // Base tiles ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1))); ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative - ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); + ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS)); + ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); + ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns); + ClassDB::bind_method(D_METHOD("get_tile_animation_columns", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_columns); + ClassDB::bind_method(D_METHOD("set_tile_animation_separation", "atlas_coords", "separation"), &TileSetAtlasSource::set_tile_animation_separation); + ClassDB::bind_method(D_METHOD("get_tile_animation_separation", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_separation); + ClassDB::bind_method(D_METHOD("set_tile_animation_speed", "atlas_coords", "speed"), &TileSetAtlasSource::set_tile_animation_speed); + ClassDB::bind_method(D_METHOD("get_tile_animation_speed", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_speed); + ClassDB::bind_method(D_METHOD("set_tile_animation_frames_count", "atlas_coords", "frames_count"), &TileSetAtlasSource::set_tile_animation_frames_count); + ClassDB::bind_method(D_METHOD("get_tile_animation_frames_count", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_frames_count); + ClassDB::bind_method(D_METHOD("set_tile_animation_frame_duration", "atlas_coords", "frame_index", "duration"), &TileSetAtlasSource::set_tile_animation_frame_duration); + ClassDB::bind_method(D_METHOD("get_tile_animation_frame_duration", "atlas_coords", "frame_index"), &TileSetAtlasSource::get_tile_animation_frame_duration); + ClassDB::bind_method(D_METHOD("get_tile_animation_total_duration", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_total_duration); + // Alternative tiles ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile); @@ -3584,7 +3770,7 @@ void TileSetAtlasSource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); - ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region); + ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0)); } TileSetAtlasSource::~TileSetAtlasSource() { @@ -3618,6 +3804,45 @@ void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coo }; } +void TileSetAtlasSource::_clear_coords_mapping_cache(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + TileAlternativesData &tad = tiles[p_atlas_coords]; + for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < tad.size_in_atlas.x; x++) { + for (int y = 0; y < tad.size_in_atlas.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (!_coords_mapping_cache.has(coords)) { + WARN_PRINT(vformat("TileSetAtlasSource has no cached tile at position %s, the position cache might be corrupted.", coords)); + } else { + if (_coords_mapping_cache[coords] != p_atlas_coords) { + WARN_PRINT(vformat("The position cache at position %s is pointing to a wrong tile, the position cache might be corrupted.", coords)); + } + _coords_mapping_cache.erase(coords); + } + } + } + } +} + +void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < tad.size_in_atlas.x; x++) { + for (int y = 0; y < tad.size_in_atlas.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (_coords_mapping_cache.has(coords)) { + WARN_PRINT(vformat("The cache already has a tile for position %s, the position cache might be corrupted.", coords)); + } + _coords_mapping_cache[coords] = p_atlas_coords; + } + } + } +} + /////////////////////////////// TileSetScenesCollectionSource ////////////////////////////////////// void TileSetScenesCollectionSource::_compute_next_alternative_id() { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index ba7207241a..46cb1fb36d 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -447,16 +447,23 @@ public: class TileSetAtlasSource : public TileSetSource { GDCLASS(TileSetAtlasSource, TileSetSource); -public: +private: struct TileAlternativesData { Vector2i size_in_atlas = Vector2i(1, 1); Vector2i texture_offset; + + // Animation + int animation_columns = 0; + Vector2i animation_separation; + real_t animation_speed = 1.0; + LocalVector<real_t> animation_frames_durations; + + // Alternatives Map<int, TileData *> alternatives; Vector<int> alternatives_ids; int next_alternative_id = 1; }; -private: Ref<Texture2D> texture; Vector2i margins; Vector2i separation; @@ -471,6 +478,9 @@ private: void _compute_next_alternative_id(const Vector2i p_atlas_coords); + void _create_coords_mapping_cache(Vector2i p_atlas_coords); + void _clear_coords_mapping_cache(Vector2i p_atlas_coords); + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -513,18 +523,32 @@ public: Vector2i get_texture_region_size() const; // Base tiles. - void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does. - void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative + void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); + void remove_tile(Vector2i p_atlas_coords); virtual bool has_tile(Vector2i p_atlas_coords) const override; - bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const; void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)); Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const; virtual int get_tiles_count() const override; virtual Vector2i get_tile_id(int p_index) const override; + bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const; + Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const; + // Animation. + void set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns); + int get_tile_animation_columns(const Vector2i p_atlas_coords) const; + void set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation); + Vector2i get_tile_animation_separation(const Vector2i p_atlas_coords) const; + void set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed); + real_t get_tile_animation_speed(const Vector2i p_atlas_coords) const; + void set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count); + int get_tile_animation_frames_count(const Vector2i p_atlas_coords) const; + void set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration); + real_t get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const; + real_t get_tile_animation_total_duration(const Vector2i p_atlas_coords) const; + // Alternative tiles. int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1); void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile); @@ -542,7 +566,7 @@ public: Vector2i get_atlas_grid_size() const; bool has_tiles_outside_texture(); void clear_tiles_outside_texture(); - Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const; + Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const; Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const; ~TileSetAtlasSource(); diff --git a/scene/resources/world_margin_shape_2d.cpp b/scene/resources/world_boundary_shape_2d.cpp index 3b43681528..39af92793f 100644 --- a/scene/resources/world_margin_shape_2d.cpp +++ b/scene/resources/world_boundary_shape_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* world_margin_shape_2d.cpp */ +/* world_boundary_shape_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "world_margin_shape_2d.h" +#include "world_boundary_shape_2d.h" #include "core/math/geometry_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" -bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { +bool WorldBoundaryShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { Vector2 point = get_distance() * get_normal(); Vector2 l[2][2] = { { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }, { point, point + get_normal() * 30 } }; @@ -48,7 +48,7 @@ bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, doubl return false; } -void WorldMarginShape2D::_update_shape() { +void WorldBoundaryShape2D::_update_shape() { Array arr; arr.push_back(normal); arr.push_back(distance); @@ -56,25 +56,25 @@ void WorldMarginShape2D::_update_shape() { emit_changed(); } -void WorldMarginShape2D::set_normal(const Vector2 &p_normal) { +void WorldBoundaryShape2D::set_normal(const Vector2 &p_normal) { normal = p_normal; _update_shape(); } -void WorldMarginShape2D::set_distance(real_t p_distance) { +void WorldBoundaryShape2D::set_distance(real_t p_distance) { distance = p_distance; _update_shape(); } -Vector2 WorldMarginShape2D::get_normal() const { +Vector2 WorldBoundaryShape2D::get_normal() const { return normal; } -real_t WorldMarginShape2D::get_distance() const { +real_t WorldBoundaryShape2D::get_distance() const { return distance; } -void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) { +void WorldBoundaryShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector2 point = get_distance() * get_normal(); Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }; @@ -83,7 +83,7 @@ void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) { RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3); } -Rect2 WorldMarginShape2D::get_rect() const { +Rect2 WorldBoundaryShape2D::get_rect() const { Vector2 point = get_distance() * get_normal(); Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }; @@ -96,22 +96,22 @@ Rect2 WorldMarginShape2D::get_rect() const { return rect; } -real_t WorldMarginShape2D::get_enclosing_radius() const { +real_t WorldBoundaryShape2D::get_enclosing_radius() const { return distance; } -void WorldMarginShape2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldMarginShape2D::set_normal); - ClassDB::bind_method(D_METHOD("get_normal"), &WorldMarginShape2D::get_normal); +void WorldBoundaryShape2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldBoundaryShape2D::set_normal); + ClassDB::bind_method(D_METHOD("get_normal"), &WorldBoundaryShape2D::get_normal); - ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldMarginShape2D::set_distance); - ClassDB::bind_method(D_METHOD("get_distance"), &WorldMarginShape2D::get_distance); + ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldBoundaryShape2D::set_distance); + ClassDB::bind_method(D_METHOD("get_distance"), &WorldBoundaryShape2D::get_distance); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance"); } -WorldMarginShape2D::WorldMarginShape2D() : - Shape2D(PhysicsServer2D::get_singleton()->world_margin_shape_create()) { +WorldBoundaryShape2D::WorldBoundaryShape2D() : + Shape2D(PhysicsServer2D::get_singleton()->world_boundary_shape_create()) { _update_shape(); } diff --git a/scene/resources/world_margin_shape_2d.h b/scene/resources/world_boundary_shape_2d.h index 3c1d593ffe..4cc60f5985 100644 --- a/scene/resources/world_margin_shape_2d.h +++ b/scene/resources/world_boundary_shape_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* world_margin_shape_2d.h */ +/* world_boundary_shape_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef WORLD_MARGIN_SHAPE_2D_H -#define WORLD_MARGIN_SHAPE_2D_H +#ifndef WORLD_BOUNDARY_SHAPE_2D_H +#define WORLD_BOUNDARY_SHAPE_2D_H #include "scene/resources/shape_2d.h" -class WorldMarginShape2D : public Shape2D { - GDCLASS(WorldMarginShape2D, Shape2D); +class WorldBoundaryShape2D : public Shape2D { + GDCLASS(WorldBoundaryShape2D, Shape2D); - // WorldMarginShape2D is often used for one-way platforms, where the normal pointing up makes sense. + // WorldBoundaryShape2D is often used for one-way platforms, where the normal pointing up makes sense. Vector2 normal = Vector2(0, -1); real_t distance = 0.0; @@ -58,7 +58,7 @@ public: virtual Rect2 get_rect() const override; virtual real_t get_enclosing_radius() const override; - WorldMarginShape2D(); + WorldBoundaryShape2D(); }; -#endif // WORLD_MARGIN_SHAPE_2D_H +#endif // WORLD_BOUNDARY_SHAPE_2D_H diff --git a/scene/resources/world_margin_shape_3d.cpp b/scene/resources/world_boundary_shape_3d.cpp index 28d50e1921..8cde537164 100644 --- a/scene/resources/world_margin_shape_3d.cpp +++ b/scene/resources/world_boundary_shape_3d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* world_margin_shape_3d.cpp */ +/* world_boundary_shape_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "world_margin_shape_3d.h" +#include "world_boundary_shape_3d.h" #include "servers/physics_server_3d.h" -Vector<Vector3> WorldMarginShape3D::get_debug_mesh_lines() const { +Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const { Plane p = get_plane(); Vector<Vector3> points; @@ -60,29 +60,29 @@ Vector<Vector3> WorldMarginShape3D::get_debug_mesh_lines() const { return points; } -void WorldMarginShape3D::_update_shape() { +void WorldBoundaryShape3D::_update_shape() { PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), plane); Shape3D::_update_shape(); } -void WorldMarginShape3D::set_plane(Plane p_plane) { +void WorldBoundaryShape3D::set_plane(const Plane &p_plane) { plane = p_plane; _update_shape(); notify_change_to_owners(); } -Plane WorldMarginShape3D::get_plane() const { +const Plane &WorldBoundaryShape3D::get_plane() const { return plane; } -void WorldMarginShape3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_plane", "plane"), &WorldMarginShape3D::set_plane); - ClassDB::bind_method(D_METHOD("get_plane"), &WorldMarginShape3D::get_plane); +void WorldBoundaryShape3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_plane", "plane"), &WorldBoundaryShape3D::set_plane); + ClassDB::bind_method(D_METHOD("get_plane"), &WorldBoundaryShape3D::get_plane); ADD_PROPERTY(PropertyInfo(Variant::PLANE, "plane"), "set_plane", "get_plane"); } -WorldMarginShape3D::WorldMarginShape3D() : - Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_PLANE)) { +WorldBoundaryShape3D::WorldBoundaryShape3D() : + Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_WORLD_BOUNDARY)) { set_plane(Plane(0, 1, 0, 0)); } diff --git a/scene/resources/world_margin_shape_3d.h b/scene/resources/world_boundary_shape_3d.h index 00417c4408..853f555ebc 100644 --- a/scene/resources/world_margin_shape_3d.h +++ b/scene/resources/world_boundary_shape_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* world_margin_shape_3d.h */ +/* world_boundary_shape_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef WORLD_MARGIN_SHAPE_3D_H -#define WORLD_MARGIN_SHAPE_3D_H +#ifndef WORLD_BOUNDARY_SHAPE_3D_H +#define WORLD_BOUNDARY_SHAPE_3D_H #include "scene/resources/shape_3d.h" -class WorldMarginShape3D : public Shape3D { - GDCLASS(WorldMarginShape3D, Shape3D); +class WorldBoundaryShape3D : public Shape3D { + GDCLASS(WorldBoundaryShape3D, Shape3D); Plane plane; protected: @@ -42,8 +42,8 @@ protected: virtual void _update_shape() override; public: - void set_plane(Plane p_plane); - Plane get_plane() const; + void set_plane(const Plane &p_plane); + const Plane &get_plane() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; virtual real_t get_enclosing_radius() const override { @@ -51,6 +51,6 @@ public: return 0; } - WorldMarginShape3D(); + WorldBoundaryShape3D(); }; -#endif // WORLD_MARGIN_SHAPE_H +#endif // WORLD_BOUNDARY_SHAPE_H diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 5d89d295c2..b283749ffa 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -91,9 +91,6 @@ SceneStringNames::SceneStringNames() { update = StaticCString::create("update"); updated = StaticCString::create("updated"); - _get_gizmo_geometry = StaticCString::create("_get_gizmo_geometry"); - _can_gizmo_scale = StaticCString::create("_can_gizmo_scale"); - _physics_process = StaticCString::create("_physics_process"); _process = StaticCString::create("_process"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 01f427ecd1..2923351eab 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -111,9 +111,6 @@ public: StringName _body_inout; StringName _area_inout; - StringName _get_gizmo_geometry; - StringName _can_gizmo_scale; - StringName _physics_process; StringName _process; StringName _enter_world; |