diff options
Diffstat (limited to 'scene')
27 files changed, 460 insertions, 258 deletions
diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index c4b2608812..48817679bc 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -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 - RigidDynamicBody2D::Mode rigid_mode = get_mode(); - if (rigid_mode == RigidDynamicBody2D::MODE_STATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_KINEMATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); - } else if (rigid_mode == RigidDynamicBody2D::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); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index c3dc9ab92b..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,7 +57,10 @@ 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)) { + // 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(); @@ -133,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() { @@ -430,7 +436,7 @@ void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsD 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()); } @@ -524,29 +530,60 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) } } -void RigidDynamicBody2D::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(); +} + +bool RigidDynamicBody2D::is_freeze_enabled() const { + return freeze; +} + +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::Mode RigidDynamicBody2D::get_mode() const { - return mode; +RigidDynamicBody2D::FreezeMode RigidDynamicBody2D::get_freeze_mode() const { + return freeze_mode; } void RigidDynamicBody2D::set_mass(real_t p_mass) { @@ -844,17 +881,14 @@ TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const { 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 RigidDynamicBody2D (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 RigidDynamicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody2D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody2D::get_mode); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody2D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody2D::get_mass); @@ -918,11 +952,19 @@ void RigidDynamicBody2D::_bind_methods() { 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_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_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("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"); @@ -936,6 +978,9 @@ void RigidDynamicBody2D::_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"); @@ -952,10 +997,8 @@ void RigidDynamicBody2D::_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); @@ -1090,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); @@ -1111,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; @@ -1240,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; @@ -1274,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; } @@ -1568,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 5d0d98a2df..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; @@ -117,11 +117,9 @@ 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; @@ -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; @@ -290,7 +298,7 @@ private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody2D::Mode); +VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode); @@ -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); - real_t 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/tile_map.cpp b/scene/2d/tile_map.cpp index 03db9c0d32..929233e4e0 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -2034,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; diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index c34c150145..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.")); } } diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index c79f956642..4e496fba47 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -124,8 +124,7 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const { if (shape.is_valid() && Object::cast_to<RigidDynamicBody3D>(get_parent()) && - Object::cast_to<ConcavePolygonShape3D>(*shape) && - Object::cast_to<RigidDynamicBody3D>(get_parent())->get_mode() != RigidDynamicBody3D::MODE_STATIC) { + Object::cast_to<ConcavePolygonShape3D>(*shape)) { warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static.")); } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 48da186860..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", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1)); + 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); @@ -97,7 +97,10 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { 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, p_max_collisions)) { + // 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(); @@ -177,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, p_max_collisions); + // 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) { @@ -599,27 +605,60 @@ void RigidDynamicBody3D::_notification(int p_what) { #endif } -void RigidDynamicBody3D::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; } - update_configuration_warnings(); + + lock_rotation = p_lock_rotation; + _apply_body_mode(); } -RigidDynamicBody3D::Mode RigidDynamicBody3D::get_mode() const { - return mode; +bool RigidDynamicBody3D::is_lock_rotation_enabled() const { + return lock_rotation; +} + +void RigidDynamicBody3D::set_freeze_enabled(bool p_freeze) { + if (p_freeze == freeze) { + return; + } + + freeze = p_freeze; + _apply_body_mode(); +} + +bool RigidDynamicBody3D::is_freeze_enabled() const { + return freeze; +} + +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) { @@ -892,17 +931,14 @@ TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const { 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 RigidDynamicBody (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 RigidDynamicBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody3D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody3D::get_mode); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody3D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody3D::get_mass); @@ -963,11 +999,19 @@ void RigidDynamicBody3D::_bind_methods() { 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_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_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("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"); @@ -981,6 +1025,9 @@ void RigidDynamicBody3D::_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"); @@ -994,10 +1041,8 @@ void RigidDynamicBody3D::_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); @@ -1146,8 +1191,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (collision_state.floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { Transform3D gt = get_global_transform(); - real_t travel_total = result.travel.length(); - if (travel_total <= margin + CMP_EPSILON) { + if (result.travel.length() <= margin + CMP_EPSILON) { gt.origin -= result.travel; } set_global_transform(gt); @@ -1186,7 +1230,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo 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) { + 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); @@ -1377,7 +1421,9 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) return; } + // 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)) { @@ -1403,12 +1449,15 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) } bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_zero_approx(floor_snap_length) || up_direction == Vector3() || collision_state.floor || !was_on_floor || 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 * floor_snap_length, result, margin, true, 4, false, true)) { + 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()); diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 96f3d7d747..a53147cb8f 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -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; @@ -133,11 +133,9 @@ 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; @@ -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; @@ -298,7 +308,7 @@ private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody3D::Mode); +VARIANT_ENUM_CAST(RigidDynamicBody3D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody3D::CenterOfMassMode); class KinematicCollision3D; @@ -364,8 +374,8 @@ private: }; CollisionState collision_state; - 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 = 6; 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/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index bc3bb81ed4..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; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index bca3b2059d..d9c08ec272 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1071,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)); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index d9acbeb828..89fd9bfc88 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; } @@ -1555,6 +1560,20 @@ void LineEdit::deselect() { update(); } +bool LineEdit::has_selection() const { + return selection.enabled; +} + +int LineEdit::get_selection_from_column() const { + ERR_FAIL_COND_V(!selection.enabled, -1); + return selection.begin; +} + +int LineEdit::get_selection_to_column() const { + ERR_FAIL_COND_V(!selection.enabled, -1); + return selection.end; +} + void LineEdit::selection_delete() { if (selection.enabled) { delete_text(selection.begin, selection.end); @@ -2089,6 +2108,9 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect); + ClassDB::bind_method(D_METHOD("has_selection"), &LineEdit::has_selection); + ClassDB::bind_method(D_METHOD("get_selection_from_column"), &LineEdit::get_selection_from_column); + ClassDB::bind_method(D_METHOD("get_selection_to_column"), &LineEdit::get_selection_to_column); ClassDB::bind_method(D_METHOD("set_text", "text"), &LineEdit::set_text); ClassDB::bind_method(D_METHOD("get_text"), &LineEdit::get_text); ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &LineEdit::get_draw_control_chars); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index e364a79c83..923024dd56 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; @@ -229,6 +231,9 @@ public: void select_all(); void selection_delete(); void deselect(); + bool has_selection() const; + int get_selection_from_column() const; + int get_selection_to_column() const; void delete_char(); void delete_text(int p_from_column, int p_to_column); diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 7faa9aca2c..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; } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 09899413f2..5b13e1da0b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -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; @@ -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; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 2be59ca04d..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,8 +156,9 @@ 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); - cached_minimum_size_dirty = true; } TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { @@ -167,22 +169,27 @@ 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); - cached_minimum_size_dirty = true; } 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); - cached_minimum_size_dirty = true; } bool TreeItem::is_checked(int p_column) const { @@ -214,8 +221,10 @@ 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); - cached_minimum_size_dirty = true; } String TreeItem::get_text(int p_column) const { @@ -231,7 +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); } - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } Control::TextDirection TreeItem::get_text_direction(int p_column) const { @@ -241,10 +250,12 @@ 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); - cached_minimum_size_dirty = true; } void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_value) { @@ -253,8 +264,9 @@ 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); - cached_minimum_size_dirty = true; } } @@ -269,11 +281,13 @@ 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); - cached_minimum_size_dirty = true; } } @@ -284,10 +298,12 @@ 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); - cached_minimum_size_dirty = true; } Array TreeItem::get_structured_text_bidi_override_options(int p_column) const { @@ -297,11 +313,13 @@ 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); - cached_minimum_size_dirty = true; } } @@ -312,10 +330,11 @@ 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); - cached_minimum_size_dirty = true; } String TreeItem::get_suffix(int p_column) const { @@ -325,9 +344,11 @@ 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); - cached_minimum_size_dirty = true; } Ref<Texture2D> TreeItem::get_icon(int p_column) const { @@ -337,9 +358,11 @@ 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); - cached_minimum_size_dirty = true; } Rect2 TreeItem::get_icon_region(int p_column) const { @@ -360,9 +383,11 @@ 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); - cached_minimum_size_dirty = true; } int TreeItem::get_icon_max_width(int p_column) const { @@ -474,8 +499,11 @@ 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(); - cached_minimum_size_dirty = true; } int TreeItem::get_custom_minimum_height() const { @@ -799,8 +827,9 @@ 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); - cached_minimum_size_dirty = true; } int TreeItem::get_button_count(int p_column) const { @@ -843,8 +872,9 @@ 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); - cached_minimum_size_dirty = true; } void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) { @@ -859,8 +889,9 @@ 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); - cached_minimum_size_dirty = true; } bool TreeItem::is_button_disabled(int p_column, int p_idx) const { @@ -872,9 +903,11 @@ 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); - cached_minimum_size_dirty = true; } bool TreeItem::is_editable(int p_column) { @@ -906,8 +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; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } Ref<Font> TreeItem::get_custom_font(int p_column) const { @@ -917,8 +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; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } int TreeItem::get_custom_font_size(int p_column) const { @@ -961,8 +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; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } bool TreeItem::is_custom_set_as_button(int p_column) const { @@ -972,9 +1008,11 @@ 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); - cached_minimum_size_dirty = true; } TreeItem::TextAlign TreeItem::get_text_align(int p_column) const { @@ -984,9 +1022,11 @@ 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); - cached_minimum_size_dirty = true; } bool TreeItem::get_expand_right(int p_column) const { @@ -996,8 +1036,11 @@ 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); - cached_minimum_size_dirty = true; } bool TreeItem::is_folding_disabled() const { @@ -1009,14 +1052,12 @@ Size2 TreeItem::get_minimum_size(int p_column) { Tree *tree = get_tree(); ERR_FAIL_COND_V(!tree, Size2()); - if (cached_minimum_size_dirty) { - 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); @@ -1052,11 +1093,11 @@ Size2 TreeItem::get_minimum_size(int p_column) { size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; } - cached_minimum_size = size; - cached_minimum_size_dirty = false; + cells.write[p_column].cached_minimum_size = size; + cells.write[p_column].cached_minimum_size_dirty = false; } - return cached_minimum_size; + return cell.cached_minimum_size; } Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -1337,10 +1378,6 @@ void Tree::update_cache() { cache.title_button_color = get_theme_color(SNAME("title_button_color")); v_scroll->set_custom_step(cache.font->get_height(cache.font_size)); - - for (TreeItem *item = get_root(); item; item = item->get_next()) { - item->cached_minimum_size_dirty = true; - } } int Tree::compute_item_height(TreeItem *p_item) const { @@ -4001,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) { @@ -4029,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); } } @@ -4069,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 85fed941dc..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; @@ -130,9 +133,6 @@ private: bool disable_folding = false; int custom_min_height = 0; - Size2i cached_minimum_size; - bool cached_minimum_size_dirty = true; - TreeItem *parent = nullptr; // parent item TreeItem *prev = nullptr; // previous in list TreeItem *next = nullptr; // next in list 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_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 67cbd0e094..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); @@ -3520,6 +3525,13 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) 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++) { @@ -3528,6 +3540,11 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s 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. + } + } } } } @@ -3558,8 +3575,7 @@ 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; |