diff options
Diffstat (limited to 'scene')
37 files changed, 830 insertions, 638 deletions
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 8d8b187445..30f012c7aa 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -165,21 +165,13 @@ void PhysicsBody2D::remove_collision_exception_with(Node *p_node) { void StaticBody2D::set_constant_linear_velocity(const Vector2 &p_vel) { constant_linear_velocity = p_vel; - if (kinematic_motion) { - _update_kinematic_motion(); - } else { - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity); - } + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity); } void StaticBody2D::set_constant_angular_velocity(real_t p_vel) { constant_angular_velocity = p_vel; - if (kinematic_motion) { - _update_kinematic_motion(); - } else { - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity); - } + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity); } Vector2 StaticBody2D::get_constant_linear_velocity() const { @@ -209,81 +201,83 @@ Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const { return physics_material_override; } -void StaticBody2D::set_kinematic_motion_enabled(bool p_enabled) { - if (p_enabled == kinematic_motion) { - return; - } - - kinematic_motion = p_enabled; +void StaticBody2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody2D::set_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity); + ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity); - if (kinematic_motion) { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); - } else { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); - } + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override); -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - return; - } -#endif + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); +} - _update_kinematic_motion(); +StaticBody2D::StaticBody2D(PhysicsServer2D::BodyMode p_mode) : + PhysicsBody2D(p_mode) { } -bool StaticBody2D::is_kinematic_motion_enabled() const { - return kinematic_motion; +void StaticBody2D::_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); + } else { + PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } } -void StaticBody2D::set_sync_to_physics(bool p_enable) { +void AnimatableBody2D::set_sync_to_physics(bool p_enable) { if (sync_to_physics == p_enable) { return; } sync_to_physics = p_enable; + _update_kinematic_motion(); +} + +bool AnimatableBody2D::is_sync_to_physics_enabled() const { + return sync_to_physics; +} + +void AnimatableBody2D::_update_kinematic_motion() { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); return; } #endif - if (kinematic_motion) { - _update_kinematic_motion(); + if (sync_to_physics) { + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); + set_only_update_transform_changes(true); + set_notify_local_transform(true); + } else { + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); + set_only_update_transform_changes(false); + set_notify_local_transform(false); } } -bool StaticBody2D::is_sync_to_physics_enabled() const { - return sync_to_physics; +void AnimatableBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { + AnimatableBody2D *body = (AnimatableBody2D *)p_instance; + body->_body_state_changed(p_state); } -void StaticBody2D::_direct_state_changed(Object *p_state) { +void AnimatableBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { if (!sync_to_physics) { return; } - PhysicsDirectBodyState2D *state = Object::cast_to<PhysicsDirectBodyState2D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument"); - - last_valid_transform = state->get_transform(); + last_valid_transform = p_state->get_transform(); set_notify_local_transform(false); set_global_transform(last_valid_transform); set_notify_local_transform(true); } -TypedArray<String> StaticBody2D::get_configuration_warnings() const { - TypedArray<String> warnings = PhysicsBody2D::get_configuration_warnings(); - - if (sync_to_physics && !kinematic_motion) { - warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled.")); - } - - return warnings; -} - -void StaticBody2D::_notification(int p_what) { +void AnimatableBody2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { last_valid_transform = get_global_transform(); @@ -293,10 +287,6 @@ void StaticBody2D::_notification(int p_what) { // Used by sync to physics, send the new transform to the physics... Transform2D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); - new_transform.translate(constant_linear_velocity * delta_time); - new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); // ... but then revert changes. @@ -304,98 +294,19 @@ void StaticBody2D::_notification(int p_what) { set_global_transform(last_valid_transform); set_notify_local_transform(true); } break; - - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - - ERR_FAIL_COND(!kinematic_motion); - - Transform2D new_transform = get_global_transform(); - - real_t delta_time = get_physics_process_delta_time(); - new_transform.translate(constant_linear_velocity * delta_time); - new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); - - if (sync_to_physics) { - // Propagate transform change to node. - set_global_transform(new_transform); - } else { - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); - - // Propagate transform change to node. - set_block_transform_notify(true); - set_global_transform(new_transform); - set_block_transform_notify(false); - } - } break; } } -void StaticBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody2D::set_constant_linear_velocity); - ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity); - ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity); - ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity); - - ClassDB::bind_method(D_METHOD("set_kinematic_motion_enabled", "enabled"), &StaticBody2D::set_kinematic_motion_enabled); - ClassDB::bind_method(D_METHOD("is_kinematic_motion_enabled"), &StaticBody2D::is_kinematic_motion_enabled); - - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override); - ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override); - - ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody2D::set_sync_to_physics); - ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody2D::is_sync_to_physics_enabled); +void AnimatableBody2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &AnimatableBody2D::set_sync_to_physics); + ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &AnimatableBody2D::is_sync_to_physics_enabled); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); } -StaticBody2D::StaticBody2D() : - PhysicsBody2D(PhysicsServer2D::BODY_MODE_STATIC) { -} - -void StaticBody2D::_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); - } else { - PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); - PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); - } -} - -void StaticBody2D::_update_kinematic_motion() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - - if (kinematic_motion && sync_to_physics) { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody2D::_direct_state_changed)); - set_only_update_transform_changes(true); - set_notify_local_transform(true); - } else { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); - set_only_update_transform_changes(false); - set_notify_local_transform(false); - } - - bool needs_physics_process = false; - if (kinematic_motion) { - if (!Math::is_zero_approx(constant_angular_velocity) || !constant_linear_velocity.is_equal_approx(Vector2())) { - needs_physics_process = true; - } - } - - set_physics_process_internal(needs_physics_process); +AnimatableBody2D::AnimatableBody2D() : + StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) { + _update_kinematic_motion(); } void RigidBody2D::_body_enter_tree(ObjectID p_id) { @@ -511,26 +422,26 @@ struct _RigidBody2DInOut { int local_shape = 0; }; -void RigidBody2D::_direct_state_changed(Object *p_state) { -#ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState2D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument"); -#else - state = (PhysicsDirectBodyState2D *)p_state; //trust it -#endif +void RigidBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { + RigidBody2D *body = (RigidBody2D *)p_instance; + body->_body_state_changed(p_state); +} +void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { set_block_transform_notify(true); // don't want notify (would feedback loop) if (mode != MODE_KINEMATIC) { - set_global_transform(state->get_transform()); + set_global_transform(p_state->get_transform()); } - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); - if (sleeping != state->is_sleeping()) { - sleeping = state->is_sleeping(); + + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); + + if (sleeping != p_state->is_sleeping()) { + sleeping = p_state->is_sleeping(); emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed); } - GDVIRTUAL_CALL(_integrate_forces, state); + GDVIRTUAL_CALL(_integrate_forces, p_state); set_block_transform_notify(false); // want it back @@ -546,20 +457,18 @@ void RigidBody2D::_direct_state_changed(Object *p_state) { } } - _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(state->get_contact_count() * sizeof(_RigidBody2DInOut)); + _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBody2DInOut)); int toadd_count = 0; //state->get_contact_count(); RigidBody2D_RemoveAction *toremove = (RigidBody2D_RemoveAction *)alloca(rc * sizeof(RigidBody2D_RemoveAction)); int toremove_count = 0; //put the ones to add - for (int i = 0; i < state->get_contact_count(); i++) { - RID rid = state->get_contact_collider(i); - ObjectID obj = state->get_contact_collider_id(i); - int local_shape = state->get_contact_local_shape(i); - int shape = state->get_contact_collider_shape(i); - - //bool found=false; + for (int i = 0; i < p_state->get_contact_count(); i++) { + RID rid = p_state->get_contact_collider(i); + ObjectID obj = p_state->get_contact_collider_id(i); + int local_shape = p_state->get_contact_local_shape(i); + int shape = p_state->get_contact_collider_shape(i); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj); if (!E) { @@ -612,8 +521,6 @@ void RigidBody2D::_direct_state_changed(Object *p_state) { contact_monitor->locked = false; } - - state = nullptr; } void RigidBody2D::set_mode(Mode p_mode) { @@ -653,11 +560,53 @@ real_t RigidBody2D::get_mass() const { void RigidBody2D::set_inertia(real_t p_inertia) { ERR_FAIL_COND(p_inertia < 0); - PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, p_inertia); + inertia = p_inertia; + PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia); } real_t RigidBody2D::get_inertia() const { - return PhysicsServer2D::get_singleton()->body_get_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA); + return inertia; +} + +void RigidBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) { + if (center_of_mass_mode == p_mode) { + return; + } + + center_of_mass_mode = p_mode; + + switch (center_of_mass_mode) { + case CENTER_OF_MASS_MODE_AUTO: { + center_of_mass = Vector2(); + PhysicsServer2D::get_singleton()->body_reset_mass_properties(get_rid()); + if (inertia != 0.0) { + PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia); + } + } break; + + case CENTER_OF_MASS_MODE_CUSTOM: { + PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass); + } break; + } +} + +RigidBody2D::CenterOfMassMode RigidBody2D::get_center_of_mass_mode() const { + return center_of_mass_mode; +} + +void RigidBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) { + if (center_of_mass == p_center_of_mass) { + return; + } + + ERR_FAIL_COND(center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM); + center_of_mass = 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 { + return center_of_mass; } void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { @@ -709,25 +658,15 @@ real_t RigidBody2D::get_angular_damp() const { } void RigidBody2D::set_axis_velocity(const Vector2 &p_axis) { - Vector2 v = state ? state->get_linear_velocity() : linear_velocity; Vector2 axis = p_axis.normalized(); - v -= axis * axis.dot(v); - v += p_axis; - if (state) { - set_linear_velocity(v); - } else { - PhysicsServer2D::get_singleton()->body_set_axis_velocity(get_rid(), p_axis); - linear_velocity = v; - } + 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) { linear_velocity = p_velocity; - if (state) { - state->set_linear_velocity(linear_velocity); - } else { - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); - } + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } Vector2 RigidBody2D::get_linear_velocity() const { @@ -736,11 +675,7 @@ Vector2 RigidBody2D::get_linear_velocity() const { void RigidBody2D::set_angular_velocity(real_t p_velocity) { angular_velocity = p_velocity; - if (state) { - state->set_angular_velocity(angular_velocity); - } else { - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); - } + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } real_t RigidBody2D::get_angular_velocity() const { @@ -925,6 +860,12 @@ void RigidBody2D::_bind_methods() { 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_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_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_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); @@ -981,8 +922,11 @@ void RigidBody2D::_bind_methods() { GDVIRTUAL_BIND(_integrate_forces, "state"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp", PROPERTY_USAGE_NONE), "set_inertia", "get_inertia"); + 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"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater"), "set_center_of_mass", "get_center_of_mass"); + ADD_LINKED_PROPERTY("center_of_mass_mode", "center_of_mass"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); @@ -1012,14 +956,25 @@ void RigidBody2D::_bind_methods() { BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); BIND_ENUM_CONSTANT(MODE_KINEMATIC); + BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); + BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); + BIND_ENUM_CONSTANT(CCD_MODE_DISABLED); BIND_ENUM_CONSTANT(CCD_MODE_CAST_RAY); BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE); } +void RigidBody2D::_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; + } + } +} + RigidBody2D::RigidBody2D() : PhysicsBody2D(PhysicsServer2D::BODY_MODE_DYNAMIC) { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody2D::_direct_state_changed)); + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); } RigidBody2D::~RigidBody2D() { @@ -1045,14 +1000,19 @@ void RigidBody2D::_reload_physics_characteristics() { bool CharacterBody2D::move_and_slide() { // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. - float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); Vector2 current_platform_velocity = platform_velocity; if ((on_floor || on_wall) && platform_rid.is_valid()) { - bool excluded = (moving_platform_ignore_layers & platform_layer) != 0; + bool excluded = false; + if (on_floor) { + excluded = (moving_platform_floor_layers & platform_layer) == 0; + } else if (on_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. + //this approach makes sure there is less delay between the actual body velocity and the one we saved PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid); if (bs) { Transform2D gt = get_global_transform(); @@ -1095,7 +1055,7 @@ bool CharacterBody2D::move_and_slide() { return motion_results.size() > 0; } -void CharacterBody2D::_move_and_slide_grounded(real_t p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) { +void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) { Vector2 motion = linear_velocity * p_delta; Vector2 motion_slide_up = motion.slide(up_direction); @@ -1239,7 +1199,7 @@ void CharacterBody2D::_move_and_slide_grounded(real_t p_delta, bool p_was_on_flo } } -void CharacterBody2D::_move_and_slide_free(real_t p_delta) { +void CharacterBody2D::_move_and_slide_free(double p_delta) { Vector2 motion = linear_velocity * p_delta; platform_rid = RID(); @@ -1469,12 +1429,20 @@ void CharacterBody2D::set_slide_on_ceiling_enabled(bool p_enabled) { slide_on_ceiling = p_enabled; } -uint32_t CharacterBody2D::get_moving_platform_ignore_layers() const { - return moving_platform_ignore_layers; +uint32_t CharacterBody2D::get_moving_platform_floor_layers() const { + return moving_platform_floor_layers; +} + +void CharacterBody2D::set_moving_platform_floor_layers(uint32_t p_exclude_layers) { + moving_platform_floor_layers = p_exclude_layers; +} + +uint32_t CharacterBody2D::get_moving_platform_wall_layers() const { + return moving_platform_wall_layers; } -void CharacterBody2D::set_moving_platform_ignore_layers(uint32_t p_exclude_layers) { - moving_platform_ignore_layers = p_exclude_layers; +void CharacterBody2D::set_moving_platform_wall_layers(uint32_t p_exclude_layers) { + moving_platform_wall_layers = p_exclude_layers; } void CharacterBody2D::set_motion_mode(MotionMode p_mode) { @@ -1559,8 +1527,10 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody2D::set_slide_on_ceiling_enabled); ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody2D::is_slide_on_ceiling_enabled); - ClassDB::bind_method(D_METHOD("set_moving_platform_ignore_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_ignore_layers); - ClassDB::bind_method(D_METHOD("get_moving_platform_ignore_layers"), &CharacterBody2D::get_moving_platform_ignore_layers); + ClassDB::bind_method(D_METHOD("set_moving_platform_floor_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_floor_layers); + ClassDB::bind_method(D_METHOD("get_moving_platform_floor_layers"), &CharacterBody2D::get_moving_platform_floor_layers); + ClassDB::bind_method(D_METHOD("set_moving_platform_wall_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_wall_layers); + ClassDB::bind_method(D_METHOD("get_moving_platform_wall_layers"), &CharacterBody2D::get_moving_platform_wall_layers); ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody2D::get_max_slides); ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides); @@ -1602,7 +1572,8 @@ void CharacterBody2D::_bind_methods() { 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_GROUP("Moving platform", "moving_platform"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_ignore_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_ignore_layers", "get_moving_platform_ignore_layers"); + 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); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 885f0ace05..1d6437a3ad 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -63,20 +63,13 @@ public: class StaticBody2D : public PhysicsBody2D { GDCLASS(StaticBody2D, PhysicsBody2D); +private: Vector2 constant_linear_velocity; real_t constant_angular_velocity = 0.0; Ref<PhysicsMaterial> physics_material_override; - bool kinematic_motion = false; - bool sync_to_physics = false; - - Transform2D last_valid_transform; - - void _direct_state_changed(Object *p_state); - protected: - void _notification(int p_what); static void _bind_methods(); public: @@ -89,17 +82,32 @@ public: Vector2 get_constant_linear_velocity() const; real_t get_constant_angular_velocity() const; - virtual TypedArray<String> get_configuration_warnings() const override; - - StaticBody2D(); + StaticBody2D(PhysicsServer2D::BodyMode p_mode = PhysicsServer2D::BODY_MODE_STATIC); private: void _reload_physics_characteristics(); +}; - void _update_kinematic_motion(); +class AnimatableBody2D : public StaticBody2D { + GDCLASS(AnimatableBody2D, StaticBody2D); + +private: + bool sync_to_physics = false; + + Transform2D last_valid_transform; + + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state); + void _body_state_changed(PhysicsDirectBodyState2D *p_state); - void set_kinematic_motion_enabled(bool p_enabled); - bool is_kinematic_motion_enabled() const; +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + AnimatableBody2D(); + +private: + void _update_kinematic_motion(); void set_sync_to_physics(bool p_enable); bool is_sync_to_physics_enabled() const; @@ -116,6 +124,11 @@ public: MODE_KINEMATIC, }; + enum CenterOfMassMode { + CENTER_OF_MASS_MODE_AUTO, + CENTER_OF_MASS_MODE_CUSTOM, + }; + enum CCDMode { CCD_MODE_DISABLED, CCD_MODE_CAST_RAY, @@ -124,10 +137,13 @@ public: private: bool can_sleep = true; - PhysicsDirectBodyState2D *state = nullptr; Mode mode = MODE_DYNAMIC; real_t mass = 1.0; + real_t inertia = 0.0; + CenterOfMassMode center_of_mass_mode = CENTER_OF_MASS_MODE_AUTO; + Vector2 center_of_mass; + Ref<PhysicsMaterial> physics_material_override; real_t gravity_scale = 1.0; real_t linear_damp = -1.0; @@ -183,12 +199,16 @@ private: void _body_exit_tree(ObjectID p_id); void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); - void _direct_state_changed(Object *p_state); + + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state); + void _body_state_changed(PhysicsDirectBodyState2D *p_state); protected: void _notification(int p_what); static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; + GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *) public: @@ -201,6 +221,12 @@ public: void set_inertia(real_t p_inertia); real_t get_inertia() const; + void set_center_of_mass_mode(CenterOfMassMode p_mode); + CenterOfMassMode get_center_of_mass_mode() const; + + void set_center_of_mass(const Vector2 &p_center_of_mass); + const Vector2 &get_center_of_mass() const; + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); Ref<PhysicsMaterial> get_physics_material_override() const; @@ -265,6 +291,7 @@ private: }; VARIANT_ENUM_CAST(RigidBody2D::Mode); +VARIANT_ENUM_CAST(RigidBody2D::CenterOfMassMode); VARIANT_ENUM_CAST(RigidBody2D::CCDMode); class CharacterBody2D : public PhysicsBody2D { @@ -310,7 +337,8 @@ private: float floor_snap_length = 0; 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_ignore_layers = 0; + uint32_t moving_platform_floor_layers = UINT32_MAX; + uint32_t moving_platform_wall_layers = 0; Vector2 linear_velocity; Vector2 floor_normal; @@ -350,14 +378,17 @@ private: real_t get_free_mode_min_slide_angle() const; void set_free_mode_min_slide_angle(real_t p_radians); - uint32_t get_moving_platform_ignore_layers() const; - void set_moving_platform_ignore_layers(const uint32_t p_exclude_layer); + uint32_t get_moving_platform_floor_layers() const; + void set_moving_platform_floor_layers(const uint32_t p_exclude_layer); + + uint32_t get_moving_platform_wall_layers() const; + void set_moving_platform_wall_layers(const uint32_t p_exclude_layer); void set_motion_mode(MotionMode p_mode); MotionMode get_motion_mode() const; - void _move_and_slide_free(real_t p_delta); - void _move_and_slide_grounded(real_t p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity); + void _move_and_slide_free(double p_delta); + void _move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity); Ref<KinematicCollision2D> _get_slide_collision(int p_bounce); Ref<KinematicCollision2D> _get_last_slide_collision(); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 13f1d258a8..729cc95bd7 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1147,7 +1147,7 @@ void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) { PhysicsServer2D::get_singleton()->body_set_space(body, space); Transform2D xform; - xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(layer))); + xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer))); xform = global_transform * xform; PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 06a4087b60..00c6664e65 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -223,121 +223,113 @@ Ref<PhysicsMaterial> StaticBody3D::get_physics_material_override() const { return physics_material_override; } -void StaticBody3D::set_kinematic_motion_enabled(bool p_enabled) { - if (p_enabled == kinematic_motion) { - return; - } +void StaticBody3D::set_constant_linear_velocity(const Vector3 &p_vel) { + constant_linear_velocity = p_vel; - kinematic_motion = p_enabled; + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity); +} - if (kinematic_motion) { - set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); - } else { - set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); - } +void StaticBody3D::set_constant_angular_velocity(const Vector3 &p_vel) { + constant_angular_velocity = p_vel; -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - return; - } -#endif + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity); +} - _update_kinematic_motion(); +Vector3 StaticBody3D::get_constant_linear_velocity() const { + return constant_linear_velocity; } -bool StaticBody3D::is_kinematic_motion_enabled() const { - return kinematic_motion; +Vector3 StaticBody3D::get_constant_angular_velocity() const { + return constant_angular_velocity; } -void StaticBody3D::set_constant_linear_velocity(const Vector3 &p_vel) { - constant_linear_velocity = p_vel; +void StaticBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody3D::set_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody3D::set_constant_angular_velocity); + ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody3D::get_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody3D::get_constant_angular_velocity); + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody3D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody3D::get_physics_material_override); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); +} + +StaticBody3D::StaticBody3D(PhysicsServer3D::BodyMode p_mode) : + PhysicsBody3D(p_mode) { +} - if (kinematic_motion) { - _update_kinematic_motion(); +void StaticBody3D::_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); } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity); + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); } } -void StaticBody3D::set_sync_to_physics(bool p_enable) { +Vector3 AnimatableBody3D::get_linear_velocity() const { + return linear_velocity; +} + +Vector3 AnimatableBody3D::get_angular_velocity() const { + return angular_velocity; +} + +void AnimatableBody3D::set_sync_to_physics(bool p_enable) { if (sync_to_physics == p_enable) { return; } sync_to_physics = p_enable; + _update_kinematic_motion(); +} + +bool AnimatableBody3D::is_sync_to_physics_enabled() const { + return sync_to_physics; +} + +void AnimatableBody3D::_update_kinematic_motion() { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); return; } #endif - if (kinematic_motion) { - _update_kinematic_motion(); + if (sync_to_physics) { + set_only_update_transform_changes(true); + set_notify_local_transform(true); + } else { + set_only_update_transform_changes(false); + set_notify_local_transform(false); } } -bool StaticBody3D::is_sync_to_physics_enabled() const { - return sync_to_physics; +void AnimatableBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + AnimatableBody3D *body = (AnimatableBody3D *)p_instance; + body->_body_state_changed(p_state); } -void StaticBody3D::_direct_state_changed(Object *p_state) { - PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); - - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); +void AnimatableBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); if (!sync_to_physics) { return; } - last_valid_transform = state->get_transform(); + last_valid_transform = p_state->get_transform(); set_notify_local_transform(false); set_global_transform(last_valid_transform); set_notify_local_transform(true); _on_transform_changed(); } -TypedArray<String> StaticBody3D::get_configuration_warnings() const { - TypedArray<String> warnings = PhysicsBody3D::get_configuration_warnings(); - - if (sync_to_physics && !kinematic_motion) { - warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled.")); - } - - return warnings; -} - -void StaticBody3D::set_constant_angular_velocity(const Vector3 &p_vel) { - constant_angular_velocity = p_vel; - - if (kinematic_motion) { - _update_kinematic_motion(); - } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity); - } -} - -Vector3 StaticBody3D::get_constant_linear_velocity() const { - return constant_linear_velocity; -} - -Vector3 StaticBody3D::get_constant_angular_velocity() const { - return constant_angular_velocity; -} - -Vector3 StaticBody3D::get_linear_velocity() const { - return linear_velocity; -} - -Vector3 StaticBody3D::get_angular_velocity() const { - return angular_velocity; -} - -void StaticBody3D::_notification(int p_what) { +void AnimatableBody3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { last_valid_transform = get_global_transform(); @@ -347,17 +339,6 @@ void StaticBody3D::_notification(int p_what) { // Used by sync to physics, send the new transform to the physics... Transform3D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); - new_transform.origin += constant_linear_velocity * delta_time; - - real_t ang_vel = constant_angular_velocity.length(); - if (!Math::is_zero_approx(ang_vel)) { - Vector3 ang_vel_axis = constant_angular_velocity / ang_vel; - Basis rot(ang_vel_axis, ang_vel * delta_time); - new_transform.basis = rot * new_transform.basis; - new_transform.orthonormalize(); - } - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform); // ... but then revert changes. @@ -366,108 +347,21 @@ void StaticBody3D::_notification(int p_what) { set_notify_local_transform(true); _on_transform_changed(); } break; - - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - - ERR_FAIL_COND(!kinematic_motion); - - Transform3D new_transform = get_global_transform(); - - real_t delta_time = get_physics_process_delta_time(); - new_transform.origin += constant_linear_velocity * delta_time; - - real_t ang_vel = constant_angular_velocity.length(); - if (!Math::is_zero_approx(ang_vel)) { - Vector3 ang_vel_axis = constant_angular_velocity / ang_vel; - Basis rot(ang_vel_axis, ang_vel * delta_time); - new_transform.basis = rot * new_transform.basis; - new_transform.orthonormalize(); - } - - if (sync_to_physics) { - // Propagate transform change to node. - set_global_transform(new_transform); - } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform); - - // Propagate transform change to node. - set_ignore_transform_notification(true); - set_global_transform(new_transform); - set_ignore_transform_notification(false); - _on_transform_changed(); - } - } break; } } -void StaticBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody3D::set_constant_linear_velocity); - ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody3D::set_constant_angular_velocity); - ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody3D::get_constant_linear_velocity); - ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody3D::get_constant_angular_velocity); - - ClassDB::bind_method(D_METHOD("set_kinematic_motion_enabled", "enabled"), &StaticBody3D::set_kinematic_motion_enabled); - ClassDB::bind_method(D_METHOD("is_kinematic_motion_enabled"), &StaticBody3D::is_kinematic_motion_enabled); +void AnimatableBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &AnimatableBody3D::set_sync_to_physics); + ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &AnimatableBody3D::is_sync_to_physics_enabled); - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody3D::set_physics_material_override); - ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody3D::get_physics_material_override); - - ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody3D::set_sync_to_physics); - ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody3D::is_sync_to_physics_enabled); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); } -StaticBody3D::StaticBody3D() : - PhysicsBody3D(PhysicsServer3D::BODY_MODE_STATIC) { -} +AnimatableBody3D::AnimatableBody3D() : + StaticBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) { + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); -void StaticBody3D::_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); - } else { - PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); - PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); - } -} - -void StaticBody3D::_update_kinematic_motion() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - - if (kinematic_motion && sync_to_physics) { - set_only_update_transform_changes(true); - set_notify_local_transform(true); - } else { - set_only_update_transform_changes(false); - set_notify_local_transform(false); - } - - bool needs_physics_process = false; - if (kinematic_motion) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody3D::_direct_state_changed)); - - if (!constant_angular_velocity.is_equal_approx(Vector3()) || !constant_linear_velocity.is_equal_approx(Vector3())) { - needs_physics_process = true; - } - } else { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); - } - - set_physics_process_internal(needs_physics_process); + _update_kinematic_motion(); } void RigidBody3D::_body_enter_tree(ObjectID p_id) { @@ -582,25 +476,26 @@ struct _RigidBodyInOut { int local_shape = 0; }; -void RigidBody3D::_direct_state_changed(Object *p_state) { -#ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); -#else - state = (PhysicsDirectBodyState3D *)p_state; //trust it -#endif +void RigidBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + RigidBody3D *body = (RigidBody3D *)p_instance; + body->_body_state_changed(p_state); +} +void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { set_ignore_transform_notification(true); - set_global_transform(state->get_transform()); - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); - inverse_inertia_tensor = state->get_inverse_inertia_tensor(); - if (sleeping != state->is_sleeping()) { - sleeping = state->is_sleeping(); + set_global_transform(p_state->get_transform()); + + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); + + inverse_inertia_tensor = p_state->get_inverse_inertia_tensor(); + + if (sleeping != p_state->is_sleeping()) { + sleeping = p_state->is_sleeping(); emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed); } - GDVIRTUAL_CALL(_integrate_forces, state); + GDVIRTUAL_CALL(_integrate_forces, p_state); set_ignore_transform_notification(false); _on_transform_changed(); @@ -617,18 +512,18 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { } } - _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(state->get_contact_count() * sizeof(_RigidBodyInOut)); + _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBodyInOut)); int toadd_count = 0; //state->get_contact_count(); RigidBody3D_RemoveAction *toremove = (RigidBody3D_RemoveAction *)alloca(rc * sizeof(RigidBody3D_RemoveAction)); int toremove_count = 0; //put the ones to add - for (int i = 0; i < state->get_contact_count(); i++) { - RID rid = state->get_contact_collider(i); - ObjectID obj = state->get_contact_collider_id(i); - int local_shape = state->get_contact_local_shape(i); - int shape = state->get_contact_collider_shape(i); + for (int i = 0; i < p_state->get_contact_count(); i++) { + RID rid = p_state->get_contact_collider(i); + ObjectID obj = p_state->get_contact_collider_id(i); + int local_shape = p_state->get_contact_local_shape(i); + int shape = p_state->get_contact_collider_shape(i); //bool found=false; @@ -683,8 +578,6 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { contact_monitor->locked = false; } - - state = nullptr; } void RigidBody3D::_notification(int p_what) { @@ -738,6 +631,60 @@ real_t RigidBody3D::get_mass() const { return mass; } +void RigidBody3D::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); + + inertia = p_inertia; + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_INERTIA, inertia); +} + +const Vector3 &RigidBody3D::get_inertia() const { + return inertia; +} + +void RigidBody3D::set_center_of_mass_mode(CenterOfMassMode p_mode) { + if (center_of_mass_mode == p_mode) { + return; + } + + center_of_mass_mode = p_mode; + + switch (center_of_mass_mode) { + case CENTER_OF_MASS_MODE_AUTO: { + center_of_mass = Vector3(); + PhysicsServer3D::get_singleton()->body_reset_mass_properties(get_rid()); + if (inertia != Vector3()) { + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_INERTIA, inertia); + } + } break; + + case CENTER_OF_MASS_MODE_CUSTOM: { + PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS, center_of_mass); + } break; + } +} + +RigidBody3D::CenterOfMassMode RigidBody3D::get_center_of_mass_mode() const { + return center_of_mass_mode; +} + +void RigidBody3D::set_center_of_mass(const Vector3 &p_center_of_mass) { + if (center_of_mass == p_center_of_mass) { + return; + } + + ERR_FAIL_COND(center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM); + center_of_mass = 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 { + return center_of_mass; +} + void RigidBody3D::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))) { @@ -787,25 +734,15 @@ real_t RigidBody3D::get_angular_damp() const { } void RigidBody3D::set_axis_velocity(const Vector3 &p_axis) { - Vector3 v = state ? state->get_linear_velocity() : linear_velocity; Vector3 axis = p_axis.normalized(); - v -= axis * axis.dot(v); - v += p_axis; - if (state) { - set_linear_velocity(v); - } else { - PhysicsServer3D::get_singleton()->body_set_axis_velocity(get_rid(), p_axis); - linear_velocity = v; - } + linear_velocity -= axis * axis.dot(linear_velocity); + linear_velocity += p_axis; + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } void RigidBody3D::set_linear_velocity(const Vector3 &p_velocity) { linear_velocity = p_velocity; - if (state) { - state->set_linear_velocity(linear_velocity); - } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); - } + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } Vector3 RigidBody3D::get_linear_velocity() const { @@ -814,11 +751,7 @@ Vector3 RigidBody3D::get_linear_velocity() const { void RigidBody3D::set_angular_velocity(const Vector3 &p_velocity) { angular_velocity = p_velocity; - if (state) { - state->set_angular_velocity(angular_velocity); - } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); - } + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } Vector3 RigidBody3D::get_angular_velocity() const { @@ -972,6 +905,15 @@ void RigidBody3D::_bind_methods() { 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_inertia", "inertia"), &RigidBody3D::set_inertia); + ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody3D::get_inertia); + + 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_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_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); @@ -1025,7 +967,11 @@ void RigidBody3D::_bind_methods() { GDVIRTUAL_BIND(_integrate_forces, "state"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); + 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"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater"), "set_center_of_mass", "get_center_of_mass"); + ADD_LINKED_PROPERTY("center_of_mass_mode", "center_of_mass"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); @@ -1051,11 +997,22 @@ void RigidBody3D::_bind_methods() { BIND_ENUM_CONSTANT(MODE_STATIC); BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); BIND_ENUM_CONSTANT(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 { + if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) { + if (property.name == "center_of_mass") { + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + } + } } RigidBody3D::RigidBody3D() : PhysicsBody3D(PhysicsServer3D::BODY_MODE_DYNAMIC) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody3D::_direct_state_changed)); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); } RigidBody3D::~RigidBody3D() { @@ -1083,7 +1040,7 @@ bool CharacterBody3D::move_and_slide() { bool was_on_floor = on_floor; // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky - float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { @@ -2252,23 +2209,19 @@ void PhysicalBone3D::_notification(int p_what) { } } -void PhysicalBone3D::_direct_state_changed(Object *p_state) { +void PhysicalBone3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + PhysicalBone3D *bone = (PhysicalBone3D *)p_instance; + bone->_body_state_changed(p_state); +} + +void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { if (!simulate_physics || !_internal_simulate_physics) { return; } - /// Update bone transform - - PhysicsDirectBodyState3D *state; - -#ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); -#else - state = (PhysicsDirectBodyState3D *)p_state; //trust it -#endif + /// Update bone transform. - Transform3D global_transform(state->get_transform()); + Transform3D global_transform(p_state->get_transform()); set_ignore_transform_notification(true); set_global_transform(global_transform); @@ -2332,7 +2285,7 @@ void PhysicalBone3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "body_offset"), "set_body_offset", "get_body_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); + 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, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale"); @@ -2730,7 +2683,7 @@ void PhysicalBone3D::_start_physics_simulation() { set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &PhysicalBone3D::_direct_state_changed)); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); set_as_top_level(true); _internal_simulate_physics = true; } @@ -2749,7 +2702,7 @@ void PhysicalBone3D::_stop_physics_simulation() { PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0); } if (_internal_simulate_physics) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); parent_skeleton->set_bone_global_pose_override(bone_id, Transform3D(), 0.0, false); set_as_top_level(false); _internal_simulate_physics = false; diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 17e688451d..8e6463f838 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -73,23 +73,13 @@ public: class StaticBody3D : public PhysicsBody3D { GDCLASS(StaticBody3D, PhysicsBody3D); +private: Vector3 constant_linear_velocity; Vector3 constant_angular_velocity; - Vector3 linear_velocity; - Vector3 angular_velocity; - Ref<PhysicsMaterial> physics_material_override; - bool kinematic_motion = false; - bool sync_to_physics = false; - - Transform3D last_valid_transform; - - void _direct_state_changed(Object *p_state); - protected: - void _notification(int p_what); static void _bind_methods(); public: @@ -102,20 +92,38 @@ public: Vector3 get_constant_linear_velocity() const; Vector3 get_constant_angular_velocity() const; - virtual Vector3 get_linear_velocity() const override; - virtual Vector3 get_angular_velocity() const override; + StaticBody3D(PhysicsServer3D::BodyMode p_mode = PhysicsServer3D::BODY_MODE_STATIC); - virtual TypedArray<String> get_configuration_warnings() const override; +private: + void _reload_physics_characteristics(); +}; - StaticBody3D(); +class AnimatableBody3D : public StaticBody3D { + GDCLASS(AnimatableBody3D, StaticBody3D); private: - void _reload_physics_characteristics(); + Vector3 linear_velocity; + Vector3 angular_velocity; - void _update_kinematic_motion(); + bool sync_to_physics = false; - void set_kinematic_motion_enabled(bool p_enabled); - bool is_kinematic_motion_enabled() const; + Transform3D last_valid_transform; + + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + void _body_state_changed(PhysicsDirectBodyState3D *p_state); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual Vector3 get_linear_velocity() const override; + virtual Vector3 get_angular_velocity() const override; + + AnimatableBody3D(); + +private: + void _update_kinematic_motion(); void set_sync_to_physics(bool p_enable); bool is_sync_to_physics_enabled() const; @@ -132,14 +140,22 @@ public: MODE_KINEMATIC, }; + enum CenterOfMassMode { + CENTER_OF_MASS_MODE_AUTO, + CENTER_OF_MASS_MODE_CUSTOM, + }; + GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) protected: bool can_sleep = true; - PhysicsDirectBodyState3D *state = nullptr; Mode mode = MODE_DYNAMIC; real_t mass = 1.0; + Vector3 inertia; + CenterOfMassMode center_of_mass_mode = CENTER_OF_MASS_MODE_AUTO; + Vector3 center_of_mass; + Ref<PhysicsMaterial> physics_material_override; Vector3 linear_velocity; @@ -197,11 +213,14 @@ protected: void _body_exit_tree(ObjectID p_id); void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); - virtual void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); void _notification(int p_what); static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; + public: void set_mode(Mode p_mode); Mode get_mode() const; @@ -211,6 +230,15 @@ public: virtual real_t get_inverse_mass() const override { return 1.0 / mass; } + void set_inertia(const Vector3 &p_inertia); + const Vector3 &get_inertia() const; + + void set_center_of_mass_mode(CenterOfMassMode p_mode); + CenterOfMassMode get_center_of_mass_mode() const; + + void set_center_of_mass(const Vector3 &p_center_of_mass); + const Vector3 &get_center_of_mass() const; + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); Ref<PhysicsMaterial> get_physics_material_override() const; @@ -271,6 +299,7 @@ private: }; VARIANT_ENUM_CAST(RigidBody3D::Mode); +VARIANT_ENUM_CAST(RigidBody3D::CenterOfMassMode); class KinematicCollision3D; @@ -525,7 +554,8 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); - void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + void _body_state_changed(PhysicsDirectBodyState3D *p_state); static void _bind_methods(); diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 28ff3e9412..7eb189e890 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -355,6 +355,11 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody3D::set_drag_coefficient); ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody3D::get_drag_coefficient); + ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform); + + ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::pin_point, DEFVAL(NodePath())); + ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned); + ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody3D::set_ray_pickable); ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody3D::is_ray_pickable); diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 92c0e09947..daeea81891 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -802,24 +802,21 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { } } -void VehicleBody3D::_direct_state_changed(Object *p_state) { - RigidBody3D::_direct_state_changed(p_state); +void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { + RigidBody3D::_body_state_changed(p_state); - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); - - real_t step = state->get_step(); + real_t step = p_state->get_step(); for (int i = 0; i < wheels.size(); i++) { - _update_wheel(i, state); + _update_wheel(i, p_state); } for (int i = 0; i < wheels.size(); i++) { - _ray_cast(i, state); - wheels[i]->set_transform(state->get_transform().inverse() * wheels[i]->m_worldTransform); + _ray_cast(i, p_state); + wheels[i]->set_transform(p_state->get_transform().inverse() * wheels[i]->m_worldTransform); } - _update_suspension(state); + _update_suspension(p_state); for (int i = 0; i < wheels.size(); i++) { //apply suspension force @@ -831,20 +828,20 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { suspensionForce = wheel.m_maxSuspensionForce; } Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; - Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - state->get_transform().origin; + Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - p_state->get_transform().origin; - state->apply_impulse(impulse, relative_position); + p_state->apply_impulse(impulse, relative_position); } - _update_friction(state); + _update_friction(p_state); for (int i = 0; i < wheels.size(); i++) { VehicleWheel3D &wheel = *wheels[i]; - Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - state->get_transform().origin; - Vector3 vel = state->get_linear_velocity() + (state->get_angular_velocity()).cross(relpos); // * mPos); + Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - p_state->get_transform().origin; + Vector3 vel = p_state->get_linear_velocity() + (p_state->get_angular_velocity()).cross(relpos); // * mPos); if (wheel.m_raycastInfo.m_isInContact) { - const Transform3D &chassisWorldTransform = state->get_transform(); + const Transform3D &chassisWorldTransform = p_state->get_transform(); Vector3 fwd( chassisWorldTransform.basis[0][Vector3::AXIS_Z], @@ -864,8 +861,6 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact } - - state = nullptr; } void VehicleBody3D::set_engine_force(real_t p_engine_force) { @@ -926,7 +921,5 @@ void VehicleBody3D::_bind_methods() { VehicleBody3D::VehicleBody3D() { exclude.insert(get_rid()); - //PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &VehicleBody3D::_direct_state_changed)); - set_mass(40); } diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index 2c10205ea3..f29c3d89b7 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -192,7 +192,8 @@ class VehicleBody3D : public RigidBody3D { static void _bind_methods(); - void _direct_state_changed(Object *p_state) override; + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state) override; public: void set_engine_force(real_t p_engine_force); diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index a2f1d2b15a..cf2df4e1a2 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -351,11 +351,11 @@ MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control Label *l = memnew(Label); l->set_theme_type_variation("HeaderSmall"); l->set_text(p_label); - add_child(l); + add_child(l, false, INTERNAL_MODE_FRONT); MarginContainer *mc = memnew(MarginContainer); mc->add_theme_constant_override("margin_left", 0); mc->add_child(p_control); - add_child(mc); + add_child(mc, false, INTERNAL_MODE_FRONT); if (p_expand) { mc->set_v_size_flags(SIZE_EXPAND_FILL); } diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index d93107df2d..411fb2e1f0 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -34,11 +34,13 @@ Size2 CheckBox::get_icon_size() const { Ref<Texture2D> checked = Control::get_theme_icon(SNAME("checked")); - Ref<Texture2D> checked_disabled = Control::get_theme_icon(SNAME("checked_disabled")); Ref<Texture2D> unchecked = Control::get_theme_icon(SNAME("unchecked")); - Ref<Texture2D> unchecked_disabled = Control::get_theme_icon(SNAME("unchecked_disabled")); Ref<Texture2D> radio_checked = Control::get_theme_icon(SNAME("radio_checked")); Ref<Texture2D> radio_unchecked = Control::get_theme_icon(SNAME("radio_unchecked")); + Ref<Texture2D> checked_disabled = Control::get_theme_icon(SNAME("checked_disabled")); + Ref<Texture2D> unchecked_disabled = Control::get_theme_icon(SNAME("unchecked_disabled")); + Ref<Texture2D> radio_checked_disabled = Control::get_theme_icon(SNAME("radio_checked_disabled")); + Ref<Texture2D> radio_unchecked_disabled = Control::get_theme_icon(SNAME("radio_unchecked_disabled")); Size2 tex_size = Size2(0, 0); if (!checked.is_null()) { @@ -53,6 +55,18 @@ Size2 CheckBox::get_icon_size() const { if (!radio_unchecked.is_null()) { tex_size = Size2(MAX(tex_size.width, radio_unchecked->get_width()), MAX(tex_size.height, radio_unchecked->get_height())); } + if (!checked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, checked_disabled->get_width()), MAX(tex_size.height, checked_disabled->get_height())); + } + if (!unchecked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, unchecked_disabled->get_width()), MAX(tex_size.height, unchecked_disabled->get_height())); + } + if (!radio_checked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, radio_checked_disabled->get_width()), MAX(tex_size.height, radio_checked_disabled->get_height())); + } + if (!radio_unchecked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, radio_unchecked_disabled->get_width()), MAX(tex_size.height, radio_unchecked_disabled->get_height())); + } return tex_size; } diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 0dddb2b09a..1afb0b8e9d 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -1139,7 +1139,7 @@ void ColorPicker::_bind_methods() { ColorPicker::ColorPicker() : BoxContainer(true) { HBoxContainer *hb_edit = memnew(HBoxContainer); - add_child(hb_edit); + add_child(hb_edit, false, INTERNAL_MODE_FRONT); hb_edit->set_v_size_flags(SIZE_EXPAND_FILL); hb_edit->add_child(uv_edit); @@ -1151,7 +1151,7 @@ ColorPicker::ColorPicker() : uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit)); HBoxContainer *hb_smpl = memnew(HBoxContainer); - add_child(hb_smpl); + add_child(hb_smpl, false, INTERNAL_MODE_FRONT); hb_smpl->add_child(sample); sample->set_h_size_flags(SIZE_EXPAND_FILL); @@ -1165,12 +1165,12 @@ ColorPicker::ColorPicker() : btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed)); VBoxContainer *vbl = memnew(VBoxContainer); - add_child(vbl); + add_child(vbl, false, INTERNAL_MODE_FRONT); - add_child(memnew(HSeparator)); + add_child(memnew(HSeparator), false, INTERNAL_MODE_FRONT); VBoxContainer *vbr = memnew(VBoxContainer); - add_child(vbr); + add_child(vbr, false, INTERNAL_MODE_FRONT); vbr->set_h_size_flags(SIZE_EXPAND_FILL); for (int i = 0; i < 4; i++) { @@ -1273,11 +1273,11 @@ ColorPicker::ColorPicker() : set_pick_color(Color(1, 1, 1)); - add_child(preset_separator); + add_child(preset_separator, false, INTERNAL_MODE_FRONT); preset_container->set_h_size_flags(SIZE_EXPAND_FILL); preset_container->set_columns(preset_column_count); - add_child(preset_container); + add_child(preset_container, false, INTERNAL_MODE_FRONT); btn_add_preset->set_icon_align(Button::ALIGN_CENTER); btn_add_preset->set_tooltip(RTR("Add current color as a preset.")); @@ -1405,7 +1405,7 @@ void ColorPickerButton::_update_picker() { picker = memnew(ColorPicker); picker->set_anchors_and_offsets_preset(PRESET_WIDE); popup->add_child(picker); - add_child(popup); + add_child(popup, false, INTERNAL_MODE_FRONT); picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed)); popup->connect("about_to_popup", callable_mp(this, &ColorPickerButton::_about_to_popup)); popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed)); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 4ac6a58d36..81411d5844 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -30,6 +30,7 @@ #include "control.h" +#include "container.h" #include "core/config/project_settings.h" #include "core/math/geometry_2d.h" #include "core/object/message_queue.h" @@ -168,6 +169,20 @@ Size2 Control::_edit_get_minimum_size() const { } #endif +String Control::properties_managed_by_container[] = { + "offset_left", + "offset_top", + "offset_right", + "offset_bottom", + "anchor_left", + "anchor_top", + "anchor_right", + "anchor_bottom", + "rect_position", + "rect_scale", + "rect_size" +}; + void Control::accept_event() { if (is_inside_tree()) { get_viewport()->_gui_accept_event(); @@ -442,6 +457,20 @@ void Control::_validate_property(PropertyInfo &property) const { property.hint_string = hint_string; } + if (!Object::cast_to<Container>(get_parent())) { + return; + } + // Disable the property if it's managed by the parent container. + bool property_is_managed_by_container = false; + for (unsigned i = 0; i < properties_managed_by_container_count; i++) { + property_is_managed_by_container = properties_managed_by_container[i] == property.name; + if (property_is_managed_by_container) { + break; + } + } + if (property_is_managed_by_container) { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } } Control *Control::get_parent_control() const { diff --git a/scene/gui/control.h b/scene/gui/control.h index 0faa617f8d..9cec5d6e8d 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -230,6 +230,9 @@ private: } data; + static constexpr unsigned properties_managed_by_container_count = 11; + static String properties_managed_by_container[properties_managed_by_container_count]; + // used internally Control *_find_control_at_pos(CanvasItem *p_node, const Point2 &p_pos, const Transform2D &p_xform, Transform2D &r_inv_xform); diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index da858e8e83..5d98aaa698 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -319,7 +319,7 @@ AcceptDialog::AcceptDialog() { set_clamp_to_embedder(true); bg = memnew(Panel); - add_child(bg); + add_child(bg, false, INTERNAL_MODE_FRONT); hbc = memnew(HBoxContainer); @@ -331,9 +331,9 @@ AcceptDialog::AcceptDialog() { label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END); label->set_begin(Point2(margin, margin)); label->set_end(Point2(-margin, -button_margin - 10)); - add_child(label); + add_child(label, false, INTERNAL_MODE_FRONT); - add_child(hbc); + add_child(hbc, false, INTERNAL_MODE_FRONT); hbc->add_spacer(); ok = memnew(Button); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 2512b24623..973b72973d 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -924,7 +924,7 @@ FileDialog::FileDialog() { show_hidden_files = default_show_hidden_files; vbox = memnew(VBoxContainer); - add_child(vbox); + add_child(vbox, false, INTERNAL_MODE_FRONT); vbox->connect("theme_changed", callable_mp(this, &FileDialog::_theme_changed)); mode = FILE_MODE_SAVE_FILE; @@ -1023,8 +1023,7 @@ FileDialog::FileDialog() { filter->connect("item_selected", callable_mp(this, &FileDialog::_filter_selected)); confirm_save = memnew(ConfirmationDialog); - // confirm_save->set_as_top_level(true); - add_child(confirm_save); + add_child(confirm_save, false, INTERNAL_MODE_FRONT); confirm_save->connect("confirmed", callable_mp(this, &FileDialog::_save_confirm_pressed)); @@ -1036,16 +1035,16 @@ FileDialog::FileDialog() { makedirname = memnew(LineEdit); makedirname->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); makevb->add_margin_child(TTRC("Name:"), makedirname); - add_child(makedialog); + add_child(makedialog, false, INTERNAL_MODE_FRONT); makedialog->register_text_enter(makedirname); makedialog->connect("confirmed", callable_mp(this, &FileDialog::_make_dir_confirm)); mkdirerr = memnew(AcceptDialog); mkdirerr->set_text(TTRC("Could not create folder.")); - add_child(mkdirerr); + add_child(mkdirerr, false, INTERNAL_MODE_FRONT); exterr = memnew(AcceptDialog); exterr->set_text(TTRC("Must use a valid extension.")); - add_child(exterr); + add_child(exterr, false, INTERNAL_MODE_FRONT); update_filters(); update_dir(); diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index bbab3008e2..56b8a936e1 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -48,7 +48,7 @@ GradientEdit::GradientEdit() { picker = memnew(ColorPicker); popup->add_child(picker); - add_child(popup); + add_child(popup, false, INTERNAL_MODE_FRONT); } int GradientEdit::_get_point_from_pos(int x) { diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 0281bc7efb..cabae9feb2 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -366,7 +366,6 @@ void GraphEdit::_graph_node_raised(Node *p_gn) { } move_child(connections_layer, first_not_comment); - top_layer->raise(); emit_signal(SNAME("node_selected"), p_gn); } @@ -2246,14 +2245,14 @@ GraphEdit::GraphEdit() { zoom_max = (1 * Math::pow(zoom_step, 4)); top_layer = memnew(GraphEditFilter(this)); - add_child(top_layer); + add_child(top_layer, false, INTERNAL_MODE_BACK); top_layer->set_mouse_filter(MOUSE_FILTER_PASS); top_layer->set_anchors_and_offsets_preset(Control::PRESET_WIDE); top_layer->connect("draw", callable_mp(this, &GraphEdit::_top_layer_draw)); top_layer->connect("gui_input", callable_mp(this, &GraphEdit::_top_layer_input)); connections_layer = memnew(Control); - add_child(connections_layer); + add_child(connections_layer, false, INTERNAL_MODE_FRONT); connections_layer->connect("draw", callable_mp(this, &GraphEdit::_connections_layer_draw)); connections_layer->set_name("CLAYER"); connections_layer->set_disable_visibility_clip(true); // so it can draw freely and be offset diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 8297de9f30..d10ad90c1f 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1656,7 +1656,7 @@ void ItemList::_bind_methods() { ItemList::ItemList() { scroll_bar = memnew(VScrollBar); - add_child(scroll_bar); + add_child(scroll_bar, false, INTERNAL_MODE_FRONT); scroll_bar->connect("value_changed", callable_mp(this, &ItemList::_scroll_changed)); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index d524e78caa..d9acbeb828 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -674,7 +674,7 @@ void LineEdit::_notification(int p_what) { int y_ofs = style->get_offset().y + (y_area - text_height) / 2; Color selection_color = get_theme_color(SNAME("selection_color")); - Color font_color = is_editable() ? get_theme_color(SNAME("font_color")) : get_theme_color(SNAME("font_uneditable_color")); + Color font_color = get_theme_color(is_editable() ? SNAME("font_color") : SNAME("font_uneditable_color")); Color font_selected_color = get_theme_color(SNAME("font_selected_color")); Color caret_color = get_theme_color(SNAME("caret_color")); @@ -2220,7 +2220,7 @@ void LineEdit::_bind_methods() { void LineEdit::_ensure_menu() { if (!menu) { menu = memnew(PopupMenu); - add_child(menu); + add_child(menu, false, INTERNAL_MODE_FRONT); menu_dir = memnew(PopupMenu); menu_dir->set_name("DirMenu"); @@ -2305,7 +2305,7 @@ LineEdit::LineEdit() { set_mouse_filter(MOUSE_FILTER_STOP); caret_blink_timer = memnew(Timer); - add_child(caret_blink_timer); + add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT); caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret)); set_caret_blink_enabled(false); diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index e312aaed57..737ba84617 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -172,7 +172,7 @@ MenuButton::MenuButton() { popup = memnew(PopupMenu); popup->hide(); - add_child(popup); + add_child(popup, false, INTERNAL_MODE_FRONT); popup->connect("about_to_popup", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(true)); popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(false)); } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index cd55f258b3..d16e96dbec 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -351,7 +351,7 @@ OptionButton::OptionButton() { popup = memnew(PopupMenu); popup->hide(); - add_child(popup); + add_child(popup, false, INTERNAL_MODE_FRONT); popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected)); popup->connect("id_focused", callable_mp(this, &OptionButton::_focused)); popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(false)); diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index f7e7e1cd60..e9414598a2 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -255,5 +255,5 @@ void PopupPanel::_notification(int p_what) { PopupPanel::PopupPanel() { panel = memnew(Panel); - add_child(panel); + add_child(panel, false, INTERNAL_MODE_FRONT); } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index c37095a9f8..c0a559e624 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -1690,7 +1690,7 @@ PopupMenu::PopupMenu() { // Margin Container margin_container = memnew(MarginContainer); margin_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE); - add_child(margin_container); + add_child(margin_container, false, INTERNAL_MODE_FRONT); margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background)); // Scroll Container @@ -1704,7 +1704,7 @@ PopupMenu::PopupMenu() { control->set_anchors_and_offsets_preset(Control::PRESET_WIDE); control->set_h_size_flags(Control::SIZE_EXPAND_FILL); control->set_v_size_flags(Control::SIZE_EXPAND_FILL); - scroll_container->add_child(control); + scroll_container->add_child(control, false, INTERNAL_MODE_FRONT); control->connect("draw", callable_mp(this, &PopupMenu::_draw_items)); connect("window_input", callable_mp(this, &PopupMenu::gui_input)); @@ -1713,13 +1713,13 @@ PopupMenu::PopupMenu() { submenu_timer->set_wait_time(0.3); submenu_timer->set_one_shot(true); submenu_timer->connect("timeout", callable_mp(this, &PopupMenu::_submenu_timeout)); - add_child(submenu_timer); + add_child(submenu_timer, false, INTERNAL_MODE_FRONT); minimum_lifetime_timer = memnew(Timer); minimum_lifetime_timer->set_wait_time(0.3); minimum_lifetime_timer->set_one_shot(true); minimum_lifetime_timer->connect("timeout", callable_mp(this, &PopupMenu::_minimum_lifetime_timeout)); - add_child(minimum_lifetime_timer); + add_child(minimum_lifetime_timer, false, INTERNAL_MODE_FRONT); } PopupMenu::~PopupMenu() { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index b7519af4aa..fbc67d8a24 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -4361,7 +4361,7 @@ RichTextLabel::RichTextLabel() { current_frame = main; vscroll = memnew(VScrollBar); - add_child(vscroll); + add_child(vscroll, false, INTERNAL_MODE_FRONT); vscroll->set_drag_node(String("..")); vscroll->set_step(1); vscroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index b5daa8b3f1..0c051f61e2 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -228,9 +228,6 @@ void ScrollContainer::_update_scrollbar_position() { v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0); v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); - h_scroll->raise(); - v_scroll->raise(); - _updating_scrollbars = false; } @@ -618,12 +615,12 @@ void ScrollContainer::_bind_methods() { ScrollContainer::ScrollContainer() { h_scroll = memnew(HScrollBar); h_scroll->set_name("_h_scroll"); - add_child(h_scroll); + add_child(h_scroll, false, INTERNAL_MODE_BACK); h_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved)); v_scroll = memnew(VScrollBar); v_scroll->set_name("_v_scroll"); - add_child(v_scroll); + add_child(v_scroll, false, INTERNAL_MODE_BACK); v_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved)); deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone"); diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 65a3eb3adf..1074d0d8a0 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -278,7 +278,7 @@ void SpinBox::_bind_methods() { SpinBox::SpinBox() { line_edit = memnew(LineEdit); - add_child(line_edit); + add_child(line_edit, false, INTERNAL_MODE_FRONT); line_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE); line_edit->set_mouse_filter(MOUSE_FILTER_PASS); @@ -291,5 +291,5 @@ SpinBox::SpinBox() { range_click_timer = memnew(Timer); range_click_timer->connect("timeout", callable_mp(this, &SpinBox::_range_click_timeout)); - add_child(range_click_timer); + add_child(range_click_timer, false, INTERNAL_MODE_FRONT); } diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 5884d2a854..845a2ec6c7 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -434,14 +434,14 @@ void TabContainer::_notification(int p_what) { } int tab_width = tab_widths[i]; - if (get_tab_disabled(index)) { + if (index == current) { + x_current = x; + } else if (get_tab_disabled(index)) { if (rtl) { _draw_tab(tab_disabled, font_disabled_color, index, size.width - (tabs_ofs_cache + x) - tab_width); } else { _draw_tab(tab_disabled, font_disabled_color, index, tabs_ofs_cache + x); } - } else if (index == current) { - x_current = x; } else { if (rtl) { _draw_tab(tab_unselected, font_unselected_color, index, size.width - (tabs_ofs_cache + x) - tab_width); @@ -459,12 +459,13 @@ void TabContainer::_notification(int p_what) { panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); } - // Draw selected tab in front. only draw selected tab when it's in visible range. + // Draw selected tab in front. Only draw selected tab when it's in visible range. if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) { + Ref<StyleBox> current_style_box = get_tab_disabled(current) ? tab_disabled : tab_selected; if (rtl) { - _draw_tab(tab_selected, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]); + _draw_tab(current_style_box, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]); } else { - _draw_tab(tab_selected, font_selected_color, current, tabs_ofs_cache + x_current); + _draw_tab(current_style_box, font_selected_color, current, tabs_ofs_cache + x_current); } } @@ -640,8 +641,8 @@ void TabContainer::_on_mouse_exited() { int TabContainer::_get_tab_width(int p_index) const { ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0); - Control *control = Object::cast_to<Control>(_get_tabs()[p_index]); - if (!control || control->is_set_as_top_level() || get_tab_hidden(p_index)) { + Control *control = get_tab_control(p_index); + if (!control || get_tab_hidden(p_index)) { return 0; } @@ -905,7 +906,7 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) { if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { Control *moving_tabc = from_tabc->get_tab_control(tab_from_id); from_tabc->remove_child(moving_tabc); - add_child(moving_tabc); + add_child(moving_tabc, false, INTERNAL_MODE_FRONT); if (hover_now < 0) { hover_now = get_tab_count() - 1; } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a731f80741..b9e9a4d450 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -557,13 +557,25 @@ void TextEdit::_notification(int p_what) { } int minimap_draw_amount = minimap_visible_lines + get_line_wrap_count(minimap_line + 1); - // draw the minimap - Color viewport_color = (background_color.get_v() < 0.5) ? Color(1, 1, 1, 0.1) : Color(0, 0, 0, 0.1); + // Draw the minimap. + + // Add visual feedback when dragging or hovering the the visible area rectangle. + float viewport_alpha; + if (dragging_minimap) { + viewport_alpha = 0.25; + } else if (hovering_minimap) { + viewport_alpha = 0.175; + } else { + viewport_alpha = 0.1; + } + + const Color viewport_color = (background_color.get_v() < 0.5) ? Color(1, 1, 1, viewport_alpha) : Color(0, 0, 0, viewport_alpha); if (rtl) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - minimap_width, viewport_offset_y, minimap_width, viewport_height), viewport_color); } else { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, minimap_width, viewport_height), viewport_color); } + for (int i = 0; i < minimap_draw_amount; i++) { minimap_line++; @@ -1549,6 +1561,10 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } } + if (draw_minimap && !dragging_selection) { + _update_minimap_hover(); + } + if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) { accept_event(); // Accept event if scroll changed. } @@ -5004,7 +5020,7 @@ void TextEdit::_paste_internal() { void TextEdit::_generate_context_menu() { if (!menu) { menu = memnew(PopupMenu); - add_child(menu); + add_child(menu, false, INTERNAL_MODE_FRONT); menu_dir = memnew(PopupMenu); menu_dir->set_name("DirMenu"); @@ -5012,7 +5028,7 @@ void TextEdit::_generate_context_menu() { menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO); menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR); menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL); - menu->add_child(menu_dir); + menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); menu_ctl = memnew(PopupMenu); menu_ctl->set_name("CTLMenu"); @@ -5034,7 +5050,7 @@ void TextEdit::_generate_context_menu() { menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ); menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ); menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY); - menu->add_child(menu_ctl); + menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); @@ -5644,6 +5660,33 @@ void TextEdit::_scroll_lines_down() { } // Minimap + +void TextEdit::_update_minimap_hover() { + const Point2 mp = get_local_mouse_pos(); + const int xmargin_end = get_size().width - style_normal->get_margin(SIDE_RIGHT); + + const bool hovering_sidebar = mp.x > xmargin_end - minimap_width && mp.x < xmargin_end; + if (!hovering_sidebar) { + if (hovering_minimap) { + // Only redraw if the hovering status changed. + hovering_minimap = false; + update(); + } + + // Return early to avoid running the operations below when not needed. + return; + } + + const int row = get_minimap_line_at_pos(Point2i(mp.x, mp.y)); + + const bool new_hovering_minimap = row >= get_first_visible_line() && row <= get_last_full_visible_line(); + if (new_hovering_minimap != hovering_minimap) { + // Only redraw if the hovering status changed. + hovering_minimap = new_hovering_minimap; + update(); + } +} + void TextEdit::_update_minimap_click() { Point2 mp = get_local_mouse_pos(); @@ -5948,8 +5991,8 @@ TextEdit::TextEdit() { h_scroll = memnew(HScrollBar); v_scroll = memnew(VScrollBar); - add_child(h_scroll); - add_child(v_scroll); + add_child(h_scroll, false, INTERNAL_MODE_FRONT); + add_child(v_scroll, false, INTERNAL_MODE_FRONT); h_scroll->connect("value_changed", callable_mp(this, &TextEdit::_scroll_moved)); v_scroll->connect("value_changed", callable_mp(this, &TextEdit::_scroll_moved)); @@ -5958,19 +6001,19 @@ TextEdit::TextEdit() { /* Caret. */ caret_blink_timer = memnew(Timer); - add_child(caret_blink_timer); + add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT); caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", callable_mp(this, &TextEdit::_toggle_draw_caret)); set_caret_blink_enabled(false); /* Selection. */ click_select_held = memnew(Timer); - add_child(click_select_held); + add_child(click_select_held, false, INTERNAL_MODE_FRONT); click_select_held->set_wait_time(0.05); click_select_held->connect("timeout", callable_mp(this, &TextEdit::_click_selection_held)); idle_detect = memnew(Timer); - add_child(idle_detect); + add_child(idle_detect, false, INTERNAL_MODE_FRONT); idle_detect->set_one_shot(true); idle_detect->set_wait_time(GLOBAL_GET("gui/timers/text_edit_idle_detect_sec")); idle_detect->connect("timeout", callable_mp(this, &TextEdit::_push_current_op)); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index ced03e19d0..07999c2442 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -446,12 +446,14 @@ private: // minimap scroll bool minimap_clicked = false; + bool hovering_minimap = false; bool dragging_minimap = false; bool can_drag_minimap = false; double minimap_scroll_ratio = 0.0; double minimap_scroll_click_pos = 0.0; + void _update_minimap_hover(); void _update_minimap_click(); void _update_minimap_drag(); diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 5e5dec3579..286f01ee33 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -100,6 +100,15 @@ Ref<Texture2D> TextureProgressBar::get_progress_texture() const { return progress; } +void TextureProgressBar::set_progress_offset(Point2 p_offset) { + progress_offset = p_offset; + update(); +} + +Point2 TextureProgressBar::get_progress_offset() const { + return progress_offset; +} + void TextureProgressBar::set_tint_under(const Color &p_tint) { tint_under = p_tint; update(); @@ -360,6 +369,9 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu } } + if (p_texture == progress) { + dst_rect.position += progress_offset; + } p_texture->get_rect_region(dst_rect, src_rect, dst_rect, src_rect); RID ci = get_canvas_item(); @@ -403,20 +415,24 @@ void TextureProgressBar::_notification(int p_what) { Size2 s = progress->get_size(); switch (mode) { case FILL_LEFT_TO_RIGHT: { - Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_RIGHT_TO_LEFT: { - Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_TOP_TO_BOTTOM: { - Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_BOTTOM_TO_TOP: { - Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_CLOCKWISE: case FILL_COUNTER_CLOCKWISE: @@ -427,8 +443,9 @@ void TextureProgressBar::_notification(int p_what) { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { - Rect2 region = Rect2(Point2(), s); - draw_texture_rect(progress, region, false, tint_progress); + Rect2 region = Rect2(progress_offset, s); + Rect2 source = Rect2(Point2(), s); + draw_texture_rect_region(progress, region, source, tint_progress); } else if (val != 0) { Array pts; float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1; @@ -454,14 +471,14 @@ void TextureProgressBar::_notification(int p_what) { Vector<Point2> uvs; Vector<Point2> points; uvs.push_back(get_relative_center()); - points.push_back(Point2(s.x * get_relative_center().x, s.y * get_relative_center().y)); + points.push_back(progress_offset + Point2(s.x * get_relative_center().x, s.y * get_relative_center().y)); for (int i = 0; i < pts.size(); i++) { Point2 uv = unit_val_to_uv(pts[i]); if (uvs.find(uv) >= 0) { continue; } uvs.push_back(uv); - points.push_back(Point2(uv.x * s.x, uv.y * s.y)); + points.push_back(progress_offset + Point2(uv.x * s.x, uv.y * s.y)); } Vector<Color> colors; colors.push_back(tint_progress); @@ -484,17 +501,19 @@ void TextureProgressBar::_notification(int p_what) { } } break; case FILL_BILINEAR_LEFT_AND_RIGHT: { - Rect2 region = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_BILINEAR_TOP_AND_BOTTOM: { - Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_MODE_MAX: break; default: - draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); + draw_texture_rect_region(progress, Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } } if (over.is_valid()) { @@ -585,6 +604,9 @@ void TextureProgressBar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgressBar::set_tint_over); ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgressBar::get_tint_over); + ClassDB::bind_method(D_METHOD("set_texture_progress_offset", "offset"), &TextureProgressBar::set_progress_offset); + ClassDB::bind_method(D_METHOD("get_texture_progress_offset"), &TextureProgressBar::get_progress_offset); + ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgressBar::set_radial_initial_angle); ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgressBar::get_radial_initial_angle); @@ -604,6 +626,7 @@ void TextureProgressBar::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_under_texture", "get_under_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_progress_texture", "get_progress_texture"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_progress_offset"), "set_texture_progress_offset", "get_texture_progress_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); ADD_GROUP("Tint", "tint_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under"); diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h index d147c43a26..c508f41387 100644 --- a/scene/gui/texture_progress_bar.h +++ b/scene/gui/texture_progress_bar.h @@ -61,6 +61,9 @@ public: void set_fill_mode(int p_fill); int get_fill_mode(); + void set_progress_offset(Point2 p_offset); + Point2 get_progress_offset() const; + void set_radial_initial_angle(float p_angle); float get_radial_initial_angle(); @@ -100,6 +103,7 @@ public: private: FillMode mode = FILL_LEFT_TO_RIGHT; + Point2 progress_offset; float rad_init_angle = 0.0; float rad_max_degrees = 360.0; Point2 rad_center_off; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 3e46e2749b..cb990892ed 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -4795,12 +4795,11 @@ Tree::Tree() { popup_menu = memnew(PopupMenu); popup_menu->hide(); - add_child(popup_menu); - // popup_menu->set_as_top_level(true); + add_child(popup_menu, false, INTERNAL_MODE_FRONT); popup_editor = memnew(Popup); popup_editor->set_wrap_controls(true); - add_child(popup_editor); + add_child(popup_editor, false, INTERNAL_MODE_FRONT); popup_editor_vb = memnew(VBoxContainer); popup_editor->add_child(popup_editor_vb); popup_editor_vb->add_theme_constant_override("separation", 0); @@ -4818,12 +4817,12 @@ Tree::Tree() { h_scroll = memnew(HScrollBar); v_scroll = memnew(VScrollBar); - add_child(h_scroll); - add_child(v_scroll); + add_child(h_scroll, false, INTERNAL_MODE_FRONT); + add_child(v_scroll, false, INTERNAL_MODE_FRONT); range_click_timer = memnew(Timer); range_click_timer->connect("timeout", callable_mp(this, &Tree::_range_click_timeout)); - add_child(range_click_timer); + add_child(range_click_timer, false, INTERNAL_MODE_FRONT); h_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved)); v_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved)); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index cafa4fa2d2..f2a2648140 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -48,6 +48,7 @@ #include <stdint.h> VARIANT_ENUM_CAST(Node::ProcessMode); +VARIANT_ENUM_CAST(Node::InternalMode); int Node::orphan_node_count = 0; @@ -291,14 +292,40 @@ void Node::_propagate_exit_tree() { void Node::move_child(Node *p_child, int p_pos) { ERR_FAIL_NULL(p_child); - ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1, vformat("Invalid new child position: %d.", p_pos)); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); + + // We need to check whether node is internal and move it only in the relevant node range. + if (p_child->_is_internal_front()) { + ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos)); + _move_child(p_child, p_pos); + } else if (p_child->_is_internal_back()) { + ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos)); + _move_child(p_child, data.children.size() - data.internal_children_back + p_pos); + } else { + ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos)); + _move_child(p_child, p_pos + data.internal_children_front); + } +} + +void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup)."); // Specifying one place beyond the end // means the same as moving to the last position - if (p_pos == data.children.size()) { - p_pos--; + if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly. + if (p_child->_is_internal_front()) { + if (p_pos == data.internal_children_front) { + p_pos--; + } + } else if (p_child->_is_internal_back()) { + if (p_pos == data.children.size()) { + p_pos--; + } + } else { + if (p_pos == data.children.size() - data.internal_children_back) { + p_pos--; + } + } } if (p_child->data.pos == p_pos) { @@ -339,7 +366,14 @@ void Node::raise() { return; } - data.parent->move_child(this, data.parent->data.children.size() - 1); + // Internal children move within a different index range. + if (_is_internal_front()) { + data.parent->move_child(this, data.parent->data.internal_children_front - 1); + } else if (_is_internal_back()) { + data.parent->move_child(this, data.parent->data.internal_children_back - 1); + } else { + data.parent->move_child(this, data.parent->get_child_count(false) - 1); + } } void Node::add_child_notify(Node *p_child) { @@ -483,24 +517,24 @@ void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int } } -void Node::set_network_master(int p_peer_id, bool p_recursive) { - data.network_master = p_peer_id; +void Node::set_network_authority(int p_peer_id, bool p_recursive) { + data.network_authority = p_peer_id; if (p_recursive) { for (int i = 0; i < data.children.size(); i++) { - data.children[i]->set_network_master(p_peer_id, true); + data.children[i]->set_network_authority(p_peer_id, true); } } } -int Node::get_network_master() const { - return data.network_master; +int Node::get_network_authority() const { + return data.network_authority; } -bool Node::is_network_master() const { +bool Node::is_network_authority() const { ERR_FAIL_COND_V(!is_inside_tree(), false); - return get_multiplayer()->get_network_unique_id() == data.network_master; + return get_multiplayer()->get_network_unique_id() == data.network_authority; } /***** RPC CONFIG ********/ @@ -1058,6 +1092,10 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { p_child->data.pos = data.children.size(); data.children.push_back(p_child); p_child->data.parent = this; + + if (data.internal_children_back > 0) { + _move_child(p_child, data.children.size() - data.internal_children_back - 1); + } p_child->notification(NOTIFICATION_PARENTED); if (data.tree) { @@ -1070,7 +1108,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { add_child_notify(p_child); } -void Node::add_child(Node *p_child, bool p_legible_unique_name) { +void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_internal) { ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent @@ -1079,19 +1117,35 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) { #endif ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead."); - /* Validate name */ _validate_child_name(p_child, p_legible_unique_name); - _add_child_nocheck(p_child, p_child->data.name); + + if (p_internal == INTERNAL_MODE_FRONT) { + _move_child(p_child, data.internal_children_front); + data.internal_children_front++; + } else if (p_internal == INTERNAL_MODE_BACK) { + if (data.internal_children_back > 0) { + _move_child(p_child, data.children.size() - 1, true); + } + data.internal_children_back++; + } } void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) { ERR_FAIL_NULL(p_sibling); + ERR_FAIL_NULL(data.parent); ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself! ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_sibling() failed. Consider using call_deferred(\"add_sibling\", sibling) instead."); - get_parent()->add_child(p_sibling, p_legible_unique_name); - get_parent()->move_child(p_sibling, this->get_index() + 1); + InternalMode internal = INTERNAL_MODE_DISABLED; + if (_is_internal_front()) { // The sibling will have the same internal status. + internal = INTERNAL_MODE_FRONT; + } else if (_is_internal_back()) { + internal = INTERNAL_MODE_BACK; + } + + data.parent->add_child(p_sibling, p_legible_unique_name, internal); + data.parent->_move_child(p_sibling, get_index() + 1); } void Node::_propagate_validate_owner() { @@ -1145,7 +1199,12 @@ void Node::remove_child(Node *p_child) { ERR_FAIL_COND_MSG(idx == -1, vformat("Cannot remove child node '%s' as it is not a child of this node.", p_child->get_name())); //ERR_FAIL_COND( p_child->data.blocked > 0 ); - //if (data.scene) { does not matter + // If internal child, update the counter. + if (p_child->_is_internal_front()) { + data.internal_children_front--; + } else if (p_child->_is_internal_back()) { + data.internal_children_back--; + } p_child->_set_tree(nullptr); //} @@ -1175,17 +1234,29 @@ void Node::remove_child(Node *p_child) { } } -int Node::get_child_count() const { - return data.children.size(); +int Node::get_child_count(bool p_include_internal) const { + if (p_include_internal) { + return data.children.size(); + } else { + return data.children.size() - data.internal_children_front - data.internal_children_back; + } } -Node *Node::get_child(int p_index) const { - if (p_index < 0) { - p_index += data.children.size(); +Node *Node::get_child(int p_index, bool p_include_internal) const { + if (p_include_internal) { + if (p_index < 0) { + p_index += data.children.size(); + } + ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr); + return data.children[p_index]; + } else { + if (p_index < 0) { + p_index += data.children.size() - data.internal_children_front - data.internal_children_back; + } + ERR_FAIL_INDEX_V(p_index, data.children.size() - data.internal_children_front - data.internal_children_back, nullptr); + p_index += data.internal_children_front; + return data.children[p_index]; } - ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr); - - return data.children[p_index]; } Node *Node::_get_child_by_name(const StringName &p_name) const { @@ -1717,7 +1788,13 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) { data.blocked--; } -int Node::get_index() const { +int Node::get_index(bool p_include_internal) const { + // p_include_internal = false doesn't make sense if the node is internal. + ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false."); + + if (data.parent && !p_include_internal) { + return data.pos - data.parent->data.internal_children_front; + } return data.pos; } @@ -2429,12 +2506,12 @@ void Node::queue_delete() { } } -TypedArray<Node> Node::_get_children() const { +TypedArray<Node> Node::_get_children(bool p_include_internal) const { TypedArray<Node> arr; - int cc = get_child_count(); + int cc = get_child_count(p_include_internal); arr.resize(cc); for (int i = 0; i < cc; i++) { - arr[i] = get_child(i); + arr[i] = get_child(i, p_include_internal); } return arr; @@ -2581,11 +2658,11 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name); ClassDB::bind_method(D_METHOD("get_name"), &Node::get_name); - ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name"), &Node::add_child, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0)); ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child); - ClassDB::bind_method(D_METHOD("get_child_count"), &Node::get_child_count); - ClassDB::bind_method(D_METHOD("get_children"), &Node::_get_children); - ClassDB::bind_method(D_METHOD("get_child", "idx"), &Node::get_child); + ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript. + ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_child", "idx", "include_internal"), &Node::get_child, DEFVAL(false)); ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node); ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node); ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null); @@ -2609,7 +2686,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner); ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner); ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip); - ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index); + ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false)); ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree); ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty); ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename); @@ -2661,10 +2738,10 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("request_ready"), &Node::request_ready); - ClassDB::bind_method(D_METHOD("set_network_master", "id", "recursive"), &Node::set_network_master, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_network_master"), &Node::get_network_master); + ClassDB::bind_method(D_METHOD("set_network_authority", "id", "recursive"), &Node::set_network_authority, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_network_authority"), &Node::get_network_authority); - ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master); + ClassDB::bind_method(D_METHOD("is_network_authority"), &Node::is_network_authority); ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer); ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer); @@ -2747,6 +2824,10 @@ void Node::_bind_methods() { BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS); BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING); + BIND_ENUM_CONSTANT(INTERNAL_MODE_DISABLED); + BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT); + BIND_ENUM_CONSTANT(INTERNAL_MODE_BACK); + ADD_SIGNAL(MethodInfo("ready")); ADD_SIGNAL(MethodInfo("renamed")); ADD_SIGNAL(MethodInfo("tree_entered")); diff --git a/scene/main/node.h b/scene/main/node.h index a07accba2f..d0246ebe84 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -73,6 +73,12 @@ public: NAME_CASING_SNAKE_CASE }; + enum InternalMode { + INTERNAL_MODE_DISABLED, + INTERNAL_MODE_FRONT, + INTERNAL_MODE_BACK, + }; + struct Comparator { bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); } }; @@ -97,6 +103,8 @@ private: Node *parent = nullptr; Node *owner = nullptr; Vector<Node *> children; + int internal_children_front = 0; + int internal_children_back = 0; int pos = -1; int depth = -1; int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed. @@ -119,7 +127,7 @@ private: ProcessMode process_mode = PROCESS_MODE_INHERIT; Node *process_owner = nullptr; - int network_master = 1; // Server by default. + int network_authority = 1; // Server by default. Vector<MultiplayerAPI::RPCConfig> rpc_methods; // Variables used to properly sort the node when processing, ignored otherwise. @@ -172,12 +180,15 @@ private: void _duplicate_signals(const Node *p_original, Node *p_copy) const; Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = nullptr) const; - TypedArray<Node> _get_children() const; + TypedArray<Node> _get_children(bool p_include_internal = true) const; Array _get_groups() const; Variant _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Variant _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; } + _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; } + friend class SceneTree; void _set_tree(SceneTree *p_tree); @@ -284,12 +295,12 @@ public: StringName get_name() const; void set_name(const String &p_name); - void add_child(Node *p_child, bool p_legible_unique_name = false); + void add_child(Node *p_child, bool p_legible_unique_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED); void add_sibling(Node *p_sibling, bool p_legible_unique_name = false); void remove_child(Node *p_child); - int get_child_count() const; - Node *get_child(int p_index) const; + int get_child_count(bool p_include_internal = true) const; + Node *get_child(int p_index, bool p_include_internal = true) const; bool has_node(const NodePath &p_path) const; Node *get_node(const NodePath &p_path) const; Node *get_node_or_null(const NodePath &p_path) const; @@ -327,6 +338,7 @@ public: int get_persistent_group_count() const; void move_child(Node *p_child, int p_pos); + void _move_child(Node *p_child, int p_pos, bool p_ignore_end = false); void raise(); void set_owner(Node *p_owner); @@ -334,7 +346,7 @@ public: void get_owned_by(Node *p_by, List<Node *> *p_owned); void remove_and_skip(); - int get_index() const; + int get_index(bool p_include_internal = true) const; Ref<Tween> create_tween(); @@ -450,9 +462,9 @@ public: bool is_displayed_folded() const; /* NETWORK */ - void set_network_master(int p_peer_id, bool p_recursive = true); - int get_network_master() const; - bool is_network_master() const; + void set_network_authority(int p_peer_id, bool p_recursive = true); + int get_network_authority() const; + bool is_network_authority() const; uint16_t rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_rpc_mode, MultiplayerPeer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC Vector<MultiplayerAPI::RPCConfig> get_node_rpc_methods() const; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 1fcfce2ea9..ca5a3915d0 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1497,7 +1497,7 @@ void Window::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "position"), "set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,Fullscreen"), "set_mode", "get_mode"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_screen"), "set_current_screen", "get_current_screen"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen"), "set_current_screen", "get_current_screen"); ADD_GROUP("Flags", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 0c97fee711..25c8e3e0b7 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -488,6 +488,7 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(CollisionObject3D); GDREGISTER_VIRTUAL_CLASS(PhysicsBody3D); GDREGISTER_CLASS(StaticBody3D); + GDREGISTER_CLASS(AnimatableBody3D); GDREGISTER_CLASS(RigidBody3D); GDREGISTER_CLASS(KinematicCollision3D); GDREGISTER_CLASS(CharacterBody3D); @@ -647,6 +648,7 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(CollisionObject2D); GDREGISTER_VIRTUAL_CLASS(PhysicsBody2D); GDREGISTER_CLASS(StaticBody2D); + GDREGISTER_CLASS(AnimatableBody2D); GDREGISTER_CLASS(RigidBody2D); GDREGISTER_CLASS(CharacterBody2D); GDREGISTER_CLASS(KinematicCollision2D); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index dbe118a262..b863a309c0 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -424,7 +424,7 @@ Error ResourceLoaderText::load() { } } - if (path.find("://") == -1 && path.is_rel_path()) { + if (path.find("://") == -1 && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); } @@ -768,7 +768,7 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen } } - if (!using_uid && path.find("://") == -1 && path.is_rel_path()) { + if (!using_uid && path.find("://") == -1 && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); } |