diff options
Diffstat (limited to 'scene')
83 files changed, 3771 insertions, 2070 deletions
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index d78b9847e6..33b1c7bcce 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -300,8 +300,8 @@ void Area2D::_clear_monitoring() { body_map.clear(); //disconnect all monitored stuff - for (Map<ObjectID, BodyState>::Element *E = bmcopy.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : bmcopy) { + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (!node) { //node may have been deleted in previous frame or at other legitimate point @@ -311,12 +311,12 @@ void Area2D::_clear_monitoring() { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree)); - if (!E->get().in_tree) { + if (!E.value.in_tree) { continue; } - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E.value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E.value.rid, node, E.value.shapes[i].body_shape, E.value.shapes[i].area_shape); } emit_signal(SceneStringNames::get_singleton()->body_exited, obj); @@ -328,8 +328,8 @@ void Area2D::_clear_monitoring() { area_map.clear(); //disconnect all monitored stuff - for (Map<ObjectID, AreaState>::Element *E = bmcopy.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, AreaState> &E : bmcopy) { + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (!node) { //node may have been deleted in previous frame or at other legitimate point @@ -339,12 +339,12 @@ void Area2D::_clear_monitoring() { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree)); - if (!E->get().in_tree) { + if (!E.value.in_tree) { continue; } - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E.value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E.value.rid, node, E.value.shapes[i].area_shape, E.value.shapes[i].self_shape); } emit_signal(SceneStringNames::get_singleton()->area_exited, obj); @@ -404,8 +404,8 @@ TypedArray<Node2D> Area2D::get_overlapping_bodies() const { TypedArray<Node2D> ret; ret.resize(body_map.size()); int idx = 0; - for (const Map<ObjectID, BodyState>::Element *E = body_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : body_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { @@ -421,8 +421,8 @@ TypedArray<Area2D> Area2D::get_overlapping_areas() const { TypedArray<Area2D> ret; ret.resize(area_map.size()); int idx = 0; - for (const Map<ObjectID, AreaState>::Element *E = area_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, AreaState> &E : area_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 5d3a538f60..6916f832d0 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -342,15 +342,15 @@ real_t CollisionObject2D::get_shape_owner_one_way_collision_margin(uint32_t p_ow } void CollisionObject2D::get_shape_owners(List<uint32_t> *r_owners) { - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - r_owners->push_back(E->key()); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + r_owners->push_back(E.key); } } Array CollisionObject2D::_get_shape_owners() { Array ret; - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - ret.push_back(E->key()); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + ret.push_back(E.key); } return ret; @@ -434,10 +434,10 @@ void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) shapes[p_owner].shapes.remove(p_shape); - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (E->get().shapes[i].index > index_to_remove) { - E->get().shapes.write[i].index -= 1; + for (KeyValue<uint32_t, ShapeData> &E : shapes) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (E.value.shapes[i].index > index_to_remove) { + E.value.shapes.write[i].index -= 1; } } } @@ -456,10 +456,10 @@ void CollisionObject2D::shape_owner_clear_shapes(uint32_t p_owner) { uint32_t CollisionObject2D::shape_find_owner(int p_shape_index) const { ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0); - for (const Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (E->get().shapes[i].index == p_shape_index) { - return E->key(); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (E.value.shapes[i].index == p_shape_index) { + return E.key; } } } diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joint_2d.cpp index 4a6606256e..3b371d4a07 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* joints_2d.cpp */ +/* joint_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "joints_2d.h" +#include "joint_2d.h" #include "physics_body_2d.h" #include "scene/scene_string_names.h" diff --git a/scene/2d/joints_2d.h b/scene/2d/joint_2d.h index dc5a08f815..0c3956e463 100644 --- a/scene/2d/joints_2d.h +++ b/scene/2d/joint_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* joints_2d.h */ +/* joint_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef JOINTS_2D_H -#define JOINTS_2D_H +#ifndef JOINT_2D_H +#define JOINT_2D_H #include "node_2d.h" @@ -148,4 +148,4 @@ public: DampedSpringJoint2D(); }; -#endif // JOINTS_2D_H +#endif // JOINT_2D_H diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index 48817679bc..c1b0bc35dd 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -30,6 +30,8 @@ #include "physical_bone_2d.h" +#include "scene/2d/joint_2d.h" + void PhysicalBone2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h index a250d0aadd..8b41f75c3e 100644 --- a/scene/2d/physical_bone_2d.h +++ b/scene/2d/physical_bone_2d.h @@ -31,11 +31,11 @@ #ifndef PHYSICAL_BONE_2D_H #define PHYSICAL_BONE_2D_H -#include "scene/2d/joints_2d.h" #include "scene/2d/physics_body_2d.h" - #include "scene/2d/skeleton_2d.h" +class Joint2D; + class PhysicalBone2D : public RigidDynamicBody2D { GDCLASS(PhysicalBone2D, RigidDynamicBody2D); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index c07a999588..f493d97ceb 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -54,13 +54,14 @@ PhysicsBody2D::~PhysicsBody2D() { } } -Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin) { - PhysicsServer2D::MotionResult result; - +Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_linear_velocity, bool p_test_only, real_t p_margin) { // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); - if (move_and_collide(p_motion * delta, result, p_margin, p_test_only)) { + PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin); + + PhysicsServer2D::MotionResult result; + if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -74,18 +75,18 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_t return Ref<KinematicCollision2D>(); } -bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) { +bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only, bool p_cancel_sliding) { if (is_only_update_transform_changes_enabled()) { ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation."); } - Transform2D gt = get_global_transform(); - bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude); + + bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_parameters, &r_result); // Restore direction of motion to be along original motion, // in order to avoid sliding due to recovery, // but only if collision depth is low enough to avoid tunneling. if (p_cancel_sliding) { - real_t motion_length = p_motion.length(); + real_t motion_length = p_parameters.motion.length(); real_t precision = 0.001; if (colliding) { @@ -93,7 +94,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M // so even in normal resting cases the depth can be a bit more than the margin. precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction); - if (r_result.collision_depth > (real_t)p_margin + precision) { + if (r_result.collision_depth > p_parameters.margin + precision) { p_cancel_sliding = false; } } @@ -102,7 +103,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M // When motion is null, recovery is the resulting motion. Vector2 motion_normal; if (motion_length > CMP_EPSILON) { - motion_normal = p_motion / motion_length; + motion_normal = p_parameters.motion / motion_length; } // Check depth of recovery. @@ -111,15 +112,16 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M real_t recovery_length = recovery.length(); // Fixes cases where canceling slide causes the motion to go too deep into the ground, // because we're only taking rest information into account and not general recovery. - if (recovery_length < (real_t)p_margin + precision) { + if (recovery_length < p_parameters.margin + precision) { // Apply adjustment to motion. r_result.travel = motion_normal * projected_length; - r_result.remainder = p_motion - r_result.travel; + r_result.remainder = p_parameters.motion - r_result.travel; } } } if (!p_test_only) { + Transform2D gt = p_parameters.from; gt.elements[2] += r_result.travel; set_global_transform(gt); } @@ -127,7 +129,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M return colliding; } -bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) { +bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) { ERR_FAIL_COND_V(!is_inside_tree(), false); PhysicsServer2D::MotionResult *r = nullptr; @@ -139,7 +141,9 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); - return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r); + PhysicsServer2D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin); + + return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); } TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() { @@ -288,6 +292,12 @@ void AnimatableBody2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { last_valid_transform = get_global_transform(); + _update_kinematic_motion(); + } break; + + case NOTIFICATION_EXIT_TREE: { + set_only_update_transform_changes(false); + set_notify_local_transform(false); } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { @@ -313,7 +323,6 @@ void AnimatableBody2D::_bind_methods() { AnimatableBody2D::AnimatableBody2D() : StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) { - _update_kinematic_motion(); } void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) { @@ -457,9 +466,9 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) //untag all int rc = 0; - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - E->get().shapes[i].tagged = false; + for (KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + for (int i = 0; i < E.value.shapes.size(); i++) { + E.value.shapes[i].tagged = false; rc++; } } @@ -503,12 +512,12 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) //put the ones to remove - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (!E->get().shapes[i].tagged) { - toremove[toremove_count].rid = E->get().rid; - toremove[toremove_count].body_id = E->key(); - toremove[toremove_count].pair = E->get().shapes[i]; + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (!E.value.shapes[i].tagged) { + toremove[toremove_count].rid = E.value.rid; + toremove[toremove_count].body_id = E.key; + toremove[toremove_count].pair = E.value.shapes[i]; toremove_count++; } } @@ -815,8 +824,8 @@ TypedArray<Node2D> RigidDynamicBody2D::get_colliding_bodies() const { TypedArray<Node2D> ret; ret.resize(contact_monitor->body_map.size()); int idx = 0; - for (const Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { @@ -835,9 +844,9 @@ void RigidDynamicBody2D::set_contact_monitor(bool p_enabled) { if (!p_enabled) { ERR_FAIL_COND_MSG(contact_monitor->locked, "Can't disable contact monitoring during in/out callback. Use call_deferred(\"set_contact_monitor\", false) instead."); - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { //clean up mess - Object *obj = ObjectDB::get_instance(E->key()); + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (node) { @@ -1047,6 +1056,8 @@ bool CharacterBody2D::move_and_slide() { double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); Vector2 current_platform_velocity = platform_velocity; + Transform2D gt = get_global_transform(); + previous_position = gt.elements[2]; if ((on_floor || on_wall) && platform_rid.is_valid()) { bool excluded = false; @@ -1059,7 +1070,6 @@ bool CharacterBody2D::move_and_slide() { //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(); Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2]; current_platform_velocity = bs->get_velocity_at_local_position(local_position); } @@ -1069,6 +1079,7 @@ bool CharacterBody2D::move_and_slide() { } motion_results.clear(); + last_motion = Vector2(); bool was_on_floor = on_floor; on_floor = false; @@ -1076,10 +1087,14 @@ bool CharacterBody2D::move_and_slide() { on_wall = false; if (!current_platform_velocity.is_equal_approx(Vector2())) { + PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); + parameters.exclude_bodies.insert(platform_rid); + if (platform_object_id.is_valid()) { + parameters.exclude_objects.insert(platform_object_id); + } + PhysicsServer2D::MotionResult floor_result; - Set<RID> exclude; - exclude.insert(platform_rid); - if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, false, false, exclude)) { + if (move_and_collide(parameters, floor_result, false, false)) { motion_results.push_back(floor_result); _set_collision_direction(floor_result); } @@ -1091,23 +1106,33 @@ bool CharacterBody2D::move_and_slide() { _move_and_slide_free(delta); } - if (!on_floor && !on_wall) { + // Compute real velocity. + real_velocity = get_position_delta() / delta; + + if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) { // Add last platform velocity when just left a moving platform. - linear_velocity += current_platform_velocity; + if (!on_floor && !on_wall) { + if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { + current_platform_velocity = current_platform_velocity.slide(up_direction); + } + motion_velocity += current_platform_velocity; + } } return motion_results.size() > 0; } 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 = motion_velocity * p_delta; Vector2 motion_slide_up = motion.slide(up_direction); Vector2 prev_floor_normal = floor_normal; RID prev_platform_rid = platform_rid; + ObjectID prev_platform_object_id = platform_object_id; int prev_platform_layer = platform_layer; platform_rid = RID(); + platform_object_id = ObjectID(); floor_normal = Vector2(); platform_velocity = Vector2(); @@ -1116,28 +1141,47 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo bool sliding_enabled = !floor_stop_on_slope; // Constant speed can be applied only the first time sliding is enabled. bool can_apply_constant_speed = sliding_enabled; + // If the platform's ceiling push down the body. + bool apply_ceiling_velocity = false; bool first_slide = true; - bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0; + bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0; Vector2 last_travel; for (int iteration = 0; iteration < max_slides; ++iteration) { - PhysicsServer2D::MotionResult result; + PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); + + Vector2 prev_position = parameters.from.elements[2]; - Vector2 prev_position = get_global_transform().elements[2]; + PhysicsServer2D::MotionResult result; + bool collided = move_and_collide(parameters, result, false, !sliding_enabled); - bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled); + last_motion = result.travel; if (collided) { motion_results.push_back(result); _set_collision_direction(result); - if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { + // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one. + if (on_ceiling && result.collider_velocity != Vector2() && result.collider_velocity.dot(up_direction) < 0) { + // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward. + if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (result.collision_normal + up_direction).length() < 0.01) { + apply_ceiling_velocity = true; + Vector2 ceiling_vertical_velocity = up_direction * up_direction.dot(result.collider_velocity); + Vector2 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity); + if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) { + motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction); + } + } + } + + if (on_floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) { Transform2D gt = get_global_transform(); if (result.travel.length() <= margin + CMP_EPSILON) { gt.elements[2] -= result.travel; } set_global_transform(gt); - linear_velocity = Vector2(); + motion_velocity = Vector2(); + last_motion = Vector2(); motion = Vector2(); break; } @@ -1160,10 +1204,12 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo } on_floor = true; platform_rid = prev_platform_rid; + platform_object_id = prev_platform_object_id; platform_layer = prev_platform_layer; platform_velocity = p_prev_platform_velocity; floor_normal = prev_floor_normal; - linear_velocity = Vector2(); + motion_velocity = Vector2(); + last_motion = Vector2(); motion = Vector2(); break; } @@ -1182,9 +1228,9 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo motion = motion_slide_norm * (motion_slide_up.length() - result.travel.slide(up_direction).length() - last_travel.slide(up_direction).length()); } // Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling. - else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up)) { + else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) { Vector2 slide_motion = result.remainder.slide(result.collision_normal); - if (slide_motion.dot(linear_velocity) > 0.0) { + if (slide_motion.dot(motion_velocity) > 0.0) { motion = slide_motion; } else { motion = Vector2(); @@ -1192,10 +1238,10 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (slide_on_ceiling && on_ceiling) { // Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall. if (vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(result.collision_normal); + motion_velocity = motion_velocity.slide(result.collision_normal); } else { // Avoid acceleration in slope when falling. - linear_velocity = up_direction * up_direction.dot(linear_velocity); + motion_velocity = up_direction * up_direction.dot(motion_velocity); } } } @@ -1203,7 +1249,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo else { motion = result.remainder; if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(up_direction); + motion_velocity = motion_velocity.slide(up_direction); motion = motion.slide(up_direction); } } @@ -1237,28 +1283,37 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Reset the gravity accumulation when touching the ground. if (on_floor && !vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(up_direction); + motion_velocity = motion_velocity.slide(up_direction); } } void CharacterBody2D::_move_and_slide_free(double p_delta) { - Vector2 motion = linear_velocity * p_delta; + Vector2 motion = motion_velocity * p_delta; platform_rid = RID(); + platform_object_id = ObjectID(); floor_normal = Vector2(); platform_velocity = Vector2(); bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { + PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); + PhysicsServer2D::MotionResult result; + bool collided = move_and_collide(parameters, result, false, false); - bool collided = move_and_collide(motion, result, margin, false, false); + last_motion = result.travel; if (collided) { motion_results.push_back(result); _set_collision_direction(result); - if (free_mode_min_slide_angle != 0 && result.get_angle(-linear_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { + if (result.remainder.is_equal_approx(Vector2())) { + motion = Vector2(); + break; + } + + if (free_mode_min_slide_angle != 0 && result.get_angle(-motion_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { motion = Vector2(); } else if (first_slide) { Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized(); @@ -1267,16 +1322,16 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) { motion = result.remainder.slide(result.collision_normal); } - if (motion.dot(linear_velocity) <= 0.0) { + if (motion.dot(motion_velocity) <= 0.0) { motion = Vector2(); } } - first_slide = false; - if (!collided || motion.is_equal_approx(Vector2())) { break; } + + first_slide = false; } } @@ -1288,10 +1343,11 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) // Snap by at least collision margin to keep floor state consistent. real_t length = MAX(floor_snap_length, margin); - Transform2D gt = get_global_transform(); + PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.collide_separation_ray = true; + PhysicsServer2D::MotionResult result; - if (move_and_collide(-up_direction * length, result, margin, true, false, true)) { - bool apply = true; + if (move_and_collide(parameters, result, true, false)) { if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { on_floor = true; floor_normal = result.collision_normal; @@ -1306,13 +1362,9 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) result.travel = Vector2(); } } - } else { - apply = false; - } - if (apply) { - gt.elements[2] += result.travel; - set_global_transform(gt); + parameters.from.elements[2] += result.travel; + set_global_transform(parameters.from); } } } @@ -1325,8 +1377,11 @@ bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin // Snap by at least collision margin to keep floor state consistent. real_t length = MAX(floor_snap_length, margin); + PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.collide_separation_ray = true; + PhysicsServer2D::MotionResult result; - if (move_and_collide(-up_direction * length, result, margin, true, false, true)) { + if (move_and_collide(parameters, result, true, false)) { if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { return true; } @@ -1344,6 +1399,7 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu on_ceiling = true; } else { on_wall = true; + wall_normal = p_result.collision_normal; // Don't apply wall velocity when the collider is a CharacterBody2D. if (Object::cast_to<CharacterBody2D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) { _set_platform_data(p_result); @@ -1353,16 +1409,17 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) { platform_rid = p_result.collider; + platform_object_id = p_result.collider_id; platform_velocity = p_result.collider_velocity; platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid); } -const Vector2 &CharacterBody2D::get_linear_velocity() const { - return linear_velocity; +const Vector2 &CharacterBody2D::get_motion_velocity() const { + return motion_velocity; } -void CharacterBody2D::set_linear_velocity(const Vector2 &p_velocity) { - linear_velocity = p_velocity; +void CharacterBody2D::set_motion_velocity(const Vector2 &p_velocity) { + motion_velocity = p_velocity; } bool CharacterBody2D::is_on_floor() const { @@ -1389,16 +1446,32 @@ bool CharacterBody2D::is_on_ceiling_only() const { return on_ceiling && !on_floor && !on_wall; } -Vector2 CharacterBody2D::get_floor_normal() const { +const Vector2 &CharacterBody2D::get_floor_normal() const { return floor_normal; } +const Vector2 &CharacterBody2D::get_wall_normal() const { + return wall_normal; +} + +const Vector2 &CharacterBody2D::get_last_motion() const { + return last_motion; +} + +Vector2 CharacterBody2D::get_position_delta() const { + return get_global_transform().elements[2] - previous_position; +} + +const Vector2 &CharacterBody2D::get_real_velocity() const { + return real_velocity; +} + real_t CharacterBody2D::get_floor_angle(const Vector2 &p_up_direction) const { ERR_FAIL_COND_V(p_up_direction == Vector2(), 0); return Math::acos(floor_normal.dot(p_up_direction)); } -Vector2 CharacterBody2D::get_platform_velocity() const { +const Vector2 &CharacterBody2D::get_platform_velocity() const { return platform_velocity; } @@ -1498,6 +1571,14 @@ CharacterBody2D::MotionMode CharacterBody2D::get_motion_mode() const { return motion_mode; } +void CharacterBody2D::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) { + moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity; +} + +CharacterBody2D::MovingPlatformApplyVelocityOnLeave CharacterBody2D::get_moving_platform_apply_velocity_on_leave() const { + return moving_platform_apply_velocity_on_leave; +} + int CharacterBody2D::get_max_slides() const { return max_slides; } @@ -1547,6 +1628,7 @@ void CharacterBody2D::_notification(int p_what) { // Reset move_and_slide() data. on_floor = false; platform_rid = RID(); + platform_object_id = ObjectID(); on_ceiling = false; on_wall = false; motion_results.clear(); @@ -1558,8 +1640,8 @@ void CharacterBody2D::_notification(int p_what) { void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody2D::move_and_slide); - ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody2D::set_linear_velocity); - ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody2D::get_linear_velocity); + ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody2D::set_motion_velocity); + ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody2D::get_motion_velocity); ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin); @@ -1589,6 +1671,8 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction); ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody2D::set_motion_mode); ClassDB::bind_method(D_METHOD("get_motion_mode"), &CharacterBody2D::get_motion_mode); + ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &CharacterBody2D::set_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &CharacterBody2D::get_moving_platform_apply_velocity_on_leave); ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody2D::is_on_floor); ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody2D::is_on_floor_only); @@ -1597,6 +1681,10 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody2D::is_on_wall); ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody2D::is_on_wall_only); ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody2D::get_floor_normal); + ClassDB::bind_method(D_METHOD("get_wall_normal"), &CharacterBody2D::get_wall_normal); + ClassDB::bind_method(D_METHOD("get_last_motion"), &CharacterBody2D::get_last_motion); + ClassDB::bind_method(D_METHOD("get_position_delta"), &CharacterBody2D::get_position_delta); + ClassDB::bind_method(D_METHOD("get_real_velocity"), &CharacterBody2D::get_real_velocity); ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody2D::get_floor_angle, DEFVAL(Vector2(0.0, -1.0))); ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody2D::get_platform_velocity); ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody2D::get_slide_collision_count); @@ -1604,10 +1692,11 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody2D::_get_last_slide_collision); ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); + ADD_GROUP("Free Mode", "free_mode_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "free_mode_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_free_mode_min_slide_angle", "get_free_mode_min_slide_angle"); ADD_GROUP("Floor", "floor_"); @@ -1617,12 +1706,17 @@ 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,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length"); ADD_GROUP("Moving platform", "moving_platform"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED); BIND_ENUM_CONSTANT(MOTION_MODE_FREE); + + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER); } void CharacterBody2D::_validate_property(PropertyInfo &property) const { @@ -1717,10 +1811,6 @@ Vector2 KinematicCollision2D::get_collider_velocity() const { return result.collider_velocity; } -Variant KinematicCollision2D::get_collider_metadata() const { - return Variant(); -} - void KinematicCollision2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_position"), &KinematicCollision2D::get_position); ClassDB::bind_method(D_METHOD("get_normal"), &KinematicCollision2D::get_normal); @@ -1734,18 +1824,4 @@ void KinematicCollision2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicCollision2D::get_collider_shape); ClassDB::bind_method(D_METHOD("get_collider_shape_index"), &KinematicCollision2D::get_collider_shape_index); ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicCollision2D::get_collider_velocity); - ClassDB::bind_method(D_METHOD("get_collider_metadata"), &KinematicCollision2D::get_collider_metadata); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "", "get_position"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "", "get_normal"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "travel"), "", "get_travel"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "remainder"), "", "get_remainder"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_local_shape"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_collider"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_collider_id"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_collider_rid"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_collider_shape"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_collider_shape_index"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collider_velocity"), "", "get_collider_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::NIL, "collider_metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "", "get_collider_metadata"); } diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index e789ac4cb7..d1f52b33f2 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -47,11 +47,11 @@ protected: Ref<KinematicCollision2D> motion_cache; - Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_test_only = false, real_t p_margin = 0.08); + Ref<KinematicCollision2D> _move(const Vector2 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.08); public: - bool move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()); - bool test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08); + bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true); + bool test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08); TypedArray<PhysicsBody2D> get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody @@ -310,10 +310,15 @@ public: MOTION_MODE_GROUNDED, MOTION_MODE_FREE, }; + enum MovingPlatformApplyVelocityOnLeave { + PLATFORM_VEL_ON_LEAVE_ALWAYS, + PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY, + PLATFORM_VEL_ON_LEAVE_NEVER, + }; bool move_and_slide(); - const Vector2 &get_linear_velocity() const; - void set_linear_velocity(const Vector2 &p_velocity); + const Vector2 &get_motion_velocity() const; + void set_motion_velocity(const Vector2 &p_velocity); bool is_on_floor() const; bool is_on_floor_only() const; @@ -321,9 +326,14 @@ public: bool is_on_wall_only() const; bool is_on_ceiling() const; bool is_on_ceiling_only() const; - Vector2 get_floor_normal() const; + const Vector2 &get_last_motion() const; + Vector2 get_position_delta() const; + const Vector2 &get_floor_normal() const; + const Vector2 &get_wall_normal() const; + const Vector2 &get_real_velocity() const; + real_t get_floor_angle(const Vector2 &p_up_direction = Vector2(0.0, -1.0)) const; - Vector2 get_platform_velocity() const; + const Vector2 &get_platform_velocity() const; int get_slide_collision_count() const; PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const; @@ -334,24 +344,31 @@ public: private: real_t margin = 0.08; MotionMode motion_mode = MOTION_MODE_GROUNDED; + MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS; bool floor_constant_speed = false; bool floor_stop_on_slope = true; bool floor_block_on_wall = true; bool slide_on_ceiling = true; int max_slides = 4; - int platform_layer; + int platform_layer = 0; real_t floor_max_angle = Math::deg2rad((real_t)45.0); real_t floor_snap_length = 1; real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0); Vector2 up_direction = Vector2(0.0, -1.0); uint32_t moving_platform_floor_layers = UINT32_MAX; uint32_t moving_platform_wall_layers = 0; - Vector2 linear_velocity; + Vector2 motion_velocity; Vector2 floor_normal; Vector2 platform_velocity; + Vector2 wall_normal; + Vector2 last_motion; + Vector2 previous_position; + Vector2 real_velocity; + RID platform_rid; + ObjectID platform_object_id; bool on_floor = false; bool on_ceiling = false; bool on_wall = false; @@ -395,6 +412,9 @@ private: void set_motion_mode(MotionMode p_mode); MotionMode get_motion_mode() const; + void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity); + MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const; + 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); @@ -414,6 +434,7 @@ protected: }; VARIANT_ENUM_CAST(CharacterBody2D::MotionMode); +VARIANT_ENUM_CAST(CharacterBody2D::MovingPlatformApplyVelocityOnLeave); class KinematicCollision2D : public RefCounted { GDCLASS(KinematicCollision2D, RefCounted); @@ -439,7 +460,6 @@ public: Object *get_collider_shape() const; int get_collider_shape_index() const; Vector2 get_collider_velocity() const; - Variant get_collider_metadata() const; }; #endif // PHYSICS_BODY_2D_H diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 929233e4e0..222ec986b0 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -51,8 +51,8 @@ void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) { pattern.erase(p_coords); if (p_update_size) { size = Vector2i(); - for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { - size = size.max(E->key() + Vector2i(1, 1)); + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + size = size.max(E.key + Vector2i(1, 1)); } } } @@ -80,8 +80,8 @@ TypedArray<Vector2i> TileMapPattern::get_used_cells() const { TypedArray<Vector2i> a; a.resize(pattern.size()); int i = 0; - for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { - Vector2i p(E->key().x, E->key().y); + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + Vector2i p(E.key.x, E.key.y); a[i++] = p; } @@ -93,8 +93,8 @@ Vector2i TileMapPattern::get_size() const { } void TileMapPattern::set_size(const Vector2i &p_size) { - for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { - Vector2i coords = E->key(); + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + Vector2i coords = E.key; if (p_size.x <= coords.x || p_size.y <= coords.y) { ERR_FAIL_MSG(vformat("Cannot set pattern size to %s, it contains a tile at %s. Size can only be increased.", p_size, coords)); }; @@ -452,6 +452,19 @@ int TileMap::get_layer_z_index(int p_layer) const { return layers[p_layer].z_index; } +void TileMap::set_collision_animatable(bool p_enabled) { + collision_animatable = p_enabled; + _clear_internals(); + set_notify_local_transform(p_enabled); + set_physics_process_internal(p_enabled); + _recreate_internals(); + emit_signal(SNAME("changed")); +} + +bool TileMap::is_collision_animatable() const { + return collision_animatable; +} + void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) { collision_visibility_mode = p_show_collision; _clear_internals(); @@ -508,7 +521,6 @@ Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer, // Call the create_quadrant method on plugins if (tile_set.is_valid()) { _rendering_create_quadrant(&q); - _physics_create_quadrant(&q); } return layers[p_layer].quadrant_map.insert(p_qk, q); @@ -526,9 +538,9 @@ void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q) { void TileMap::_make_all_quadrants_dirty() { // Make all quandrants dirty, then trigger an update later. for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - if (!E->value().dirty_list_element.in_list()) { - layers[layer].dirty_quadrant_list.add(&E->value().dirty_list_element); + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + if (!E.value.dirty_list_element.in_list()) { + layers[layer].dirty_quadrant_list.add(&E.value.dirty_list_element); } } } @@ -610,8 +622,8 @@ void TileMap::_recreate_internals() { // Recreate the quadrants. const Map<Vector2i, TileMapCell> &tile_map = layers[layer].tile_map; - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E->key().x, E->key().y)); + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E.key.x, E.key.y)); Map<Vector2i, TileMapQuadrant>::Element *Q = layers[layer].quadrant_map.find(qk); if (!Q) { @@ -619,7 +631,7 @@ void TileMap::_recreate_internals() { layers[layer].dirty_quadrant_list.add(&Q->get().dirty_list_element); } - Vector2i pk = E->key(); + Vector2i pk = E.key; Q->get().cells.insert(pk); _make_quadrant_dirty(Q); @@ -688,7 +700,7 @@ void TileMap::_recompute_rect_cache() { Rect2 r_total; for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + for (const Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { Rect2 r; r.position = map_to_world(E->key() * get_effective_quadrant_size(layer)); r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size(layer))); @@ -717,13 +729,13 @@ void TileMap::_rendering_notification(int p_what) { case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { bool visible = is_visible_in_tree(); for (int layer = 0; layer < (int)layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { Transform2D xform; - xform.set_origin(E_cell->key()); + xform.set_origin(E_cell.key); for (const RID &occluder : q.occluders) { RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, visible); } @@ -736,13 +748,13 @@ void TileMap::_rendering_notification(int p_what) { return; } for (int layer = 0; layer < (int)layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { Transform2D xform; - xform.set_origin(E_cell->key()); + xform.set_origin(E_cell.key); for (const RID &occluder : q.occluders) { RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform); } @@ -821,8 +833,8 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List RID prev_canvas_item; // Iterate over the cells of the quadrant. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = get_cell(q.layer, E_cell->value(), true); + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + TileMapCell c = get_cell(q.layer, E_cell.value, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -891,12 +903,12 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List modulate.a *= 0.3; } } - draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate); + draw_tile(canvas_item, E_cell.key - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate); // --- Occluders --- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { Transform2D xform; - xform.set_origin(E_cell->key()); + xform.set_origin(E_cell.key); if (tile_data->get_occluder(i).is_valid()) { RID occluder_id = rs->canvas_light_occluder_create(); rs->canvas_light_occluder_set_enabled(occluder_id, visible); @@ -922,13 +934,13 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List for (int layer = 0; layer < (int)layers.size(); layer++) { // Sort the quadrants coords per world coordinates Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - world_to_map[map_to_world(E->key())] = E->key(); + for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + world_to_map[map_to_world(E.key)] = E.key; } // Sort the quadrants - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) { - TileMapQuadrant &q = layers[layer].quadrant_map[E->value()]; + for (const KeyValue<Vector2i, Vector2i> &E : world_to_map) { + TileMapQuadrant &q = layers[layer].quadrant_map[E.value]; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_draw_index(ci, index++); } @@ -1092,24 +1104,67 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe void TileMap::_physics_notification(int p_what) { switch (p_what) { + case CanvasItem::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + if (is_inside_tree() && collision_animatable && !in_editor) { + // Update tranform on the physics tick when in animatable mode. + last_valid_transform = new_transform; + set_notify_local_transform(false); + set_global_transform(new_transform); + set_notify_local_transform(true); + } + } break; case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { - // Update the bodies transforms. - if (is_inside_tree()) { + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + if (is_inside_tree() && (!collision_animatable || in_editor)) { + // Update the new transform directly if we are not in animatable mode. + Transform2D global_transform = get_global_transform(); for (int layer = 0; layer < (int)layers.size(); layer++) { - Transform2D global_transform = get_global_transform(); + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + TileMapQuadrant &q = E.value; - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); + for (RID body : q.bodies) { + Transform2D xform; + xform.set_origin(map_to_world(bodies_coords[body])); + xform = global_transform * xform; + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } + } + } break; + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + if (is_inside_tree() && !in_editor && collision_animatable) { + // Only active when animatable. Send the new transform to the physics... + new_transform = get_global_transform(); + for (int layer = 0; layer < (int)layers.size(); layer++) { + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + TileMapQuadrant &q = E.value; - Transform2D xform; - xform.set_origin(map_to_world(E->key() * get_effective_quadrant_size(layer))); - xform = global_transform * xform; + for (RID body : q.bodies) { + Transform2D xform; + xform.set_origin(map_to_world(bodies_coords[body])); + xform = new_transform * xform; - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); } } } + + // ... but then revert changes. + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); } } break; } @@ -1120,29 +1175,23 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r ERR_FAIL_COND(!tile_set.is_valid()); Transform2D global_transform = get_global_transform(); + last_valid_transform = global_transform; + new_transform = global_transform; PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + RID space = get_world_2d()->get_space(); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); while (q_list_element) { TileMapQuadrant &q = *q_list_element->self(); - Vector2 quadrant_pos = map_to_world(q.coords * get_effective_quadrant_size(q.layer)); - - LocalVector<int> body_shape_count; - body_shape_count.resize(q.bodies.size()); - - // Clear shapes. - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - ps->body_clear_shapes(q.bodies[body_index]); - body_shape_count[body_index] = 0; - - // Position the bodies. - Transform2D xform; - xform.set_origin(quadrant_pos); - xform = global_transform * xform; - ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + // Clear bodies. + for (RID body : q.bodies) { + bodies_coords.erase(body); + ps->free(body); } + q.bodies.clear(); + // Recreate bodies and shapes. for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { TileMapCell c = get_cell(q.layer, E_cell->get(), true); @@ -1158,26 +1207,53 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r if (atlas_source) { TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - int &body_shape_index = body_shape_count[body_index]; + for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) { + Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer); + uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer); + uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer); - // Add the shapes again. - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { - bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); - float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); + // Create the body. + RID body = ps->body_create(); + bodies_coords[body] = E_cell->get(); + ps->body_set_mode(body, collision_animatable ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC); + ps->body_set_space(body, space); - int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); - for (int shape_index = 0; shape_index < shapes_count; shape_index++) { - Transform2D xform = Transform2D(); - xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get())); + xform = global_transform * xform; + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + + ps->body_attach_object_instance_id(body, get_instance_id()); + ps->body_set_collision_layer(body, physics_layer); + ps->body_set_collision_mask(body, physics_mask); + ps->body_set_pickable(body, false); + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, tile_data->get_constant_linear_velocity(tile_set_physics_layer)); + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, tile_data->get_constant_angular_velocity(tile_set_physics_layer)); + + if (!physics_material.is_valid()) { + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); + } else { + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); + } + + q.bodies.push_back(body); + // Add the shapes to the body. + int body_shape_index = 0; + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(tile_set_physics_layer); polygon_index++) { + // Iterate over the polygons. + bool one_way_collision = tile_data->is_collision_polygon_one_way(tile_set_physics_layer, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(tile_set_physics_layer, polygon_index); + int shapes_count = tile_data->get_collision_polygon_shapes_count(tile_set_physics_layer, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { // Add decomposed convex shapes. - Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); - ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); - ps->body_set_shape_metadata(q.bodies[body_index], body_shape_index, E_cell->get()); - ps->body_set_shape_as_one_way_collision(q.bodies[body_index], body_shape_index, one_way_collision, one_way_collision_margin); + Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index); + ps->body_add_shape(body, shape->get_rid()); + ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin); - ++body_shape_index; + body_shape_index++; } } } @@ -1189,54 +1265,11 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r } } -void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) { - ERR_FAIL_COND(!tile_set.is_valid()); - - //Get the TileMap's gobla transform. - Transform2D global_transform; - if (is_inside_tree()) { - global_transform = get_global_transform(); - } - - // Clear all bodies. - p_quadrant->bodies.clear(); - - // Create the body and set its parameters. - for (int layer = 0; layer < tile_set->get_physics_layers_count(); layer++) { - RID body = PhysicsServer2D::get_singleton()->body_create(); - PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC); - - PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, get_instance_id()); - PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer)); - PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer)); - - Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer); - if (!physics_material.is_valid()) { - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); - } else { - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); - } - - if (is_inside_tree()) { - RID space = get_world_2d()->get_space(); - PhysicsServer2D::get_singleton()->body_set_space(body, space); - - Transform2D xform; - 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); - } - - p_quadrant->bodies.push_back(body); - } -} - void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Remove a quadrant. - for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]); + for (RID body : p_quadrant->bodies) { + bodies_coords.erase(body); + PhysicsServer2D::get_singleton()->free(body); } p_quadrant->bodies.clear(); } @@ -1252,7 +1285,7 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { bool show_collision = false; switch (collision_visibility_mode) { case TileMap::VISIBILITY_MODE_DEFAULT: - show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint()); + show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_collisions_hint()); break; case TileMap::VISIBILITY_MODE_FORCE_HIDE: show_collision = false; @@ -1266,39 +1299,28 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } RenderingServer *rs = RenderingServer::get_singleton(); - - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); Color debug_collision_color = get_tree()->get_debug_collisions_color(); - for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true); - - Transform2D xform; - xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + Vector<Color> color; + color.push_back(debug_collision_color); - if (tile_set->has_source(c.source_id)) { - TileSetSource *source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Transform2D qudrant_xform; + qudrant_xform.set_origin(quadrant_pos); + Transform2D global_transform_inv = (get_global_transform() * qudrant_xform).affine_inverse(); - for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { - // Draw the debug polygon. - Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); - if (polygon.size() >= 3) { - Vector<Color> color; - color.push_back(debug_collision_color); - rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); - } - } - } + for (RID body : p_quadrant->bodies) { + Transform2D xform = Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)) * global_transform_inv; + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + for (int shape_index = 0; shape_index < ps->body_get_shape_count(body); shape_index++) { + const RID &shape = ps->body_get_shape(body, shape_index); + PhysicsServer2D::ShapeType type = ps->shape_get_type(shape); + if (type == PhysicsServer2D::SHAPE_CONVEX_POLYGON) { + Vector<Vector2> polygon = ps->shape_get_data(shape); + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); + } else { + WARN_PRINT("Wrong shape type for a tile, should be SHAPE_CONVEX_POLYGON."); } } rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D()); @@ -1313,16 +1335,16 @@ void TileMap::_navigation_notification(int p_what) { if (is_inside_tree()) { for (int layer = 0; layer < (int)layers.size(); layer++) { Transform2D tilemap_xform = get_global_transform(); - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); - for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) { - for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) { - RID region = E_region->get()[layer_index]; + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; + for (const KeyValue<Vector2i, Vector<RID>> &E_region : q.navigation_regions) { + for (int layer_index = 0; layer_index < E_region.value.size(); layer_index++) { + RID region = E_region.value[layer_index]; if (!region.is_valid()) { continue; } Transform2D tile_transform; - tile_transform.set_origin(map_to_world(E_region->key())); + tile_transform.set_origin(map_to_world(E_region.key)); NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); } } @@ -1351,9 +1373,9 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List TileMapQuadrant &q = *q_list_element->self(); // Clear navigation shapes in the quadrant. - for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) { - for (int i = 0; i < E->get().size(); i++) { - RID region = E->get()[i]; + for (const KeyValue<Vector2i, Vector<RID>> &E : q.navigation_regions) { + for (int i = 0; i < E.value.size(); i++) { + RID region = E.value[i]; if (!region.is_valid()) { continue; } @@ -1404,9 +1426,9 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Clear navigation shapes in the quadrant. - for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) { - for (int i = 0; i < E->get().size(); i++) { - RID region = E->get()[i]; + for (const KeyValue<Vector2i, Vector<RID>> &E : p_quadrant->navigation_regions) { + for (int i = 0; i < E.value.size(); i++) { + RID region = E.value[i]; if (!region.is_valid()) { continue; } @@ -1507,8 +1529,8 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ TileMapQuadrant &q = *q_list_element->self(); // Clear the scenes. - for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) { - Node *node = get_node(E->get()); + for (const KeyValue<Vector2i, String> &E : q.scenes) { + Node *node = get_node(E.value); if (node) { node->queue_delete(); } @@ -1555,8 +1577,8 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Clear the scenes. - for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) { - Node *node = get_node(E->get()); + for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) { + Node *node = get_node(E.value); if (node) { node->queue_delete(); } @@ -1858,16 +1880,21 @@ Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) { return &layers[p_layer].quadrant_map; } +Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) { + ERR_FAIL_COND_V_MSG(!bodies_coords.has(p_physics_body), Vector2i(), vformat("No tiles for the given body RID %d.", p_physics_body)); + return bodies_coords[p_physics_body]; +} + void TileMap::fix_invalid_tiles() { ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); for (unsigned int i = 0; i < layers.size(); i++) { const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; Set<Vector2i> coords; - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - TileSetSource *source = *tile_set->get_source(E->get().source_id); - if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { - coords.insert(E->key()); + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + TileSetSource *source = *tile_set->get_source(E.value.source_id); + if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) { + coords.insert(E.key); } } for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { @@ -1990,14 +2017,14 @@ Vector<int> TileMap::_get_tile_data(int p_layer) const { // Save in highest format int idx = 0; - for (const Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { uint8_t *ptr = (uint8_t *)&w[idx]; - encode_uint16((int16_t)(E->key().x), &ptr[0]); - encode_uint16((int16_t)(E->key().y), &ptr[2]); - encode_uint16(E->get().source_id, &ptr[4]); - encode_uint16(E->get().coord_x, &ptr[6]); - encode_uint16(E->get().coord_y, &ptr[8]); - encode_uint16(E->get().alternative_tile, &ptr[10]); + encode_uint16((int16_t)(E.key.x), &ptr[0]); + encode_uint16((int16_t)(E.key.y), &ptr[2]); + encode_uint16(E.value.source_id, &ptr[4]); + encode_uint16(E.value.coord_x, &ptr[6]); + encode_uint16(E.value.coord_y, &ptr[8]); + encode_uint16(E.value.alternative_tile, &ptr[10]); idx += 3; } @@ -2751,8 +2778,8 @@ TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const { TypedArray<Vector2i> a; a.resize(layers[p_layer].tile_map.size()); int i = 0; - for (Map<Vector2i, TileMapCell>::Element *E = layers[p_layer].tile_map.front(); E; E = E->next()) { - Vector2i p(E->key().x, E->key().y); + for (const KeyValue<Vector2i, TileMapCell> &E : layers[p_layer].tile_map) { + Vector2i p(E.key.x, E.key.y); a[i++] = p; } @@ -2773,8 +2800,8 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache first = false; } - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - used_rect_cache.expand_to(Vector2i(E->key().x, E->key().y)); + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + used_rect_cache.expand_to(Vector2i(E.key.x, E.key.y)); } } } @@ -2794,8 +2821,8 @@ void TileMap::set_light_mask(int p_light_mask) { // Occlusion: set light mask. CanvasItem::set_light_mask(p_light_mask); for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - for (const RID &ci : E->get().canvas_items) { + for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + for (const RID &ci : E.value.canvas_items) { RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, get_light_mask()); } } @@ -2809,8 +2836,8 @@ void TileMap::set_material(const Ref<Material> &p_material) { // Update material for the whole tilemap. for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + TileMapQuadrant &q = E.value; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); } @@ -2825,8 +2852,8 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) { // Update use_parent_material for the whole tilemap. for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + TileMapQuadrant &q = E.value; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); } @@ -3007,6 +3034,8 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index); ClassDB::bind_method(D_METHOD("get_layer_z_index", "layer"), &TileMap::get_layer_z_index); + ClassDB::bind_method(D_METHOD("set_collision_animatable", "enabled"), &TileMap::set_collision_animatable); + ClassDB::bind_method(D_METHOD("is_collision_animatable"), &TileMap::is_collision_animatable); ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "collision_visibility_mode"), &TileMap::set_collision_visibility_mode); ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode); @@ -3018,10 +3047,14 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile); + ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid); + ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); - ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles); + ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); + ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles); + ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect); @@ -3037,6 +3070,7 @@ void TileMap::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode"); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index ca38baa550..2faede2445 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -202,6 +202,7 @@ private: // Properties. Ref<TileSet> tile_set; int quadrant_size = 16; + bool collision_animatable = false; VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT; VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT; @@ -229,6 +230,9 @@ private: LocalVector<TileMapLayer> layers; int selected_layer = -1; + // Mapping for RID to coords. + Map<RID, Vector2i> bodies_coords; + // Quadrants and internals management. Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const; @@ -259,9 +263,10 @@ private: void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant); void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + Transform2D last_valid_transform; + Transform2D new_transform; void _physics_notification(int p_what); void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); - void _physics_create_quadrant(TileMapQuadrant *p_quadrant); void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant); void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant); @@ -325,6 +330,9 @@ public: void set_selected_layer(int p_layer_id); // For editor use. int get_selected_layer() const; + void set_collision_animatable(bool p_enabled); + bool is_collision_animatable() const; + void set_collision_visibility_mode(VisibilityMode p_show_collision); VisibilityMode get_collision_visibility_mode(); @@ -364,6 +372,9 @@ public: virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override; virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override; + // For finding tiles from collision. + Vector2i get_coords_for_body_rid(RID p_physics_body); + // Fixing a nclearing methods. void fix_invalid_tiles(); diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 943586f43c..7e4c40ca0e 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -262,8 +262,8 @@ void Area3D::_clear_monitoring() { body_map.clear(); //disconnect all monitored stuff - for (Map<ObjectID, BodyState>::Element *E = bmcopy.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : bmcopy) { + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (!node) { //node may have been deleted in previous frame or at other legitimate point @@ -274,12 +274,12 @@ void Area3D::_clear_monitoring() { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree)); - if (!E->get().in_tree) { + if (!E.value.in_tree) { continue; } - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E.value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E.value.rid, node, E.value.shapes[i].body_shape, E.value.shapes[i].area_shape); } emit_signal(SceneStringNames::get_singleton()->body_exited, node); @@ -291,8 +291,8 @@ void Area3D::_clear_monitoring() { area_map.clear(); //disconnect all monitored stuff - for (Map<ObjectID, AreaState>::Element *E = bmcopy.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, AreaState> &E : bmcopy) { + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (!node) { //node may have been deleted in previous frame or at other legitimate point @@ -303,12 +303,12 @@ void Area3D::_clear_monitoring() { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree)); - if (!E->get().in_tree) { + if (!E.value.in_tree) { continue; } - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E.value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E.value.rid, node, E.value.shapes[i].area_shape, E.value.shapes[i].self_shape); } emit_signal(SceneStringNames::get_singleton()->area_exited, obj); @@ -446,8 +446,8 @@ TypedArray<Node3D> Area3D::get_overlapping_bodies() const { Array ret; ret.resize(body_map.size()); int idx = 0; - for (const Map<ObjectID, BodyState>::Element *E = body_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : body_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { @@ -479,8 +479,8 @@ TypedArray<Area3D> Area3D::get_overlapping_areas() const { Array ret; ret.resize(area_map.size()); int idx = 0; - for (const Map<ObjectID, AreaState>::Element *E = area_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, AreaState> &E : area_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 9aad338d15..61d73ff1e2 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -492,6 +492,7 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::get_frustum); ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum); ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera); + ClassDB::bind_method(D_METHOD("get_pyramid_shape_rid"), &Camera3D::get_pyramid_shape_rid); ClassDB::bind_method(D_METHOD("set_cull_mask_value", "layer_number", "value"), &Camera3D::set_cull_mask_value); ClassDB::bind_method(D_METHOD("get_cull_mask_value", "layer_number"), &Camera3D::get_cull_mask_value); @@ -654,233 +655,46 @@ Vector3 Camera3D::get_doppler_tracked_velocity() const { } } -Camera3D::Camera3D() { - camera = RenderingServer::get_singleton()->camera_create(); - set_perspective(75.0, 0.05, 4000.0); - RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers); - //active=false; - velocity_tracker.instantiate(); - set_notify_transform(true); - set_disable_scale(true); -} - -Camera3D::~Camera3D() { - RenderingServer::get_singleton()->free(camera); -} +RID Camera3D::get_pyramid_shape_rid() { + if (pyramid_shape == RID()) { + pyramid_shape_points = get_near_plane_points(); + pyramid_shape = PhysicsServer3D::get_singleton()->convex_polygon_shape_create(); + PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, pyramid_shape_points); -//////////////////////////////////////// + } else { //check if points changed + Vector<Vector3> local_points = get_near_plane_points(); -void ClippedCamera3D::set_margin(real_t p_margin) { - margin = p_margin; -} - -real_t ClippedCamera3D::get_margin() const { - return margin; -} - -void ClippedCamera3D::set_process_callback(ClipProcessCallback p_mode) { - if (process_callback == p_mode) { - return; - } - process_callback = p_mode; - set_process_internal(process_callback == CLIP_PROCESS_IDLE); - set_physics_process_internal(process_callback == CLIP_PROCESS_PHYSICS); -} - -ClippedCamera3D::ClipProcessCallback ClippedCamera3D::get_process_callback() const { - return process_callback; -} - -Transform3D ClippedCamera3D::get_camera_transform() const { - Transform3D t = Camera3D::get_camera_transform(); - t.origin += -t.basis.get_axis(Vector3::AXIS_Z).normalized() * clip_offset; - return t; -} - -void ClippedCamera3D::_notification(int p_what) { - if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { - Node3D *parent = Object::cast_to<Node3D>(get_parent()); - if (!parent) { - return; - } + bool all_equal = true; - PhysicsDirectSpaceState3D *dspace = get_world_3d()->get_direct_space_state(); - ERR_FAIL_COND(!dspace); // most likely physics set to threads - - Vector3 cam_fw = -get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized(); - Vector3 cam_pos = get_global_transform().origin; - Vector3 parent_pos = parent->get_global_transform().origin; - - Plane parent_plane(parent_pos, cam_fw); - - if (parent_plane.is_point_over(cam_pos)) { - //cam is beyond parent plane - return; - } - - Vector3 ray_from = parent_plane.project(cam_pos); - - clip_offset = 0; //reset by default - - { //check if points changed - Vector<Vector3> local_points = get_near_plane_points(); - - bool all_equal = true; - - for (int i = 0; i < 5; i++) { - if (points[i] != local_points[i]) { - all_equal = false; - break; - } - } - - if (!all_equal) { - PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, local_points); - points = local_points; + for (int i = 0; i < 5; i++) { + if (local_points[i] != pyramid_shape_points[i]) { + all_equal = false; + break; } } - Transform3D xf = get_global_transform(); - xf.origin = ray_from; - xf.orthonormalize(); - - real_t closest_safe = 1.0f, closest_unsafe = 1.0f; - if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, closest_safe, closest_unsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) { - clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * closest_safe); + if (!all_equal) { + PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, local_points); + pyramid_shape_points = local_points; } - - _update_camera(); - } - - if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { - update_gizmos(); } -} - -void ClippedCamera3D::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; -} -uint32_t ClippedCamera3D::get_collision_mask() const { - return collision_mask; + return pyramid_shape; } -void ClippedCamera3D::set_collision_mask_value(int p_layer_number, bool p_value) { - ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); - ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); - uint32_t mask = get_collision_mask(); - if (p_value) { - mask |= 1 << (p_layer_number - 1); - } else { - mask &= ~(1 << (p_layer_number - 1)); - } - set_collision_mask(mask); -} - -bool ClippedCamera3D::get_collision_mask_value(int p_layer_number) const { - ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); - ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); - return get_collision_mask() & (1 << (p_layer_number - 1)); -} - -void ClippedCamera3D::add_exception_rid(const RID &p_rid) { - exclude.insert(p_rid); -} - -void ClippedCamera3D::add_exception(const Object *p_object) { - ERR_FAIL_NULL(p_object); - const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object); - if (!co) { - return; - } - add_exception_rid(co->get_rid()); -} - -void ClippedCamera3D::remove_exception_rid(const RID &p_rid) { - exclude.erase(p_rid); +Camera3D::Camera3D() { + camera = RenderingServer::get_singleton()->camera_create(); + set_perspective(75.0, 0.05, 4000.0); + RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers); + //active=false; + velocity_tracker.instantiate(); + set_notify_transform(true); + set_disable_scale(true); } -void ClippedCamera3D::remove_exception(const Object *p_object) { - ERR_FAIL_NULL(p_object); - const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object); - if (!co) { - return; +Camera3D::~Camera3D() { + RenderingServer::get_singleton()->free(camera); + if (pyramid_shape.is_valid()) { + PhysicsServer3D::get_singleton()->free(pyramid_shape); } - remove_exception_rid(co->get_rid()); -} - -void ClippedCamera3D::clear_exceptions() { - exclude.clear(); -} - -real_t ClippedCamera3D::get_clip_offset() const { - return clip_offset; -} - -void ClippedCamera3D::set_clip_to_areas(bool p_clip) { - clip_to_areas = p_clip; -} - -bool ClippedCamera3D::is_clip_to_areas_enabled() const { - return clip_to_areas; -} - -void ClippedCamera3D::set_clip_to_bodies(bool p_clip) { - clip_to_bodies = p_clip; -} - -bool ClippedCamera3D::is_clip_to_bodies_enabled() const { - return clip_to_bodies; -} - -void ClippedCamera3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ClippedCamera3D::set_margin); - ClassDB::bind_method(D_METHOD("get_margin"), &ClippedCamera3D::get_margin); - - ClassDB::bind_method(D_METHOD("set_process_callback", "process_callback"), &ClippedCamera3D::set_process_callback); - ClassDB::bind_method(D_METHOD("get_process_callback"), &ClippedCamera3D::get_process_callback); - - ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera3D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera3D::get_collision_mask); - - ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ClippedCamera3D::set_collision_mask_value); - ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ClippedCamera3D::get_collision_mask_value); - - ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera3D::add_exception_rid); - ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera3D::add_exception); - - ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ClippedCamera3D::remove_exception_rid); - ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ClippedCamera3D::remove_exception); - - ClassDB::bind_method(D_METHOD("set_clip_to_areas", "enable"), &ClippedCamera3D::set_clip_to_areas); - ClassDB::bind_method(D_METHOD("is_clip_to_areas_enabled"), &ClippedCamera3D::is_clip_to_areas_enabled); - - ClassDB::bind_method(D_METHOD("get_clip_offset"), &ClippedCamera3D::get_clip_offset); - - ClassDB::bind_method(D_METHOD("set_clip_to_bodies", "enable"), &ClippedCamera3D::set_clip_to_bodies); - ClassDB::bind_method(D_METHOD("is_clip_to_bodies_enabled"), &ClippedCamera3D::is_clip_to_bodies_enabled); - - ClassDB::bind_method(D_METHOD("clear_exceptions"), &ClippedCamera3D::clear_exceptions); - - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_margin", "get_margin"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); - - ADD_GROUP("Clip To", "clip_to"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_to_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_clip_to_areas", "is_clip_to_areas_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_to_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_clip_to_bodies", "is_clip_to_bodies_enabled"); - - BIND_ENUM_CONSTANT(CLIP_PROCESS_PHYSICS); - BIND_ENUM_CONSTANT(CLIP_PROCESS_IDLE); -} - -ClippedCamera3D::ClippedCamera3D() { - set_physics_process_internal(true); - set_notify_local_transform(Engine::get_singleton()->is_editor_hint()); - points.resize(5); - pyramid_shape = PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_CONVEX_POLYGON); -} - -ClippedCamera3D::~ClippedCamera3D() { - PhysicsServer3D::get_singleton()->free(pyramid_shape); } diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index c1af7fa4f7..6006a2ea3f 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -86,6 +86,9 @@ private: DopplerTracking doppler_tracking = DOPPLER_TRACKING_DISABLED; Ref<VelocityTracker3D> velocity_tracker; + RID pyramid_shape; + Vector<Vector3> pyramid_shape_points; + protected: void _update_camera(); virtual void _request_camera_update(); @@ -168,6 +171,8 @@ public: Vector3 get_doppler_tracked_velocity() const; + RID get_pyramid_shape_rid(); + Camera3D(); ~Camera3D(); }; @@ -176,63 +181,4 @@ VARIANT_ENUM_CAST(Camera3D::Projection); VARIANT_ENUM_CAST(Camera3D::KeepAspect); VARIANT_ENUM_CAST(Camera3D::DopplerTracking); -class ClippedCamera3D : public Camera3D { - GDCLASS(ClippedCamera3D, Camera3D); - -public: - enum ClipProcessCallback { - CLIP_PROCESS_PHYSICS, - CLIP_PROCESS_IDLE, - }; - -private: - ClipProcessCallback process_callback = CLIP_PROCESS_PHYSICS; - RID pyramid_shape; - real_t margin = 0.0; - real_t clip_offset = 0.0; - uint32_t collision_mask = 1; - bool clip_to_areas = false; - bool clip_to_bodies = true; - - Set<RID> exclude; - - Vector<Vector3> points; - -protected: - void _notification(int p_what); - static void _bind_methods(); - virtual Transform3D get_camera_transform() const override; - -public: - void set_clip_to_areas(bool p_clip); - bool is_clip_to_areas_enabled() const; - - void set_clip_to_bodies(bool p_clip); - bool is_clip_to_bodies_enabled() const; - - void set_margin(real_t p_margin); - real_t get_margin() const; - - void set_process_callback(ClipProcessCallback p_mode); - ClipProcessCallback get_process_callback() const; - - void set_collision_mask(uint32_t p_mask); - uint32_t get_collision_mask() const; - - void set_collision_mask_value(int p_layer_number, bool p_value); - bool get_collision_mask_value(int p_layer_number) const; - - void add_exception_rid(const RID &p_rid); - void add_exception(const Object *p_object); - void remove_exception_rid(const RID &p_rid); - void remove_exception(const Object *p_object); - void clear_exceptions(); - - real_t get_clip_offset() const; - - ClippedCamera3D(); - ~ClippedCamera3D(); -}; - -VARIANT_ENUM_CAST(ClippedCamera3D::ClipProcessCallback); #endif diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index e2f953974a..814ed5c2a7 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -37,8 +37,8 @@ void CollisionObject3D::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { if (_are_collision_shapes_visible()) { debug_shape_old_transform = get_global_transform(); - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - debug_shapes_to_update.insert(E->key()); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + debug_shapes_to_update.insert(E.key); } _update_debug_shapes(); } @@ -324,8 +324,8 @@ void CollisionObject3D::_update_shape_data(uint32_t p_owner) { } void CollisionObject3D::_shape_changed(const Ref<Shape3D> &p_shape) { - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - ShapeData &shapedata = E->get(); + for (KeyValue<uint32_t, ShapeData> &E : shapes) { + ShapeData &shapedata = E.value; ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); for (int i = 0; i < shapedata.shapes.size(); i++) { ShapeData::ShapeBase &s = shapes[i]; @@ -380,8 +380,8 @@ void CollisionObject3D::_update_debug_shapes() { } void CollisionObject3D::_clear_debug_shapes() { - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - ShapeData &shapedata = E->get(); + for (KeyValue<uint32_t, ShapeData> &E : shapes) { + ShapeData &shapedata = E.value; ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); for (int i = 0; i < shapedata.shapes.size(); i++) { ShapeData::ShapeBase &s = shapes[i]; @@ -400,8 +400,8 @@ void CollisionObject3D::_clear_debug_shapes() { void CollisionObject3D::_on_transform_changed() { if (debug_shapes_count > 0 && !debug_shape_old_transform.is_equal_approx(get_global_transform())) { debug_shape_old_transform = get_global_transform(); - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - ShapeData &shapedata = E->get(); + for (KeyValue<uint32_t, ShapeData> &E : shapes) { + ShapeData &shapedata = E.value; const ShapeData::ShapeBase *shapes = shapedata.shapes.ptr(); for (int i = 0; i < shapedata.shapes.size(); i++) { RS::get_singleton()->instance_set_transform(shapes[i].debug_shape, debug_shape_old_transform * shapedata.xform); @@ -523,15 +523,15 @@ bool CollisionObject3D::is_shape_owner_disabled(uint32_t p_owner) const { } void CollisionObject3D::get_shape_owners(List<uint32_t> *r_owners) { - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - r_owners->push_back(E->key()); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + r_owners->push_back(E.key); } } Array CollisionObject3D::_get_shape_owners() { Array ret; - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - ret.push_back(E->key()); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + ret.push_back(E.key); } return ret; @@ -628,10 +628,10 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) shapes[p_owner].shapes.remove(p_shape); - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (E->get().shapes[i].index > index_to_remove) { - E->get().shapes.write[i].index -= 1; + for (KeyValue<uint32_t, ShapeData> &E : shapes) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (E.value.shapes[i].index > index_to_remove) { + E.value.shapes.write[i].index -= 1; } } } @@ -650,10 +650,10 @@ void CollisionObject3D::shape_owner_clear_shapes(uint32_t p_owner) { uint32_t CollisionObject3D::shape_find_owner(int p_shape_index) const { ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0); - for (const Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (E->get().shapes[i].index == p_shape_index) { - return E->key(); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (E.value.shapes[i].index == p_shape_index) { + return E.key; } } } diff --git a/scene/3d/importer_mesh_instance_3d.cpp b/scene/3d/importer_mesh_instance_3d.cpp new file mode 100644 index 0000000000..748a2e5092 --- /dev/null +++ b/scene/3d/importer_mesh_instance_3d.cpp @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* importer_mesh_instance_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "importer_mesh_instance_3d.h" + +#include "scene/resources/importer_mesh.h" + +void ImporterMeshInstance3D::set_mesh(const Ref<ImporterMesh> &p_mesh) { + mesh = p_mesh; +} +Ref<ImporterMesh> ImporterMeshInstance3D::get_mesh() const { + return mesh; +} + +void ImporterMeshInstance3D::set_skin(const Ref<Skin> &p_skin) { + skin = p_skin; +} +Ref<Skin> ImporterMeshInstance3D::get_skin() const { + return skin; +} + +void ImporterMeshInstance3D::set_surface_material(int p_idx, const Ref<Material> &p_material) { + ERR_FAIL_COND(p_idx < 0); + if (p_idx >= surface_materials.size()) { + surface_materials.resize(p_idx + 1); + } + + surface_materials.write[p_idx] = p_material; +} +Ref<Material> ImporterMeshInstance3D::get_surface_material(int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, Ref<Material>()); + if (p_idx >= surface_materials.size()) { + return Ref<Material>(); + } + return surface_materials[p_idx]; +} + +void ImporterMeshInstance3D::set_skeleton_path(const NodePath &p_path) { + skeleton_path = p_path; +} +NodePath ImporterMeshInstance3D::get_skeleton_path() const { + return skeleton_path; +} + +void ImporterMeshInstance3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &ImporterMeshInstance3D::set_mesh); + ClassDB::bind_method(D_METHOD("get_mesh"), &ImporterMeshInstance3D::get_mesh); + + ClassDB::bind_method(D_METHOD("set_skin", "skin"), &ImporterMeshInstance3D::set_skin); + ClassDB::bind_method(D_METHOD("get_skin"), &ImporterMeshInstance3D::get_skin); + + ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &ImporterMeshInstance3D::set_skeleton_path); + ClassDB::bind_method(D_METHOD("get_skeleton_path"), &ImporterMeshInstance3D::get_skeleton_path); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "ImporterMesh"), "set_mesh", "get_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path"); +} diff --git a/scene/3d/importer_mesh_instance_3d.h b/scene/3d/importer_mesh_instance_3d.h new file mode 100644 index 0000000000..0cf7dbe86b --- /dev/null +++ b/scene/3d/importer_mesh_instance_3d.h @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* importer_mesh_instance_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SCENE_IMPORTER_MESH_INSTANCE_3D_H +#define SCENE_IMPORTER_MESH_INSTANCE_3D_H + +#include "scene/3d/node_3d.h" +#include "scene/resources/immediate_mesh.h" +#include "scene/resources/skin.h" + +class ImporterMesh; + +class ImporterMeshInstance3D : public Node3D { + GDCLASS(ImporterMeshInstance3D, Node3D) + + Ref<ImporterMesh> mesh; + Ref<Skin> skin; + NodePath skeleton_path; + Vector<Ref<Material>> surface_materials; + +protected: + static void _bind_methods(); + +public: + void set_mesh(const Ref<ImporterMesh> &p_mesh); + Ref<ImporterMesh> get_mesh() const; + + void set_skin(const Ref<Skin> &p_skin); + Ref<Skin> get_skin() const; + + void set_surface_material(int p_idx, const Ref<Material> &p_material); + Ref<Material> get_surface_material(int p_idx) const; + + void set_skeleton_path(const NodePath &p_path); + NodePath get_skeleton_path() const; +}; +#endif diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/joint_3d.cpp index 12938946a0..aa5ca85bdf 100644 --- a/scene/3d/physics_joint_3d.cpp +++ b/scene/3d/joint_3d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* physics_joint_3d.cpp */ +/* joint_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "physics_joint_3d.h" +#include "joint_3d.h" #include "scene/scene_string_names.h" diff --git a/scene/3d/physics_joint_3d.h b/scene/3d/joint_3d.h index 3e0ea38a5c..211cf8e071 100644 --- a/scene/3d/physics_joint_3d.h +++ b/scene/3d/joint_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* physics_joint_3d.h */ +/* joint_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PHYSICS_JOINT_H -#define PHYSICS_JOINT_H +#ifndef JOINT_3D_H +#define JOINT_3D_H #include "scene/3d/node_3d.h" #include "scene/3d/physics_body_3d.h" @@ -334,4 +334,4 @@ public: VARIANT_ENUM_CAST(Generic6DOFJoint3D::Param); VARIANT_ENUM_CAST(Generic6DOFJoint3D::Flag); -#endif // PHYSICS_JOINT_H +#endif // JOINT_3D_H diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 7e7db57af3..67f4a88228 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -86,8 +86,8 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { List<String> ls; - for (const Map<StringName, BlendShapeTrack>::Element *E = blend_shape_tracks.front(); E; E = E->next()) { - ls.push_back(E->key()); + for (const KeyValue<StringName, BlendShapeTrack> &E : blend_shape_tracks) { + ls.push_back(E.key); } ls.sort(); @@ -369,6 +369,8 @@ void MeshInstance3D::create_debug_tangents() { for (int i = 0; i < mesh->get_surface_count(); i++) { Array arrays = mesh->surface_get_arrays(i); + ERR_CONTINUE(arrays.size() != Mesh::ARRAY_MAX); + Vector<Vector3> verts = arrays[Mesh::ARRAY_VERTEX]; Vector<Vector3> norms = arrays[Mesh::ARRAY_NORMAL]; if (norms.size() == 0) { diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index bbf2c81a92..976bff4fbc 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -33,10 +33,6 @@ #include "core/core_string_names.h" #include "scene/scene_string_names.h" -#ifdef TOOLS_ENABLED -#include "editor/plugins/node_3d_editor_plugin.h" -#endif - void PhysicsBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1)); ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1)); @@ -95,12 +91,15 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } -Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_test_only, real_t p_margin, int p_max_collisions) { - PhysicsServer3D::MotionResult result; +Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_linear_velocity, bool p_test_only, real_t p_margin, int p_max_collisions) { // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); - if (move_and_collide(p_motion * delta, result, p_margin, p_test_only, p_max_collisions)) { + PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin); + parameters.max_collisions = p_max_collisions; + + PhysicsServer3D::MotionResult result; + if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -115,23 +114,22 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_t return Ref<KinematicCollision3D>(); } -bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, int p_max_collisions, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) { - Transform3D gt = get_global_transform(); - bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_max_collisions, p_collide_separation_ray, p_exclude); +bool PhysicsBody3D::move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only, bool p_cancel_sliding) { + bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_parameters, &r_result); // Restore direction of motion to be along original motion, // in order to avoid sliding due to recovery, // but only if collision depth is low enough to avoid tunneling. if (p_cancel_sliding) { - real_t motion_length = p_motion.length(); + real_t motion_length = p_parameters.motion.length(); real_t precision = 0.001; if (colliding) { // Can't just use margin as a threshold because collision depth is calculated on unsafe motion, // so even in normal resting cases the depth can be a bit more than the margin. - precision += motion_length * (r_result.unsafe_fraction - r_result.safe_fraction); + precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction); - if (r_result.collisions[0].depth > (real_t)p_margin + precision) { + if (r_result.collisions[0].depth > p_parameters.margin + precision) { p_cancel_sliding = false; } } @@ -140,7 +138,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M // When motion is null, recovery is the resulting motion. Vector3 motion_normal; if (motion_length > CMP_EPSILON) { - motion_normal = p_motion / motion_length; + motion_normal = p_parameters.motion / motion_length; } // Check depth of recovery. @@ -149,10 +147,10 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M real_t recovery_length = recovery.length(); // Fixes cases where canceling slide causes the motion to go too deep into the ground, // because we're only taking rest information into account and not general recovery. - if (recovery_length < (real_t)p_margin + precision) { + if (recovery_length < p_parameters.margin + precision) { // Apply adjustment to motion. r_result.travel = motion_normal * projected_length; - r_result.remainder = p_motion - r_result.travel; + r_result.remainder = p_parameters.motion - r_result.travel; } } } @@ -164,6 +162,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M } if (!p_test_only) { + Transform3D gt = p_parameters.from; gt.origin += r_result.travel; set_global_transform(gt); } @@ -171,7 +170,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M return colliding; } -bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) { +bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) { ERR_FAIL_COND_V(!is_inside_tree(), false); PhysicsServer3D::MotionResult *r = nullptr; @@ -183,7 +182,9 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); - return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r, p_max_collisions); + PhysicsServer3D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin); + + return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); } void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { @@ -340,6 +341,12 @@ void AnimatableBody3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { last_valid_transform = get_global_transform(); + _update_kinematic_motion(); + } break; + + case NOTIFICATION_EXIT_TREE: { + set_only_update_transform_changes(false); + set_notify_local_transform(false); } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { @@ -367,8 +374,6 @@ void AnimatableBody3D::_bind_methods() { AnimatableBody3D::AnimatableBody3D() : StaticBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) { PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); - - _update_kinematic_motion(); } void RigidDynamicBody3D::_body_enter_tree(ObjectID p_id) { @@ -512,9 +517,9 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) //untag all int rc = 0; - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - E->get().shapes[i].tagged = false; + for (KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + for (int i = 0; i < E.value.shapes.size(); i++) { + E.value.shapes[i].tagged = false; rc++; } } @@ -560,12 +565,12 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) //put the ones to remove - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (!E->get().shapes[i].tagged) { - toremove[toremove_count].rid = E->get().rid; - toremove[toremove_count].body_id = E->key(); - toremove[toremove_count].pair = E->get().shapes[i]; + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (!E.value.shapes[i].tagged) { + toremove[toremove_count].rid = E.value.rid; + toremove[toremove_count].body_id = E.key; + toremove[toremove_count].pair = E.value.shapes[i]; toremove_count++; } } @@ -885,9 +890,9 @@ void RigidDynamicBody3D::set_contact_monitor(bool p_enabled) { if (!p_enabled) { ERR_FAIL_COND_MSG(contact_monitor->locked, "Can't disable contact monitoring during in/out callback. Use call_deferred(\"set_contact_monitor\", false) instead."); - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { //clean up mess - Object *obj = ObjectDB::get_instance(E->key()); + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (node) { @@ -914,8 +919,8 @@ Array RigidDynamicBody3D::get_colliding_bodies() const { Array ret; ret.resize(contact_monitor->body_map.size()); int idx = 0; - for (const Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { @@ -1085,13 +1090,16 @@ void RigidDynamicBody3D::_reload_physics_characteristics() { bool CharacterBody3D::move_and_slide() { // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); - previous_position = get_global_transform().origin; + for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { - linear_velocity[i] = 0.0; + motion_velocity[i] = 0.0; } } + Transform3D gt = get_global_transform(); + previous_position = gt.origin; + Vector3 current_platform_velocity = platform_velocity; if ((collision_state.floor || collision_state.wall) && platform_rid.is_valid()) { @@ -1105,7 +1113,6 @@ bool CharacterBody3D::move_and_slide() { //this approach makes sure there is less delay between the actual body velocity and the one we saved PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(platform_rid); if (bs) { - Transform3D gt = get_global_transform(); Vector3 local_position = gt.origin - bs->get_transform().origin; current_platform_velocity = bs->get_velocity_at_local_position(local_position); } @@ -1119,11 +1126,17 @@ bool CharacterBody3D::move_and_slide() { bool was_on_floor = collision_state.floor; collision_state.state = 0; + last_motion = Vector3(); + if (!current_platform_velocity.is_equal_approx(Vector3())) { + PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); + parameters.exclude_bodies.insert(platform_rid); + if (platform_object_id.is_valid()) { + parameters.exclude_objects.insert(platform_object_id); + } + PhysicsServer3D::MotionResult floor_result; - Set<RID> exclude; - exclude.insert(platform_rid); - if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, 1, false, false, exclude)) { + if (move_and_collide(parameters, floor_result, false, false)) { motion_results.push_back(floor_result); CollisionState result_state; @@ -1146,40 +1159,45 @@ bool CharacterBody3D::move_and_slide() { if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { current_platform_velocity = current_platform_velocity.slide(up_direction); } - linear_velocity += current_platform_velocity; + motion_velocity += current_platform_velocity; } } - // Reset the gravity accumulation when touching the ground. - if (collision_state.floor && linear_velocity.dot(up_direction) <= 0) { - linear_velocity = linear_velocity.slide(up_direction); - } - return motion_results.size() > 0; } void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) { - Vector3 motion = linear_velocity * p_delta; + Vector3 motion = motion_velocity * p_delta; Vector3 motion_slide_up = motion.slide(up_direction); Vector3 prev_floor_normal = floor_normal; platform_rid = RID(); + platform_object_id = ObjectID(); platform_velocity = Vector3(); + platform_ceiling_velocity = Vector3(); floor_normal = Vector3(); wall_normal = Vector3(); + ceiling_normal = Vector3(); // No sliding on first attempt to keep floor motion stable when possible, // When stop on slope is enabled or when there is no up direction. bool sliding_enabled = !floor_stop_on_slope; // Constant speed can be applied only the first time sliding is enabled. bool can_apply_constant_speed = sliding_enabled; + // If the platform's ceiling push down the body. + bool apply_ceiling_velocity = false; bool first_slide = true; - bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0; + bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0; Vector3 total_travel; for (int iteration = 0; iteration < max_slides; ++iteration) { + PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); + parameters.max_collisions = 4; + PhysicsServer3D::MotionResult result; - bool collided = move_and_collide(motion, result, margin, false, 4, !sliding_enabled); + bool collided = move_and_collide(parameters, result, false, !sliding_enabled); + + last_motion = result.travel; if (collided) { motion_results.push_back(result); @@ -1189,20 +1207,33 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo CollisionState result_state; _set_collision_direction(result, result_state); - if (collision_state.floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { + // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one. + if (collision_state.ceiling && platform_ceiling_velocity != Vector3() && platform_ceiling_velocity.dot(up_direction) < 0) { + // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward. + if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (ceiling_normal + up_direction).length() < 0.01) { + apply_ceiling_velocity = true; + Vector3 ceiling_vertical_velocity = up_direction * up_direction.dot(platform_ceiling_velocity); + Vector3 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity); + if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) { + motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction); + } + } + } + + if (collision_state.floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) { Transform3D gt = get_global_transform(); if (result.travel.length() <= margin + CMP_EPSILON) { gt.origin -= result.travel; } set_global_transform(gt); - linear_velocity = Vector3(); + motion_velocity = Vector3(); motion = Vector3(); + last_motion = Vector3(); break; } if (result.remainder.is_equal_approx(Vector3())) { motion = Vector3(); - last_motion = result.travel; break; } @@ -1251,7 +1282,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo Vector3 forward = wall_normal.slide(up_direction).normalized(); motion = motion.slide(forward); // Avoid accelerating when you jump on the wall and smooth falling. - linear_velocity = linear_velocity.slide(forward); + motion_velocity = motion_velocity.slide(forward); // Allow only lateral motion along previous floor when already on floor. // Fixes slowing down when moving in diagonal against an inclined wall. @@ -1280,7 +1311,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (stop_all_motion) { motion = Vector3(); - linear_velocity = Vector3(); + motion_velocity = Vector3(); } } } @@ -1291,7 +1322,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(motion_slide_up.normalized()))); if (motion_angle < wall_min_slide_angle) { motion = up_direction * motion.dot(up_direction); - linear_velocity = up_direction * linear_velocity.dot(up_direction); + motion_velocity = up_direction * motion_velocity.dot(up_direction); apply_default_sliding = false; } @@ -1300,21 +1331,35 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (apply_default_sliding) { // Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling. - if ((sliding_enabled || !collision_state.floor) && (!collision_state.ceiling || slide_on_ceiling || !vel_dir_facing_up)) { + if ((sliding_enabled || !collision_state.floor) && (!collision_state.ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) { const PhysicsServer3D::MotionCollision &collision = result.collisions[0]; + Vector3 slide_motion = result.remainder.slide(collision.normal); - if (slide_motion.dot(linear_velocity) > 0.0) { + if (collision_state.floor && !collision_state.wall) { + // Slide using the intersection between the motion plane and the floor plane, + // in order to keep the direction intact. + real_t motion_length = slide_motion.length(); + slide_motion = up_direction.cross(result.remainder).cross(floor_normal); + + // Keep the length from default slide to change speed in slopes by default, + // when constant speed is not enabled. + slide_motion.normalize(); + slide_motion *= motion_length; + } + + if (slide_motion.dot(motion_velocity) > 0.0) { motion = slide_motion; } else { motion = Vector3(); } + if (slide_on_ceiling && result_state.ceiling) { // Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall. if (vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(collision.normal); + motion_velocity = motion_velocity.slide(collision.normal); } else { // Avoid acceleration in slope when falling. - linear_velocity = up_direction * up_direction.dot(linear_velocity); + motion_velocity = up_direction * up_direction.dot(motion_velocity); } } } @@ -1322,18 +1367,19 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo else { motion = result.remainder; if (result_state.ceiling && !slide_on_ceiling && vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(up_direction); + motion_velocity = motion_velocity.slide(up_direction); motion = motion.slide(up_direction); } } } + total_travel += result.travel; + // Apply Constant Speed. - if (p_was_on_floor && floor_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) { - motion = motion.normalized() * MAX(0, (motion_slide_up.length() - result.travel.slide(up_direction).length() - total_travel.slide(up_direction).length())); + if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) { + Vector3 travel_slide_up = total_travel.slide(up_direction); + motion = motion.normalized() * MAX(0, (motion_slide_up.length() - travel_slide_up.length())); } - - total_travel += result.travel; } // When you move forward in a downward slope you don’t collide because you will be in the air. // This test ensures that constant speed is applied, only if the player is still on the ground after the snap is applied. @@ -1344,44 +1390,48 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo gt.origin = gt.origin - result.travel; set_global_transform(gt); - Vector3 motion_slide_norm = motion.slide(prev_floor_normal).normalized(); + // Slide using the intersection between the motion plane and the floor plane, + // in order to keep the direction intact. + Vector3 motion_slide_norm = up_direction.cross(motion).cross(prev_floor_normal); + motion_slide_norm.normalize(); + motion = motion_slide_norm * (motion_slide_up.length()); collided = true; } - can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled; - sliding_enabled = true; - first_slide = false; - - if (!motion.is_equal_approx(Vector3())) { - last_motion = motion; - } - if (!collided || motion.is_equal_approx(Vector3())) { break; } + + can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled; + sliding_enabled = true; + first_slide = false; } _snap_on_floor(p_was_on_floor, vel_dir_facing_up); // Reset the gravity accumulation when touching the ground. if (collision_state.floor && !vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(up_direction); + motion_velocity = motion_velocity.slide(up_direction); } } void CharacterBody3D::_move_and_slide_free(double p_delta) { - Vector3 motion = linear_velocity * p_delta; + Vector3 motion = motion_velocity * p_delta; platform_rid = RID(); + platform_object_id = ObjectID(); floor_normal = Vector3(); platform_velocity = Vector3(); bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { + PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); + PhysicsServer3D::MotionResult result; + bool collided = move_and_collide(parameters, result, false, false); - bool collided = move_and_collide(motion, result, margin, false, 1, false); + last_motion = result.travel; if (collided) { motion_results.push_back(result); @@ -1389,7 +1439,12 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) { CollisionState result_state; _set_collision_direction(result, result_state); - if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-linear_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { + if (result.remainder.is_equal_approx(Vector3())) { + motion = Vector3(); + break; + } + + if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-motion_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { motion = Vector3(); if (result.travel.length() < margin + CMP_EPSILON) { Transform3D gt = get_global_transform(); @@ -1403,16 +1458,16 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) { motion = result.remainder.slide(wall_normal); } - if (motion.dot(linear_velocity) <= 0.0) { + if (motion.dot(motion_velocity) <= 0.0) { motion = Vector3(); } } - first_slide = false; - if (!collided || motion.is_equal_approx(Vector3())) { break; } + + first_slide = false; } } @@ -1424,9 +1479,12 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) // Snap by at least collision margin to keep floor state consistent. real_t length = MAX(floor_snap_length, margin); - Transform3D gt = get_global_transform(); + PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.max_collisions = 4; + parameters.collide_separation_ray = true; + PhysicsServer3D::MotionResult result; - if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) { + if (move_and_collide(parameters, result, true, false)) { CollisionState result_state; // Apply direction for floor only. _set_collision_direction(result, result_state, CollisionState(true, false, false)); @@ -1442,8 +1500,8 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) } } - gt.origin += result.travel; - set_global_transform(gt); + parameters.from.origin += result.travel; + set_global_transform(parameters.from); } } } @@ -1456,8 +1514,12 @@ bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin // Snap by at least collision margin to keep floor state consistent. real_t length = MAX(floor_snap_length, margin); + PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.max_collisions = 4; + parameters.collide_separation_ray = true; + PhysicsServer3D::MotionResult result; - if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) { + if (move_and_collide(parameters, result, true, false)) { CollisionState result_state; // Don't apply direction for any type. _set_collision_direction(result, result_state, CollisionState()); @@ -1501,6 +1563,8 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu if (ceiling_angle <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { r_state.ceiling = true; if (p_apply_state.ceiling) { + platform_ceiling_velocity = collision.collider_velocity; + ceiling_normal = collision.normal; collision_state.ceiling = true; } continue; @@ -1552,6 +1616,7 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu void CharacterBody3D::_set_platform_data(const PhysicsServer3D::MotionCollision &p_collision) { platform_rid = p_collision.collider; + platform_object_id = p_collision.collider_id; platform_velocity = p_collision.collider_velocity; platform_layer = PhysicsServer3D::get_singleton()->body_get_collision_layer(platform_rid); } @@ -1564,12 +1629,12 @@ real_t CharacterBody3D::get_safe_margin() const { return margin; } -Vector3 CharacterBody3D::get_linear_velocity() const { - return linear_velocity; +const Vector3 &CharacterBody3D::get_motion_velocity() const { + return motion_velocity; } -void CharacterBody3D::set_linear_velocity(const Vector3 &p_velocity) { - linear_velocity = p_velocity; +void CharacterBody3D::set_motion_velocity(const Vector3 &p_velocity) { + motion_velocity = p_velocity; } bool CharacterBody3D::is_on_floor() const { @@ -1596,15 +1661,15 @@ bool CharacterBody3D::is_on_ceiling_only() const { return collision_state.ceiling && !collision_state.floor && !collision_state.wall; } -Vector3 CharacterBody3D::get_floor_normal() const { +const Vector3 &CharacterBody3D::get_floor_normal() const { return floor_normal; } -Vector3 CharacterBody3D::get_wall_normal() const { +const Vector3 &CharacterBody3D::get_wall_normal() const { return wall_normal; } -Vector3 CharacterBody3D::get_last_motion() const { +const Vector3 &CharacterBody3D::get_last_motion() const { return last_motion; } @@ -1612,19 +1677,23 @@ Vector3 CharacterBody3D::get_position_delta() const { return get_transform().origin - previous_position; } -Vector3 CharacterBody3D::get_real_velocity() const { +const Vector3 &CharacterBody3D::get_real_velocity() const { return real_velocity; -}; +} real_t CharacterBody3D::get_floor_angle(const Vector3 &p_up_direction) const { ERR_FAIL_COND_V(p_up_direction == Vector3(), 0); return Math::acos(floor_normal.dot(p_up_direction)); } -Vector3 CharacterBody3D::get_platform_velocity() const { +const Vector3 &CharacterBody3D::get_platform_velocity() const { return platform_velocity; } +Vector3 CharacterBody3D::get_linear_velocity() const { + return get_real_velocity(); +} + int CharacterBody3D::get_slide_collision_count() const { return motion_results.size(); } @@ -1770,6 +1839,7 @@ void CharacterBody3D::_notification(int p_what) { // Reset move_and_slide() data. collision_state.state = 0; platform_rid = RID(); + platform_object_id = ObjectID(); motion_results.clear(); platform_velocity = Vector3(); } break; @@ -1779,8 +1849,8 @@ void CharacterBody3D::_notification(int p_what) { void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody3D::move_and_slide); - ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody3D::set_linear_velocity); - ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody3D::get_linear_velocity); + ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody3D::set_motion_velocity); + ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody3D::get_motion_velocity); ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin); @@ -1833,7 +1903,7 @@ void CharacterBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_linear_velocity", "get_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides"); ADD_GROUP("Free Mode", "free_mode_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle"); @@ -1958,51 +2028,6 @@ Vector3 KinematicCollision3D::get_collider_velocity(int p_collision_index) const return result.collisions[p_collision_index].collider_velocity; } -Variant KinematicCollision3D::get_collider_metadata(int p_collision_index) const { - ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, Variant()); - return Variant(); -} - -Vector3 KinematicCollision3D::get_best_position() const { - return result.collision_count ? get_position() : Vector3(); -} - -Vector3 KinematicCollision3D::get_best_normal() const { - return result.collision_count ? get_normal() : Vector3(); -} - -Object *KinematicCollision3D::get_best_local_shape() const { - return result.collision_count ? get_local_shape() : nullptr; -} - -Object *KinematicCollision3D::get_best_collider() const { - return result.collision_count ? get_collider() : nullptr; -} - -ObjectID KinematicCollision3D::get_best_collider_id() const { - return result.collision_count ? get_collider_id() : ObjectID(); -} - -RID KinematicCollision3D::get_best_collider_rid() const { - return result.collision_count ? get_collider_rid() : RID(); -} - -Object *KinematicCollision3D::get_best_collider_shape() const { - return result.collision_count ? get_collider_shape() : nullptr; -} - -int KinematicCollision3D::get_best_collider_shape_index() const { - return result.collision_count ? get_collider_shape_index() : 0; -} - -Vector3 KinematicCollision3D::get_best_collider_velocity() const { - return result.collision_count ? get_collider_velocity() : Vector3(); -} - -Variant KinematicCollision3D::get_best_collider_metadata() const { - return result.collision_count ? get_collider_metadata() : Variant(); -} - void KinematicCollision3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision3D::get_travel); ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision3D::get_remainder); @@ -2017,32 +2042,6 @@ void KinematicCollision3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collider_shape", "collision_index"), &KinematicCollision3D::get_collider_shape, DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_collider_shape_index", "collision_index"), &KinematicCollision3D::get_collider_shape_index, DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_collider_velocity", "collision_index"), &KinematicCollision3D::get_collider_velocity, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("get_collider_metadata", "collision_index"), &KinematicCollision3D::get_collider_metadata, DEFVAL(0)); - - ClassDB::bind_method(D_METHOD("get_best_position"), &KinematicCollision3D::get_best_position); - ClassDB::bind_method(D_METHOD("get_best_normal"), &KinematicCollision3D::get_best_normal); - ClassDB::bind_method(D_METHOD("get_best_local_shape"), &KinematicCollision3D::get_best_local_shape); - ClassDB::bind_method(D_METHOD("get_best_collider"), &KinematicCollision3D::get_best_collider); - ClassDB::bind_method(D_METHOD("get_best_collider_id"), &KinematicCollision3D::get_best_collider_id); - ClassDB::bind_method(D_METHOD("get_best_collider_rid"), &KinematicCollision3D::get_best_collider_rid); - ClassDB::bind_method(D_METHOD("get_best_collider_shape"), &KinematicCollision3D::get_best_collider_shape); - ClassDB::bind_method(D_METHOD("get_best_collider_shape_index"), &KinematicCollision3D::get_best_collider_shape_index); - ClassDB::bind_method(D_METHOD("get_best_collider_velocity"), &KinematicCollision3D::get_best_collider_velocity); - ClassDB::bind_method(D_METHOD("get_best_collider_metadata"), &KinematicCollision3D::get_best_collider_metadata); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "travel"), "", "get_travel"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "remainder"), "", "get_remainder"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_count"), "", "get_collision_count"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "", "get_best_position"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "normal"), "", "get_best_normal"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_best_local_shape"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_best_collider"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_best_collider_id"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_best_collider_rid"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_best_collider_shape"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_best_collider_shape_index"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collider_velocity"), "", "get_best_collider_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::NIL, "collider_metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "", "get_best_collider_metadata"); } /////////////////////////////////////// @@ -2981,14 +2980,11 @@ void PhysicalBone3D::_on_bone_parent_changed() { _reload_joint(); } -void PhysicalBone3D::_set_gizmo_move_joint(bool p_move_joint) { #ifdef TOOLS_ENABLED +void PhysicalBone3D::_set_gizmo_move_joint(bool p_move_joint) { gizmo_move_joint = p_move_joint; - Node3DEditor::get_singleton()->update_transform_gizmo(); -#endif } -#ifdef TOOLS_ENABLED Transform3D PhysicalBone3D::get_global_gizmo_transform() const { return gizmo_move_joint ? get_global_transform() * joint_offset : get_global_transform(); } diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index a53147cb8f..5677df730c 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -50,11 +50,11 @@ protected: uint16_t locked_axis = 0; - Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1); + Ref<KinematicCollision3D> _move(const Vector3 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1); public: - bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, int p_max_collisions = 1, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()); - bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1); + bool move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true); + bool test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1); void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const; @@ -328,8 +328,8 @@ public: }; bool move_and_slide(); - virtual Vector3 get_linear_velocity() const override; - void set_linear_velocity(const Vector3 &p_velocity); + const Vector3 &get_motion_velocity() const; + void set_motion_velocity(const Vector3 &p_velocity); bool is_on_floor() const; bool is_on_floor_only() const; @@ -337,13 +337,15 @@ public: bool is_on_wall_only() const; bool is_on_ceiling() const; bool is_on_ceiling_only() const; - Vector3 get_last_motion() const; + const Vector3 &get_last_motion() const; Vector3 get_position_delta() const; - Vector3 get_floor_normal() const; - Vector3 get_wall_normal() const; - Vector3 get_real_velocity() const; + const Vector3 &get_floor_normal() const; + const Vector3 &get_wall_normal() const; + const Vector3 &get_real_velocity() const; real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; - Vector3 get_platform_velocity() const; + const Vector3 &get_platform_velocity() const; + + virtual Vector3 get_linear_velocity() const override; int get_slide_collision_count() const; PhysicsServer3D::MotionResult get_slide_collision(int p_bounce) const; @@ -379,19 +381,22 @@ private: bool floor_block_on_wall = true; bool slide_on_ceiling = true; int max_slides = 6; - int platform_layer; + int platform_layer = 0; RID platform_rid; + ObjectID platform_object_id; uint32_t moving_platform_floor_layers = UINT32_MAX; uint32_t moving_platform_wall_layers = 0; real_t floor_snap_length = 0.1; real_t floor_max_angle = Math::deg2rad((real_t)45.0); real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0); Vector3 up_direction = Vector3(0.0, 1.0, 0.0); - Vector3 linear_velocity; + Vector3 motion_velocity; Vector3 floor_normal; Vector3 wall_normal; + Vector3 ceiling_normal; Vector3 last_motion; Vector3 platform_velocity; + Vector3 platform_ceiling_velocity; Vector3 previous_position; Vector3 real_velocity; @@ -483,18 +488,6 @@ public: Object *get_collider_shape(int p_collision_index = 0) const; int get_collider_shape_index(int p_collision_index = 0) const; Vector3 get_collider_velocity(int p_collision_index = 0) const; - Variant get_collider_metadata(int p_collision_index = 0) const; - - Vector3 get_best_position() const; - Vector3 get_best_normal() const; - Object *get_best_local_shape() const; - Object *get_best_collider() const; - ObjectID get_best_collider_id() const; - RID get_best_collider_rid() const; - Object *get_best_collider_shape() const; - int get_best_collider_shape_index() const; - Vector3 get_best_collider_velocity() const; - Variant get_best_collider_metadata() const; }; class PhysicalBone3D : public PhysicsBody3D { @@ -662,10 +655,9 @@ private: public: void _on_bone_parent_changed(); - void _set_gizmo_move_joint(bool p_move_joint); -public: #ifdef TOOLS_ENABLED + void _set_gizmo_move_joint(bool p_move_joint); virtual Transform3D get_global_gizmo_transform() const override; virtual Transform3D get_local_gizmo_transform() const override; #endif diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 8d90aabfac..2e788051f4 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -291,14 +291,10 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove new_bone_pose.origin = ci->current_pos; if (!ci->children.is_empty()) { - /// Rotate basis - const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized()); - const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); - - if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { - const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); - new_bone_pose.basis.rotate(rot_axis, rot_angle); - } + p_task->skeleton->update_bone_rest_forward_vector(ci->bone); + Vector3 forward_vector = p_task->skeleton->get_bone_axis_forward_vector(ci->bone); + // Rotate the bone towards the next bone in the chain: + new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos)); } else { // Set target orientation to tip diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp index a886c61263..21f9b0a35d 100644 --- a/scene/3d/soft_dynamic_body_3d.cpp +++ b/scene/3d/soft_dynamic_body_3d.cpp @@ -433,9 +433,9 @@ void SoftDynamicBody3D::prepare_physics_server() { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { if (get_mesh().is_valid()) { - PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()->get_rid()); } else { - PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, nullptr); + PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID()); } return; @@ -444,10 +444,10 @@ void SoftDynamicBody3D::prepare_physics_server() { if (get_mesh().is_valid() && (is_enabled() || (disable_mode != DISABLE_MODE_REMOVE))) { become_mesh_owner(); - PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()->get_rid()); RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh)); } else { - PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, nullptr); + PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID()); if (RS::get_singleton()->is_connected("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh))) { RS::get_singleton()->disconnect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh)); } diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp index 4748a9d889..116cab19b1 100644 --- a/scene/3d/spring_arm_3d.cpp +++ b/scene/3d/spring_arm_3d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "spring_arm_3d.h" +#include "scene/3d/camera_3d.h" void SpringArm3D::_notification(int p_what) { switch (p_what) { @@ -133,17 +134,32 @@ void SpringArm3D::process_spring() { Vector3 motion; const Vector3 cast_direction(get_global_transform().basis.xform(Vector3(0, 0, 1))); + motion = Vector3(cast_direction * (spring_length)); + if (shape.is_null()) { - motion = Vector3(cast_direction * (spring_length)); - PhysicsDirectSpaceState3D::RayResult r; - bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask); - if (intersected) { - real_t dist = get_global_transform().origin.distance_to(r.position); - dist -= margin; - motion_delta = dist / (spring_length); + Camera3D *camera = nullptr; + for (int i = get_child_count() - 1; 0 <= i; --i) { + camera = Object::cast_to<Camera3D>(get_child(i)); + if (camera) { + break; + } + } + + if (camera != nullptr) { + //use camera rotation, but spring arm position + Transform3D base_transform = camera->get_global_transform(); + base_transform.origin = get_global_transform().origin; + get_world_3d()->get_direct_space_state()->cast_motion(camera->get_pyramid_shape_rid(), base_transform, motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask); + } else { + PhysicsDirectSpaceState3D::RayResult r; + bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask); + if (intersected) { + real_t dist = get_global_transform().origin.distance_to(r.position); + dist -= margin; + motion_delta = dist / (spring_length); + } } } else { - motion = Vector3(cast_direction * spring_length); get_world_3d()->get_direct_space_state()->cast_motion(shape->get_rid(), get_global_transform(), motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask); } diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index af186072ac..10a66386eb 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -828,9 +828,9 @@ Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) co } StringName AnimationNodeBlendTree::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - if (E->get().node == p_node) { - return E->key(); + for (const KeyValue<StringName, Node> &E : nodes) { + if (E.value.node == p_node) { + return E.key; } } @@ -850,8 +850,8 @@ Vector2 AnimationNodeBlendTree::get_node_position(const StringName &p_node) cons void AnimationNodeBlendTree::get_child_nodes(List<ChildNode> *r_child_nodes) { Vector<StringName> ns; - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - ns.push_back(E->key()); + for (const KeyValue<StringName, Node> &E : nodes) { + ns.push_back(E.key); } ns.sort_custom<StringName::AlphCompare>(); @@ -886,10 +886,10 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) { nodes.erase(p_name); //erase connections to name - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().connections.size(); i++) { - if (E->get().connections[i] == p_name) { - E->get().connections.write[i] = StringName(); + for (KeyValue<StringName, Node> &E : nodes) { + for (int i = 0; i < E.value.connections.size(); i++) { + if (E.value.connections[i] == p_name) { + E.value.connections.write[i] = StringName(); } } } @@ -910,10 +910,10 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN nodes.erase(p_name); //rename connections - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().connections.size(); i++) { - if (E->get().connections[i] == p_name) { - E->get().connections.write[i] = p_new_name; + for (KeyValue<StringName, Node> &E : nodes) { + for (int i = 0; i < E.value.connections.size(); i++) { + if (E.value.connections[i] == p_name) { + E.value.connections.write[i] = p_new_name; } } } @@ -932,9 +932,9 @@ void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_ Ref<AnimationNode> input = nodes[p_input_node].node; ERR_FAIL_INDEX(p_input_index, nodes[p_input_node].connections.size()); - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().connections.size(); i++) { - StringName output = E->get().connections[i]; + for (KeyValue<StringName, Node> &E : nodes) { + for (int i = 0; i < E.value.connections.size(); i++) { + StringName output = E.value.connections[i]; ERR_FAIL_COND(output == p_output_node); } } @@ -976,9 +976,9 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node return CONNECTION_ERROR_CONNECTION_EXISTS; } - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().connections.size(); i++) { - StringName output = E->get().connections[i]; + for (const KeyValue<StringName, Node> &E : nodes) { + for (int i = 0; i < E.value.connections.size(); i++) { + const StringName output = E.value.connections[i]; if (output == p_output_node) { return CONNECTION_ERROR_CONNECTION_EXISTS; } @@ -988,12 +988,12 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node } void AnimationNodeBlendTree::get_node_connections(List<NodeConnection> *r_connections) const { - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().connections.size(); i++) { - StringName output = E->get().connections[i]; + for (const KeyValue<StringName, Node> &E : nodes) { + for (int i = 0; i < E.value.connections.size(); i++) { + const StringName output = E.value.connections[i]; if (output != StringName()) { NodeConnection nc; - nc.input_node = E->key(); + nc.input_node = E.key; nc.input_index = i; nc.output_node = output; r_connections->push_back(nc); @@ -1012,8 +1012,8 @@ double AnimationNodeBlendTree::process(double p_time, bool p_seek) { } void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) { - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - r_list->push_back(E->key()); + for (const KeyValue<StringName, Node> &E : nodes) { + r_list->push_back(E.key); } } @@ -1104,8 +1104,8 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { - names.push_back(E->key()); + for (const KeyValue<StringName, Node> &E : nodes) { + names.push_back(E.key); } names.sort_custom<StringName::AlphCompare>(); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 9fc1dbd0c6..81ecb50ea0 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -329,11 +329,17 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } } else { // teleport to start - path.clear(); - current = start_request; - playing = true; - play_start = true; - start_request = StringName(); //clear start request + if (p_state_machine->states.has(start_request)) { + path.clear(); + current = start_request; + playing = true; + play_start = true; + start_request = StringName(); //clear start request + } else { + StringName node = start_request; + start_request = StringName(); //clear start request + ERR_FAIL_V_MSG(0, "No such node: '" + node + "'"); + } } } @@ -571,9 +577,9 @@ Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) } StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { - if (E->get().node == p_node) { - return E->key(); + for (const KeyValue<StringName, State> &E : states) { + if (E.value.node == p_node) { + return E.key; } } @@ -583,8 +589,8 @@ StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_ void AnimationNodeStateMachine::get_child_nodes(List<ChildNode> *r_child_nodes) { Vector<StringName> nodes; - for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { - nodes.push_back(E->key()); + for (const KeyValue<StringName, State> &E : states) { + nodes.push_back(E.key); } nodes.sort_custom<StringName::AlphCompare>(); @@ -674,8 +680,8 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { List<StringName> nodes; - for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { - nodes.push_back(E->key()); + for (const KeyValue<StringName, State> &E : states) { + nodes.push_back(E.key); } nodes.sort_custom<StringName::AlphCompare>(); @@ -897,8 +903,8 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { - names.push_back(E->key()); + for (const KeyValue<StringName, State> &E : states) { + names.push_back(E.key); } names.sort_custom<StringName::AlphCompare>(); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 5825a35030..2c8c4ee788 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -37,7 +37,6 @@ #ifdef TOOLS_ENABLED #include "editor/editor_node.h" -#include "editor/editor_settings.h" #include "scene/2d/skeleton_2d.h" void AnimatedValuesBackup::update_skeletons() { @@ -124,8 +123,8 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { } else if (name == "blend_times") { Vector<BlendKey> keys; - for (Map<BlendKey, float>::Element *E = blend_times.front(); E; E = E->next()) { - keys.ordered_insert(E->key()); + for (const KeyValue<BlendKey, float> &E : blend_times) { + keys.ordered_insert(E.key); } Array array; @@ -147,8 +146,8 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const { if (property.name == "current_animation") { List<String> names; - for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) { - names.push_back(E->key()); + for (const KeyValue<StringName, AnimationData> &E : animation_set) { + names.push_back(E.key); } names.sort(); names.push_front("[stop]"); @@ -167,10 +166,10 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const { void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> anim_names; - for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) { - anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E->key()), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); - if (E->get().next != StringName()) { - anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + for (const KeyValue<StringName, AnimationData> &E : animation_set) { + anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + if (E.value.next != StringName()) { + anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } } @@ -1019,8 +1018,8 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam List<BlendKey> to_erase; Map<BlendKey, float> to_insert; - for (Map<BlendKey, float>::Element *E = blend_times.front(); E; E = E->next()) { - BlendKey bk = E->key(); + for (const KeyValue<BlendKey, float> &E : blend_times) { + BlendKey bk = E.key; BlendKey new_bk = bk; bool erase = false; if (bk.from == p_name) { @@ -1034,7 +1033,7 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam if (erase) { to_erase.push_back(bk); - to_insert[new_bk] = E->get(); + to_insert[new_bk] = E.value; } } @@ -1071,8 +1070,8 @@ Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const { void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const { List<String> anims; - for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) { - anims.push_back(E->key()); + for (const KeyValue<StringName, AnimationData> &E : animation_set) { + anims.push_back(E.key); } anims.sort(); @@ -1365,8 +1364,8 @@ void AnimationPlayer::clear_caches() { node_cache_map.clear(); - for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) { - E->get().node_cache.clear(); + for (KeyValue<StringName, AnimationData> &E : animation_set) { + E.value.node_cache.clear(); } cache_update_size = 0; @@ -1388,9 +1387,9 @@ bool AnimationPlayer::is_active() const { } StringName AnimationPlayer::find_animation(const Ref<Animation> &p_animation) const { - for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) { - if (E->get().animation == p_animation) { - return E->key(); + for (const KeyValue<StringName, AnimationData> &E : animation_set) { + if (E.value.animation == p_animation) { + return E.key; } } @@ -1493,18 +1492,12 @@ NodePath AnimationPlayer::get_root() const { } void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { -#ifdef TOOLS_ENABLED - const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; -#else - const String quote_style = "\""; -#endif - String pf = p_function; if (p_idx == 0 && (p_function == "play" || p_function == "play_backwards" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue")) { List<StringName> al; get_animation_list(&al); for (const StringName &name : al) { - r_options->push_back(String(name).quote(quote_style)); + r_options->push_back(String(name).quote()); } } Node::get_argument_options(p_function, p_idx, r_options); @@ -1545,12 +1538,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o entry.bone_idx = -1; backup->entries.push_back(entry); } else { - for (Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.front(); E; E = E->next()) { + for (const KeyValue<StringName, TrackNodeCache::PropertyAnim> &E : nc->property_anim) { AnimatedValuesBackup::Entry entry; - entry.object = E->value().object; - entry.subpath = E->value().subpath; + entry.object = E.value.object; + entry.subpath = E.value.subpath; bool valid; - entry.value = E->value().object->get_indexed(E->value().subpath, &valid); + entry.value = E.value.object->get_indexed(E.value.subpath, &valid); entry.bone_idx = -1; if (valid) { backup->entries.push_back(entry); diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index f4e477b613..3c8949ddfb 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -249,8 +249,8 @@ void SceneDebugger::remove_from_cache(const String &p_filename, Node *p_node) { Map<Node *, Map<ObjectID, Node *>> &remove_list = debugger->live_edit_remove_list; Map<Node *, Map<ObjectID, Node *>>::Element *F = remove_list.find(p_node); if (F) { - for (Map<ObjectID, Node *>::Element *G = F->get().front(); G; G = G->next()) { - memdelete(G->get()); + for (const KeyValue<ObjectID, Node *> &G : F->get()) { + memdelete(G.value); } remove_list.erase(F); } @@ -339,15 +339,15 @@ void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInsta } // Constants for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) { - for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) { + for (const KeyValue<StringName, Variant> &E : sc->get()) { String script_path = sc->key() == p_script ? "" : sc->key()->get_path().get_file() + "/"; - if (E->value().get_type() == Variant::OBJECT) { - Variant id = ((Object *)E->value())->get_instance_id(); - PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object"); + if (E.value.get_type() == Variant::OBJECT) { + Variant id = ((Object *)E.value)->get_instance_id(); + PropertyInfo pi(id.get_type(), "Constants/" + E.key, PROPERTY_HINT_OBJECT_ID, "Object"); properties.push_back(SceneDebuggerProperty(pi, id)); } else { - PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key()); - properties.push_back(SceneDebuggerProperty(pi, E->value())); + PropertyInfo pi(E.value.get_type(), "Constants/" + script_path + E.key); + properties.push_back(SceneDebuggerProperty(pi, E.value)); } } } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index c7d1d7ef82..b816f12bc3 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -614,6 +614,11 @@ void CodeEdit::_backspace_internal() { return; } + if (has_selection()) { + delete_selection(); + return; + } + int cc = get_caret_column(); int cl = get_caret_line(); @@ -621,11 +626,6 @@ void CodeEdit::_backspace_internal() { return; } - if (has_selection()) { - delete_selection(); - return; - } - if (cl > 0 && _is_line_hidden(cl - 1)) { unfold_line(get_caret_line() - 1); } @@ -1146,13 +1146,20 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const { } void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { - if (draw_breakpoints && is_line_breakpointed(p_line)) { - int padding = p_region.size.x / 6; + if (draw_breakpoints) { + bool hovering = p_region.has_point(get_local_mouse_pos()); + bool breakpointed = is_line_breakpointed(p_line); + + if (breakpointed || (hovering && !is_dragging_cursor())) { + int padding = p_region.size.x / 6; + Rect2 icon_region = p_region; + icon_region.position += Point2(padding, padding); + icon_region.size -= Point2(padding, padding) * 2; - Rect2 breakpoint_region = p_region; - breakpoint_region.position += Point2(padding, padding); - breakpoint_region.size -= Point2(padding, padding) * 2; - breakpoint_icon->draw_rect(get_canvas_item(), breakpoint_region, false, breakpoint_color); + // Darken icon when hovering & not yet breakpointed. + Color use_color = hovering && !breakpointed ? breakpoint_color.darkened(0.4) : breakpoint_color; + breakpoint_icon->draw_rect(get_canvas_item(), icon_region, false, use_color); + } } if (draw_bookmarks && is_line_bookmarked(p_line)) { @@ -1613,12 +1620,12 @@ Point2 CodeEdit::get_delimiter_start_position(int p_line, int p_column) const { bool in_region = ((p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value()) != -1; /* Check the keys for this line. */ - for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) { - if (E->key() > p_column) { + for (const KeyValue<int, int> &E : delimiter_cache[p_line]) { + if (E.key > p_column) { break; } - in_region = E->value() != -1; - start_position.x = in_region ? E->key() : -1; + in_region = E.value != -1; + start_position.x = in_region ? E.key : -1; } /* Region was found on this line and is not a multiline continuation. */ @@ -1664,12 +1671,12 @@ Point2 CodeEdit::get_delimiter_end_position(int p_line, int p_column) const { int region = (p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value(); /* Check the keys for this line. */ - for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) { - end_position.x = (E->value() == -1) ? E->key() : -1; - if (E->key() > p_column) { + for (const KeyValue<int, int> &E : delimiter_cache[p_line]) { + end_position.x = (E.value == -1) ? E.key : -1; + if (E.key > p_column) { break; } - region = E->value(); + region = E.value; } /* Region was found on this line and is not a multiline continuation. */ diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 611035fff9..54548e1941 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -35,7 +35,6 @@ #include "core/os/os.h" #ifdef TOOLS_ENABLED -#include "editor/editor_scale.h" #include "editor/editor_settings.h" #endif #include "scene/main/window.h" @@ -44,17 +43,7 @@ List<Color> ColorPicker::preset_cache; void ColorPicker::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: { - btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker"))); - btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset"))); - _update_presets(); - _update_controls(); - } break; case NOTIFICATION_ENTER_TREE: { - btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker"))); - btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset"))); - - _update_controls(); _update_color(); #ifdef TOOLS_ENABLED @@ -71,18 +60,39 @@ void ColorPicker::_notification(int p_what) { } } #endif - } break; - case NOTIFICATION_PARENTED: { + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker"))); + btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset"))); + + uv_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height")))); + w_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("h_width")), 0)); + + wheel_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height")))); + wheel_margin->add_theme_constant_override("margin_bottom", 8 * get_theme_default_base_scale()); + for (int i = 0; i < 4; i++) { + labels[i]->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0)); set_offset((Side)i, get_offset((Side)i) + get_theme_constant(SNAME("margin"))); } + + if (Engine::get_singleton()->is_editor_hint()) { + // Adjust for the width of the "Script" icon. + text_type->set_custom_minimum_size(Size2(28 * get_theme_default_base_scale(), 0)); + } + + _update_presets(); + _update_controls(); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { Popup *p = Object::cast_to<Popup>(get_parent()); if (p) { p->set_size(Size2(get_combined_minimum_size().width + get_theme_constant(SNAME("margin")) * 2, get_combined_minimum_size().height + get_theme_constant(SNAME("margin")) * 2)); } } break; + case NOTIFICATION_WM_CLOSE_REQUEST: { if (screen != nullptr && screen->is_visible()) { screen->hide(); @@ -762,11 +772,7 @@ void ColorPicker::_slider_draw(int p_which) { Size2 size = scroll[p_which]->get_size(); Color left_color; Color right_color; -#ifdef TOOLS_ENABLED - const real_t margin = 4 * EDSCALE; -#else - const real_t margin = 4; -#endif + const real_t margin = 4 * get_theme_default_base_scale(); if (p_which == 3) { scroll[p_which]->draw_texture_rect(get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true); @@ -1147,7 +1153,6 @@ ColorPicker::ColorPicker() : uv_edit->set_mouse_filter(MOUSE_FILTER_PASS); uv_edit->set_h_size_flags(SIZE_EXPAND_FILL); uv_edit->set_v_size_flags(SIZE_EXPAND_FILL); - uv_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height")))); uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit)); HBoxContainer *hb_smpl = memnew(HBoxContainer); @@ -1219,9 +1224,6 @@ ColorPicker::ColorPicker() : text_type->set_text("#"); text_type->set_tooltip(TTR("Switch between hexadecimal and code values.")); if (Engine::get_singleton()->is_editor_hint()) { -#ifdef TOOLS_ENABLED - text_type->set_custom_minimum_size(Size2(28 * EDSCALE, 0)); // Adjust for the width of the "Script" icon. -#endif text_type->connect("pressed", callable_mp(this, &ColorPicker::_text_type_toggled)); } else { text_type->set_flat(true); @@ -1236,7 +1238,6 @@ ColorPicker::ColorPicker() : wheel_edit->set_h_size_flags(SIZE_EXPAND_FILL); wheel_edit->set_v_size_flags(SIZE_EXPAND_FILL); - wheel_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height")))); hb_edit->add_child(wheel_edit); wheel_mat.instantiate(); @@ -1244,12 +1245,7 @@ ColorPicker::ColorPicker() : circle_mat.instantiate(); circle_mat->set_shader(circle_shader); - MarginContainer *wheel_margin(memnew(MarginContainer)); -#ifdef TOOLS_ENABLED - wheel_margin->add_theme_constant_override("margin_bottom", 8 * EDSCALE); -#else wheel_margin->add_theme_constant_override("margin_bottom", 8); -#endif wheel_edit->add_child(wheel_margin); wheel_margin->add_child(wheel); @@ -1261,7 +1257,6 @@ ColorPicker::ColorPicker() : wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv)); hb_edit->add_child(w_edit); - w_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("h_width")), 0)); w_edit->set_h_size_flags(SIZE_FILL); w_edit->set_v_size_flags(SIZE_EXPAND_FILL); w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 67ca007eb5..ad4f5ad5b1 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -81,6 +81,7 @@ private: Control *uv_edit = memnew(Control); Control *w_edit = memnew(Control); AspectRatioContainer *wheel_edit = memnew(AspectRatioContainer); + MarginContainer *wheel_margin = memnew(MarginContainer); Ref<ShaderMaterial> wheel_mat; Ref<ShaderMaterial> circle_mat; Control *wheel = memnew(Control); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 81411d5844..38da40a402 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -38,7 +38,6 @@ #include "core/os/os.h" #include "core/string/print_string.h" #include "core/string/translation.h" - #include "scene/gui/label.h" #include "scene/gui/panel.h" #include "scene/main/canvas_layer.h" @@ -48,7 +47,6 @@ #include "servers/text_server.h" #ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" #include "editor/plugins/canvas_item_editor_plugin.h" #endif @@ -742,7 +740,7 @@ bool Control::has_point(const Point2 &p_point) const { return Rect2(Point2(), get_size()).has_point(p_point); } -void Control::set_drag_forwarding(Control *p_target) { +void Control::set_drag_forwarding(Node *p_target) { if (p_target) { data.drag_owner = p_target->get_instance_id(); } else { @@ -754,8 +752,7 @@ Variant Control::get_drag_data(const Point2 &p_point) { if (data.drag_owner.is_valid()) { Object *obj = ObjectDB::get_instance(data.drag_owner); if (obj) { - Control *c = Object::cast_to<Control>(obj); - return c->call("_get_drag_data_fw", p_point, this); + return obj->call("_get_drag_data_fw", p_point, this); } } @@ -771,8 +768,7 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const if (data.drag_owner.is_valid()) { Object *obj = ObjectDB::get_instance(data.drag_owner); if (obj) { - Control *c = Object::cast_to<Control>(obj); - return c->call("_can_drop_data_fw", p_point, p_data, this); + return obj->call("_can_drop_data_fw", p_point, p_data, this); } } @@ -787,8 +783,7 @@ void Control::drop_data(const Point2 &p_point, const Variant &p_data) { if (data.drag_owner.is_valid()) { Object *obj = ObjectDB::get_instance(data.drag_owner); if (obj) { - Control *c = Object::cast_to<Control>(obj); - c->call("_drop_data_fw", p_point, p_data, this); + obj->call("_drop_data_fw", p_point, p_data, this); return; } } @@ -836,11 +831,12 @@ T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified."); // First, look through each control or window node in the branch, until no valid parent can be found. - // For each control iterate through its inheritance chain and see if p_name exists in any of them. + // Only nodes with a theme resource attached are considered. Control *theme_owner = p_theme_owner; Window *theme_owner_window = p_theme_owner_window; while (theme_owner || theme_owner_window) { + // For each theme resource check the theme types provided and see if p_name exists with any of them. for (const StringName &E : p_theme_types) { if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) { return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E); @@ -891,11 +887,12 @@ bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_ow ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified."); // First, look through each control or window node in the branch, until no valid parent can be found. - // For each control iterate through its inheritance chain and see if p_name exists in any of them. + // Only nodes with a theme resource attached are considered. Control *theme_owner = p_theme_owner; Window *theme_owner_window = p_theme_owner_window; while (theme_owner || theme_owner_window) { + // For each theme resource check the theme types provided and see if p_name exists with any of them. for (const StringName &E : p_theme_types) { if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) { return true; @@ -1133,6 +1130,150 @@ bool Control::has_theme_constant(const StringName &p_name, const StringName &p_t return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } +float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window) { + // First, look through each control or window node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + // For each theme resource see if their assigned theme has the default value defined and valid. + Control *theme_owner = p_theme_owner; + Window *theme_owner_window = p_theme_owner_window; + + while (theme_owner || theme_owner_window) { + if (theme_owner && theme_owner->data.theme->has_default_theme_base_scale()) { + return theme_owner->data.theme->get_default_theme_base_scale(); + } + + if (theme_owner_window && theme_owner_window->theme->has_default_theme_base_scale()) { + return theme_owner_window->theme->get_default_theme_base_scale(); + } + + Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); + Control *parent_c = Object::cast_to<Control>(parent); + if (parent_c) { + theme_owner = parent_c->data.theme_owner; + theme_owner_window = parent_c->data.theme_owner_window; + } else { + Window *parent_w = Object::cast_to<Window>(parent); + if (parent_w) { + theme_owner = parent_w->theme_owner; + theme_owner_window = parent_w->theme_owner_window; + } else { + theme_owner = nullptr; + theme_owner_window = nullptr; + } + } + } + + // Secondly, check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + if (Theme::get_project_default()->has_default_theme_base_scale()) { + return Theme::get_project_default()->get_default_theme_base_scale(); + } + } + + // Lastly, fall back on the default Theme. + return Theme::get_default()->get_default_theme_base_scale(); +} + +float Control::get_theme_default_base_scale() const { + return fetch_theme_default_base_scale(data.theme_owner, data.theme_owner_window); +} + +Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window) { + // First, look through each control or window node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + // For each theme resource see if their assigned theme has the default value defined and valid. + Control *theme_owner = p_theme_owner; + Window *theme_owner_window = p_theme_owner_window; + + while (theme_owner || theme_owner_window) { + if (theme_owner && theme_owner->data.theme->has_default_theme_font()) { + return theme_owner->data.theme->get_default_theme_font(); + } + + if (theme_owner_window && theme_owner_window->theme->has_default_theme_font()) { + return theme_owner_window->theme->get_default_theme_font(); + } + + Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); + Control *parent_c = Object::cast_to<Control>(parent); + if (parent_c) { + theme_owner = parent_c->data.theme_owner; + theme_owner_window = parent_c->data.theme_owner_window; + } else { + Window *parent_w = Object::cast_to<Window>(parent); + if (parent_w) { + theme_owner = parent_w->theme_owner; + theme_owner_window = parent_w->theme_owner_window; + } else { + theme_owner = nullptr; + theme_owner_window = nullptr; + } + } + } + + // Secondly, check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + if (Theme::get_project_default()->has_default_theme_font()) { + return Theme::get_project_default()->get_default_theme_font(); + } + } + + // Lastly, fall back on the default Theme. + return Theme::get_default()->get_default_theme_font(); +} + +Ref<Font> Control::get_theme_default_font() const { + return fetch_theme_default_font(data.theme_owner, data.theme_owner_window); +} + +int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window) { + // First, look through each control or window node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + // For each theme resource see if their assigned theme has the default value defined and valid. + Control *theme_owner = p_theme_owner; + Window *theme_owner_window = p_theme_owner_window; + + while (theme_owner || theme_owner_window) { + if (theme_owner && theme_owner->data.theme->has_default_theme_font_size()) { + return theme_owner->data.theme->get_default_theme_font_size(); + } + + if (theme_owner_window && theme_owner_window->theme->has_default_theme_font_size()) { + return theme_owner_window->theme->get_default_theme_font_size(); + } + + Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); + Control *parent_c = Object::cast_to<Control>(parent); + if (parent_c) { + theme_owner = parent_c->data.theme_owner; + theme_owner_window = parent_c->data.theme_owner_window; + } else { + Window *parent_w = Object::cast_to<Window>(parent); + if (parent_w) { + theme_owner = parent_w->theme_owner; + theme_owner_window = parent_w->theme_owner_window; + } else { + theme_owner = nullptr; + theme_owner_window = nullptr; + } + } + } + + // Secondly, check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + if (Theme::get_project_default()->has_default_theme_font_size()) { + return Theme::get_project_default()->get_default_theme_font_size(); + } + } + + // Lastly, fall back on the default Theme. + return Theme::get_default()->get_default_theme_font_size(); +} + +int Control::get_theme_default_font_size() const { + return fetch_theme_default_font_size(data.theme_owner, data.theme_owner_window); +} + Rect2 Control::get_parent_anchorable_rect() const { if (!is_inside_tree()) { return Rect2(); @@ -2452,8 +2593,8 @@ bool Control::is_text_field() const { return false; } -Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const { - Vector<Vector2i> ret; +Array Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const { + Array ret; switch (p_theme_type) { case STRUCTURED_TEXT_URI: { int prev = 0; @@ -2619,12 +2760,6 @@ bool Control::is_visibility_clip_disabled() const { } void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { -#ifdef TOOLS_ENABLED - const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; -#else - const String quote_style = "\""; -#endif - Node::get_argument_options(p_function, p_idx, r_options); if (p_idx == 0) { @@ -2644,7 +2779,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List sn.sort_custom<StringName::AlphCompare>(); for (const StringName &name : sn) { - r_options->push_back(String(name).quote(quote_style)); + r_options->push_back(String(name).quote()); } } } @@ -2792,6 +2927,10 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Control::get_theme_default_base_scale); + ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Control::get_theme_default_font); + ClassDB::bind_method(D_METHOD("get_theme_default_font_size"), &Control::get_theme_default_font_size); + ClassDB::bind_method(D_METHOD("get_parent_control"), &Control::get_parent_control); ClassDB::bind_method(D_METHOD("set_h_grow_direction", "direction"), &Control::set_h_grow_direction); diff --git a/scene/gui/control.h b/scene/gui/control.h index 9cec5d6e8d..bdc06319ea 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -283,7 +283,7 @@ protected: //virtual void _window_gui_input(InputEvent p_event); - virtual Vector<Vector2i> structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const; + virtual Array structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -355,7 +355,7 @@ public: virtual Size2 get_minimum_size() const; virtual Size2 get_combined_minimum_size() const; virtual bool has_point(const Point2 &p_point) const; - virtual void set_drag_forwarding(Control *p_target); + virtual void set_drag_forwarding(Node *p_target); virtual Variant get_drag_data(const Point2 &p_point); virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; virtual void drop_data(const Point2 &p_point, const Variant &p_data); @@ -509,6 +509,14 @@ public: bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + static float fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window); + static Ref<Font> fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window); + static int fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window); + + float get_theme_default_base_scale() const; + Ref<Font> get_theme_default_font() const; + int get_theme_default_font_size() const; + /* TOOLTIP */ void set_tooltip(const String &p_tooltip); diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 5d98aaa698..71d2778cc3 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -37,7 +37,6 @@ #ifdef TOOLS_ENABLED #include "editor/editor_node.h" -#include "editor/editor_scale.h" #include "scene/main/window.h" // Only used to check for more modals when dimming the editor. #endif @@ -363,8 +362,7 @@ Button *ConfirmationDialog::get_cancel_button() { ConfirmationDialog::ConfirmationDialog() { set_title(TTRC("Please Confirm...")); -#ifdef TOOLS_ENABLED - set_min_size(Size2(200, 70) * EDSCALE); -#endif + set_min_size(Size2(200, 70)); + cancel = add_cancel_button(); } diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 56b8a936e1..5d024d3be7 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -32,15 +32,6 @@ #include "core/os/keyboard.h" -#ifdef TOOLS_ENABLED -#include "editor/editor_scale.h" -#define SPACING (3 * EDSCALE) -#define POINT_WIDTH (8 * EDSCALE) -#else -#define SPACING 3 -#define POINT_WIDTH 8 -#endif - GradientEdit::GradientEdit() { set_focus_mode(FOCUS_ALL); @@ -53,12 +44,12 @@ GradientEdit::GradientEdit() { int GradientEdit::_get_point_from_pos(int x) { int result = -1; - int total_w = get_size().width - get_size().height - SPACING; + int total_w = get_size().width - get_size().height - draw_spacing; float min_distance = 1e20; for (int i = 0; i < points.size(); i++) { //Check if we clicked at point float distance = ABS(x - points[i].offset * total_w); - float min = (POINT_WIDTH / 2 * 1.7); //make it easier to grab + float min = (draw_point_width / 2 * 1.7); //make it easier to grab if (distance <= min && distance < min_distance) { result = i; min_distance = distance; @@ -129,7 +120,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { grabbed = _get_point_from_pos(x); if (grabbed != -1) { - int total_w = get_size().width - get_size().height - SPACING; + int total_w = get_size().width - get_size().height - draw_spacing; Gradient::Point newPoint = points[grabbed]; newPoint.offset = CLAMP(x / float(total_w), 0, 1); @@ -151,10 +142,10 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) { update(); int x = mb->get_position().x; - int total_w = get_size().width - get_size().height - SPACING; + int total_w = get_size().width - get_size().height - draw_spacing; //Check if color selector was clicked. - if (x > total_w + SPACING) { + if (x > total_w + draw_spacing) { _show_color_picker(); return; } @@ -225,7 +216,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid() && grabbing) { - int total_w = get_size().width - get_size().height - SPACING; + int total_w = get_size().width - get_size().height - draw_spacing; int x = mm->get_position().x; @@ -297,6 +288,12 @@ void GradientEdit::_notification(int p_what) { picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed)); } } + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + draw_spacing = BASE_SPACING * get_theme_default_base_scale(); + draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale(); + } + if (p_what == NOTIFICATION_DRAW) { int w = get_size().x; int h = get_size().y; @@ -305,7 +302,7 @@ void GradientEdit::_notification(int p_what) { return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size } - int total_w = get_size().width - get_size().height - SPACING; + int total_w = get_size().width - get_size().height - draw_spacing; //Draw checker pattern for ramp draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true); @@ -358,7 +355,7 @@ void GradientEdit::_notification(int p_what) { col.a = 0.9; draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col); - Rect2 rect = Rect2(points[i].offset * total_w - POINT_WIDTH / 2, h / 2, POINT_WIDTH, h / 2); + Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2); draw_rect(rect, points[i].color, true); draw_rect(rect, col, false); if (grabbed == i) { @@ -375,15 +372,15 @@ void GradientEdit::_notification(int p_what) { } //Draw "button" for color selector - draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + SPACING, 0, h, h), true); + draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true); if (grabbed != -1) { //Draw with selection color - draw_rect(Rect2(total_w + SPACING, 0, h, h), points[grabbed].color); + draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color); } else { //if no color selected draw grey color with 'X' on top. - draw_rect(Rect2(total_w + SPACING, 0, h, h), Color(0.5, 0.5, 0.5, 1)); - draw_line(Vector2(total_w + SPACING, 0), Vector2(total_w + SPACING + h, h), Color(1, 1, 1, 0.6)); - draw_line(Vector2(total_w + SPACING, h), Vector2(total_w + SPACING + h, 0), Color(1, 1, 1, 0.6)); + draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1)); + draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6)); + draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6)); } //Draw borders around color ramp if in focus diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h index a173631963..f3a39daaf6 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -46,6 +46,13 @@ class GradientEdit : public Control { int grabbed = -1; Vector<Gradient::Point> points; + // Make sure to use the scaled value below. + const int BASE_SPACING = 3; + const int BASE_POINT_WIDTH = 8; + + int draw_spacing = BASE_SPACING; + int draw_point_width = BASE_POINT_WIDTH; + void _draw_checker(int x, int y, int w, int h); void _color_changed(const Color &p_color); int _get_point_from_pos(int x); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index d9c08ec272..35e31be9af 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -36,10 +36,6 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" -#ifdef TOOLS_ENABLED -#include "editor/editor_scale.h" -#endif - constexpr int MINIMAP_OFFSET = 12; constexpr int MINIMAP_PADDING = 5; @@ -436,6 +432,8 @@ void GraphEdit::_notification(int p_what) { snap_button->set_icon(get_theme_icon(SNAME("snap"))); minimap_button->set_icon(get_theme_icon(SNAME("minimap"))); layout_button->set_icon(get_theme_icon(SNAME("layout"))); + + zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale()); } if (p_what == NOTIFICATION_READY) { Size2 hmin = h_scroll->get_combined_minimum_size(); @@ -816,11 +814,7 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from scaled_points.push_back(points[i] * p_zoom); } -#ifdef TOOLS_ENABLED - p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * EDSCALE), lines_antialiased); -#else - p_where->draw_polyline_colors(scaled_points, colors, p_width, lines_antialiased); -#endif + p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * get_theme_default_base_scale()), lines_antialiased); } void GraphEdit::_connections_layer_draw() { @@ -2272,11 +2266,7 @@ GraphEdit::GraphEdit() { zoom_label->set_visible(false); zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER); zoom_label->set_align(Label::ALIGN_CENTER); -#ifdef TOOLS_ENABLED - zoom_label->set_custom_minimum_size(Size2(48, 0) * EDSCALE); -#else zoom_label->set_custom_minimum_size(Size2(48, 0)); -#endif _update_zoom_label(); zoom_minus = memnew(Button); diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index ecf735d7f5..8462fd259e 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -403,28 +403,28 @@ void GraphNode::_notification(int p_what) { close_rect = Rect2(); } - for (Map<int, Slot>::Element *E = slot_info.front(); E; E = E->next()) { - if (E->key() < 0 || E->key() >= cache_y.size()) { + for (const KeyValue<int, Slot> &E : slot_info) { + if (E.key < 0 || E.key >= cache_y.size()) { continue; } - if (!slot_info.has(E->key())) { + if (!slot_info.has(E.key)) { continue; } - const Slot &s = slot_info[E->key()]; + const Slot &s = slot_info[E.key]; //left if (s.enable_left) { Ref<Texture2D> p = port; if (s.custom_slot_left.is_valid()) { p = s.custom_slot_left; } - p->draw(get_canvas_item(), icofs + Point2(edgeofs, cache_y[E->key()]), s.color_left); + p->draw(get_canvas_item(), icofs + Point2(edgeofs, cache_y[E.key]), s.color_left); } if (s.enable_right) { Ref<Texture2D> p = port; if (s.custom_slot_right.is_valid()) { p = s.custom_slot_right; } - p->draw(get_canvas_item(), icofs + Point2(get_size().x - edgeofs, cache_y[E->key()]), s.color_right); + p->draw(get_canvas_item(), icofs + Point2(get_size().x - edgeofs, cache_y[E.key]), s.color_right); } } diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index 1107e3a4af..2beb2624d2 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -82,15 +82,15 @@ void GridContainer::_notification(int p_what) { // Evaluate the remaining space for expanded columns/rows. Size2 remaining_space = get_size(); - for (Map<int, int>::Element *E = col_minw.front(); E; E = E->next()) { - if (!col_expanded.has(E->key())) { - remaining_space.width -= E->get(); + for (const KeyValue<int, int> &E : col_minw) { + if (!col_expanded.has(E.key)) { + remaining_space.width -= E.value; } } - for (Map<int, int>::Element *E = row_minh.front(); E; E = E->next()) { - if (!row_expanded.has(E->key())) { - remaining_space.height -= E->get(); + for (const KeyValue<int, int> &E : row_minh) { + if (!row_expanded.has(E.key)) { + remaining_space.height -= E.value; } } remaining_space.height -= vsep * MAX(max_row - 1, 0); @@ -247,12 +247,12 @@ Size2 GridContainer::get_minimum_size() const { Size2 ms; - for (Map<int, int>::Element *E = col_minw.front(); E; E = E->next()) { - ms.width += E->get(); + for (const KeyValue<int, int> &E : col_minw) { + ms.width += E.value; } - for (Map<int, int>::Element *E = row_minh.front(); E; E = E->next()) { - ms.height += E->get(); + for (const KeyValue<int, int> &E : row_minh) { + ms.height += E.value; } ms.height += vsep * max_row; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 18cde25e55..b8cb618171 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -108,7 +108,7 @@ void Label::_shape() { } lines_rid.clear(); - uint8_t autowrap_flags = TextServer::BREAK_MANDATORY; + uint16_t autowrap_flags = TextServer::BREAK_MANDATORY; switch (autowrap_mode) { case AUTOWRAP_WORD_SMART: autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY; @@ -122,10 +122,10 @@ void Label::_shape() { case AUTOWRAP_OFF: break; } - Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); - for (int i = 0; i < line_breaks.size(); i++) { - RID line = TS->shaped_text_substr(text_rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); lines_rid.push_back(line); } } @@ -145,7 +145,7 @@ void Label::_shape() { } if (lines_dirty) { - uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; + uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; switch (overrun_behavior) { case OVERRUN_TRIM_WORD_ELLIPSIS: overrun_flags |= TextServer::OVERRUN_TRIM; @@ -231,7 +231,7 @@ void Label::_update_visible() { } } -inline void draw_glyph(const TextServer::Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) { +inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) { if (p_gl.font_rid != RID()) { TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color); } else { @@ -239,7 +239,7 @@ inline void draw_glyph(const TextServer::Glyph &p_gl, const RID &p_canvas, const } } -inline void draw_glyph_outline(const TextServer::Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Color &p_font_shadow_color, const Color &p_font_outline_color, const int &p_shadow_outline_size, const int &p_outline_size, const Vector2 &p_ofs, const Vector2 &shadow_ofs) { +inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Color &p_font_shadow_color, const Color &p_font_outline_color, const int &p_shadow_outline_size, const int &p_outline_size, const Vector2 &p_ofs, const Vector2 &shadow_ofs) { if (p_gl.font_rid != RID()) { if (p_font_shadow_color.a > 0) { TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color); @@ -387,21 +387,25 @@ void Label::_notification(int p_what) { } break; } - const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]); - const TextServer::Glyph *glyphs = visual.ptr(); - int gl_size = visual.size(); - TextServer::TrimData trim_data = TS->shaped_text_get_trim_data(lines_rid[i]); + const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); + int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + + int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]); + int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]); + + const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]); + int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps. if (font_shadow_color.a > 0 || (font_outline_color.a != 0.0 && outline_size > 0)) { Vector2 offset = ofs; // Draw RTL ellipsis string when necessary. - if (rtl && trim_data.ellipsis_pos >= 0) { - for (int gl_idx = trim_data.ellipsis_glyph_buf.size() - 1; gl_idx >= 0; gl_idx--) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) { + if (rtl && ellipsis_pos >= 0) { + for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { //Draw glyph outlines and shadow. - draw_glyph_outline(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); - offset.x += trim_data.ellipsis_glyph_buf[gl_idx].advance; + draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + offset.x += ellipsis_glyphs[gl_idx].advance; } } } @@ -410,13 +414,13 @@ void Label::_notification(int p_what) { for (int j = 0; j < gl_size; j++) { for (int k = 0; k < glyphs[j].repeat; k++) { // Trim when necessary. - if (trim_data.trim_pos >= 0) { + if (trim_pos >= 0) { if (rtl) { - if (j < trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { continue; } } else { - if (j >= trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { break; } } @@ -428,12 +432,12 @@ void Label::_notification(int p_what) { } } // Draw LTR ellipsis string when necessary. - if (!rtl && trim_data.ellipsis_pos >= 0) { - for (int gl_idx = 0; gl_idx < trim_data.ellipsis_glyph_buf.size(); gl_idx++) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) { + if (!rtl && ellipsis_pos >= 0) { + for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { //Draw glyph outlines and shadow. - draw_glyph_outline(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); - offset.x += trim_data.ellipsis_glyph_buf[gl_idx].advance; + draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + offset.x += ellipsis_glyphs[gl_idx].advance; } } } @@ -442,12 +446,12 @@ void Label::_notification(int p_what) { // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps. // Draw RTL ellipsis string when necessary. - if (rtl && trim_data.ellipsis_pos >= 0) { - for (int gl_idx = trim_data.ellipsis_glyph_buf.size() - 1; gl_idx >= 0; gl_idx--) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) { + if (rtl && ellipsis_pos >= 0) { + for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { //Draw glyph outlines and shadow. - draw_glyph(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, ofs); - ofs.x += trim_data.ellipsis_glyph_buf[gl_idx].advance; + draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); + ofs.x += ellipsis_glyphs[gl_idx].advance; } } } @@ -456,13 +460,13 @@ void Label::_notification(int p_what) { for (int j = 0; j < gl_size; j++) { for (int k = 0; k < glyphs[j].repeat; k++) { // Trim when necessary. - if (trim_data.trim_pos >= 0) { + if (trim_pos >= 0) { if (rtl) { - if (j < trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { continue; } } else { - if (j >= trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { break; } } @@ -474,12 +478,12 @@ void Label::_notification(int p_what) { } } // Draw LTR ellipsis string when necessary. - if (!rtl && trim_data.ellipsis_pos >= 0) { - for (int gl_idx = 0; gl_idx < trim_data.ellipsis_glyph_buf.size(); gl_idx++) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) { + if (!rtl && ellipsis_pos >= 0) { + for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { //Draw glyph outlines and shadow. - draw_glyph(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, ofs); - ofs.x += trim_data.ellipsis_glyph_buf[gl_idx].advance; + draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); + ofs.x += ellipsis_glyphs[gl_idx].advance; } } } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 89fd9bfc88..653885aa08 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -40,7 +40,6 @@ #include "servers/display_server.h" #include "servers/text_server.h" #ifdef TOOLS_ENABLED -#include "editor/editor_scale.h" #include "editor/editor_settings.h" #endif #include "scene/main/window.h" @@ -67,10 +66,10 @@ void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) { if (p_move_by_word) { int cc = caret_column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < cc) { - cc = words[i].x; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = words.size() - 2; i >= 0; i = i - 2) { + if (words[i] < cc) { + cc = words[i]; break; } } @@ -99,10 +98,10 @@ void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) { if (p_move_by_word) { int cc = caret_column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > cc) { - cc = words[i].y; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = 1; i < words.size(); i = i + 2) { + if (words[i] > cc) { + cc = words[i]; break; } } @@ -151,10 +150,10 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) { if (p_word) { int cc = caret_column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < cc) { - cc = words[i].x; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = words.size() - 2; i >= 0; i = i - 2) { + if (words[i] < cc) { + cc = words[i]; break; } } @@ -194,10 +193,10 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { if (p_word) { int cc = caret_column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > cc) { - cc = words[i].y; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = 1; i < words.size(); i = i + 2) { + if (words[i] > cc) { + cc = words[i]; break; } } @@ -276,12 +275,12 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { // Double-click select word. last_dblclk = OS::get_singleton()->get_ticks_msec(); last_dblclk_pos = b->get_position(); - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = 0; i < words.size(); i++) { - if ((words[i].x < caret_column && words[i].y > caret_column) || (i == words.size() - 1 && caret_column == words[i].y)) { + PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = 0; i < words.size(); i = i + 2) { + if ((words[i] < caret_column && words[i + 1] > caret_column) || (i == words.size() - 2 && caret_column == words[i + 1])) { selection.enabled = true; - selection.begin = words[i].x; - selection.end = words[i].y; + selection.begin = words[i]; + selection.end = words[i + 1]; selection.double_click = true; caret_column = selection.end; break; @@ -713,11 +712,7 @@ void LineEdit::_notification(int p_what) { ofs_max -= r_icon->get_width(); } -#ifdef TOOLS_ENABLED - int caret_width = Math::round(EDSCALE); -#else - int caret_width = 1; -#endif + int caret_width = Math::round(1 * get_theme_default_base_scale()); // Draw selections rects. Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs); @@ -737,9 +732,8 @@ void LineEdit::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_color); } } - const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(text_rid); - const TextServer::Glyph *glyphs = visual.ptr(); - int gl_size = visual.size(); + const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid); + int gl_size = TS->shaped_text_get_glyph_count(text_rid); // Draw text. ofs.y += TS->shaped_text_get_ascent(text_rid); @@ -783,38 +777,36 @@ void LineEdit::_notification(int p_what) { if (draw_caret) { if (ime_text.length() == 0) { // Normal caret. - Rect2 l_caret, t_caret; - TextServer::Direction l_dir, t_dir; - TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir); + CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column); - if (l_caret == Rect2() && t_caret == Rect2()) { + if (caret.l_caret == Rect2() && caret.t_caret == Rect2()) { // No carets, add one at the start. int h = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size"))); int y = style->get_offset().y + (y_area - h) / 2; if (rtl) { - l_dir = TextServer::DIRECTION_RTL; - l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h)); + caret.l_dir = TextServer::DIRECTION_RTL; + caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h)); } else { - l_dir = TextServer::DIRECTION_LTR; - l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h)); + caret.l_dir = TextServer::DIRECTION_LTR; + caret.l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h)); } - RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color); } else { - if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) { + if (caret.l_caret != Rect2() && caret.l_dir == TextServer::DIRECTION_AUTO) { // Draw extra marker on top of mid caret. - Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width); + Rect2 trect = Rect2(caret.l_caret.position.x - 3 * caret_width, caret.l_caret.position.y, 6 * caret_width, caret_width); trect.position += ofs; RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color); } - l_caret.position += ofs; - l_caret.size.x = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color); + caret.l_caret.position += ofs; + caret.l_caret.size.x = caret_width; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color); - t_caret.position += ofs; - t_caret.size.x = caret_width; + caret.t_caret.position += ofs; + caret.t_caret.size.x = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, caret_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.t_caret, caret_color); } } else { { @@ -1114,32 +1106,31 @@ Vector2i LineEdit::get_caret_pixel_pos() { } Vector2i ret; - Rect2 l_caret, t_caret; - TextServer::Direction l_dir, t_dir; + CaretInfo caret; // Get position of the start of caret. if (ime_text.length() != 0 && ime_selection.x != 0) { - TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x, l_caret, l_dir, t_caret, t_dir); + caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x); } else { - TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir); + caret = TS->shaped_text_get_carets(text_rid, caret_column); } - if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - ret.x = x_ofs + l_caret.position.x + scroll_offset; + if ((caret.l_caret != Rect2() && (caret.l_dir == TextServer::DIRECTION_AUTO || caret.l_dir == (TextServer::Direction)input_direction)) || (caret.t_caret == Rect2())) { + ret.x = x_ofs + caret.l_caret.position.x + scroll_offset; } else { - ret.x = x_ofs + t_caret.position.x + scroll_offset; + ret.x = x_ofs + caret.t_caret.position.x + scroll_offset; } // Get position of the end of caret. if (ime_text.length() != 0) { if (ime_selection.y != 0) { - TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir); + caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y); } else { - TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size(), l_caret, l_dir, t_caret, t_dir); + caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size()); } - if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - ret.y = x_ofs + l_caret.position.x + scroll_offset; + if ((caret.l_caret != Rect2() && (caret.l_dir == TextServer::DIRECTION_AUTO || caret.l_dir == (TextServer::Direction)input_direction)) || (caret.t_caret == Rect2())) { + ret.y = x_ofs + caret.l_caret.position.x + scroll_offset; } else { - ret.y = x_ofs + t_caret.position.x + scroll_offset; + ret.y = x_ofs + caret.t_caret.position.x + scroll_offset; } } else { ret.y = ret.x; @@ -1502,7 +1493,7 @@ void LineEdit::insert_text_at_caret(String p_text) { String post = text.substr(caret_column, text.length() - caret_column); text = pre + p_text + post; _shape(); - TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length()); + TextServer::Direction dir = TS->shaped_text_get_dominant_direction_in_range(text_rid, caret_column, caret_column + p_text.length()); if (dir != TextServer::DIRECTION_AUTO) { input_direction = (TextDirection)dir; } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 277026cc28..4588966d88 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -41,10 +41,6 @@ #include "modules/regex/regex.h" #endif -#ifdef TOOLS_ENABLED -#include "editor/editor_scale.h" -#endif - RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) const { if (p_free) { if (p_item->subitems.size()) { @@ -819,9 +815,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } - const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid); - const TextServer::Glyph *glyphs = visual.ptr(); - int gl_size = visual.size(); + const Glyph *glyphs = TS->shaped_text_get_glyphs(rid); + int gl_size = TS->shaped_text_get_glyph_count(rid); Vector2 gloff = off; // Draw oulines and shadow. @@ -996,19 +991,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Color uc = font_color; uc.a *= 0.5; float y_off = TS->shaped_text_get_underline_position(rid); - float underline_width = TS->shaped_text_get_underline_thickness(rid); -#ifdef TOOLS_ENABLED - underline_width *= EDSCALE; -#endif + float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width); } else if (_find_strikethrough(it)) { Color uc = font_color; uc.a *= 0.5; float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2; - float underline_width = TS->shaped_text_get_underline_thickness(rid); -#ifdef TOOLS_ENABLED - underline_width *= EDSCALE; -#endif + float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale(); draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width); } @@ -1593,18 +1582,18 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { if (c_frame) { const Line &l = c_frame->lines[c_line]; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (c_index >= words[i].x && c_index < words[i].y) { + PackedInt32Array words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid()); + for (int i = 0; i < words.size(); i = i + 2) { + if (c_index >= words[i] && c_index < words[i + 1]) { selection.from_frame = c_frame; selection.from_line = c_line; selection.from_item = c_item; - selection.from_char = words[i].x; + selection.from_char = words[i]; selection.to_frame = c_frame; selection.to_line = c_line; selection.to_item = c_item; - selection.to_char = words[i].y; + selection.to_char = words[i + 1]; selection.active = true; update(); diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index ef34bec347..f305bf7013 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -156,6 +156,7 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) { if (scrolling_enabled && buttons_visible) { if (missing_right) { offset++; + _ensure_no_over_offset(); // Avoid overreaching when scrolling fast. update(); } } @@ -164,7 +165,7 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) { if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (rb_hover != -1) { //pressed - emit_signal(SNAME("right_button_pressed"), rb_hover); + emit_signal(SNAME("tab_rmb_clicked"), rb_hover); } rb_pressing = false; @@ -401,7 +402,7 @@ void Tabs::_notification(int p_what) { w += tabs[i].size_text; if (tabs[i].right_button.is_valid()) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("button")); + Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight")); Ref<Texture2D> rb = tabs[i].right_button; w += get_theme_constant(SNAME("hseparation")); @@ -433,7 +434,7 @@ void Tabs::_notification(int p_what) { } if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("button")); + Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight")); Ref<Texture2D> cb = close; w += get_theme_constant(SNAME("hseparation")); @@ -449,7 +450,7 @@ void Tabs::_notification(int p_what) { if (!tabs[i].disabled && cb_hover == i) { if (cb_pressing) { - get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect); + get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect); } else { style->draw(ci, cb_rect); } @@ -886,7 +887,7 @@ void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) { hover_now = get_tab_count() - 1; } move_tab(tab_from_id, hover_now); - emit_signal(SNAME("reposition_active_tab_request"), hover_now); + emit_signal(SNAME("active_tab_rearranged"), hover_now); set_current_tab(hover_now); } else if (get_tabs_rearrange_group() != -1) { // drag and drop between Tabs @@ -1165,10 +1166,10 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &Tabs::get_select_with_rmb); ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("reposition_active_tab_request", PropertyInfo(Variant::INT, "idx_to"))); + ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to"))); ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab"))); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 5b13e1da0b..57bcbb7c2d 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -42,10 +42,6 @@ #include "scene/main/window.h" -#ifdef TOOLS_ENABLED -#include "editor/editor_scale.h" -#endif - static bool _is_text_char(char32_t c) { return !is_symbol(c); } @@ -186,7 +182,7 @@ void TextEdit::Text::_calculate_max_line_width() { max_width = width; } -void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Vector<Vector2i> &p_bidi_override) { +void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Array &p_bidi_override) { ERR_FAIL_INDEX(p_line, text.size()); if (font.is_null() || font_size <= 0) { @@ -278,14 +274,14 @@ void TextEdit::Text::invalidate_all() { void TextEdit::Text::clear() { text.clear(); - insert(0, "", Vector<Vector2i>()); + insert(0, "", Array()); } int TextEdit::Text::get_max_width() const { return max_width; } -void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override) { +void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_override) { ERR_FAIL_INDEX(p_line, text.size()); text.write[p_line].data = p_text; @@ -293,7 +289,7 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i invalidate_cache(p_line); } -void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override) { +void TextEdit::Text::insert(int p_at, const String &p_text, const Array &p_bidi_override) { Line line; line.gutters.resize(gutter_count); line.hidden = false; @@ -1076,9 +1072,8 @@ void TextEdit::_notification(int p_what) { ofs_y += (row_height - text_height) / 2; - const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid); - const TextServer::Glyph *glyphs = visual.ptr(); - int gl_size = visual.size(); + const Glyph *glyphs = TS->shaped_text_get_glyphs(rid); + int gl_size = TS->shaped_text_get_glyph_count(rid); ofs_y += ldata->get_line_ascent(line_wrap_index); int char_ofs = 0; @@ -1174,38 +1169,33 @@ void TextEdit::_notification(int p_what) { } } - // Carets -#ifdef TOOLS_ENABLED - int caret_width = Math::round(EDSCALE); -#else - int caret_width = 1; -#endif + // Carets. + int caret_width = Math::round(1 * get_theme_default_base_scale()); if (!clipped && caret.line == line && line_wrap_index == caret_wrap_index) { caret.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index); if (ime_text.length() == 0) { - Rect2 l_caret, t_caret; - TextServer::Direction l_dir, t_dir; + CaretInfo ts_caret; if (str.length() != 0) { // Get carets. - TS->shaped_text_get_carets(rid, caret.column, l_caret, l_dir, t_caret, t_dir); + ts_caret = TS->shaped_text_get_carets(rid, caret.column); } else { // No carets, add one at the start. int h = font->get_height(font_size); if (rtl) { - l_dir = TextServer::DIRECTION_RTL; - l_caret = Rect2(Vector2(xmargin_end - char_margin + ofs_x, -h / 2), Size2(caret_width * 4, h)); + ts_caret.l_dir = TextServer::DIRECTION_RTL; + ts_caret.l_caret = Rect2(Vector2(xmargin_end - char_margin + ofs_x, -h / 2), Size2(caret_width * 4, h)); } else { - l_dir = TextServer::DIRECTION_LTR; - l_caret = Rect2(Vector2(char_ofs, -h / 2), Size2(caret_width * 4, h)); + ts_caret.l_dir = TextServer::DIRECTION_LTR; + ts_caret.l_caret = Rect2(Vector2(char_ofs, -h / 2), Size2(caret_width * 4, h)); } } - if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - caret.draw_pos.x = char_margin + ofs_x + l_caret.position.x; + if ((ts_caret.l_caret != Rect2() && (ts_caret.l_dir == TextServer::DIRECTION_AUTO || ts_caret.l_dir == (TextServer::Direction)input_direction)) || (ts_caret.t_caret == Rect2())) { + caret.draw_pos.x = char_margin + ofs_x + ts_caret.l_caret.position.x; } else { - caret.draw_pos.x = char_margin + ofs_x + t_caret.position.x; + caret.draw_pos.x = char_margin + ofs_x + ts_caret.t_caret.position.x; } if (caret.draw_pos.x >= xmargin_beg && caret.draw_pos.x < xmargin_end) { @@ -1215,64 +1205,64 @@ void TextEdit::_notification(int p_what) { //Block or underline caret, draw trailing carets at full height. int h = font->get_height(font_size); - if (t_caret != Rect2()) { + if (ts_caret.t_caret != Rect2()) { if (overtype_mode) { - t_caret.position.y = TS->shaped_text_get_descent(rid); - t_caret.size.y = caret_width; + ts_caret.t_caret.position.y = TS->shaped_text_get_descent(rid); + ts_caret.t_caret.size.y = caret_width; } else { - t_caret.position.y = -TS->shaped_text_get_ascent(rid); - t_caret.size.y = h; + ts_caret.t_caret.position.y = -TS->shaped_text_get_ascent(rid); + ts_caret.t_caret.size.y = h; } - t_caret.position += Vector2(char_margin + ofs_x, ofs_y); - draw_rect(t_caret, caret_color, overtype_mode); + ts_caret.t_caret.position += Vector2(char_margin + ofs_x, ofs_y); + draw_rect(ts_caret.t_caret, caret_color, overtype_mode); - if (l_caret != Rect2() && l_dir != t_dir) { - l_caret.position += Vector2(char_margin + ofs_x, ofs_y); - l_caret.size.x = caret_width; - draw_rect(l_caret, caret_color * Color(1, 1, 1, 0.5)); + if (ts_caret.l_caret != Rect2() && ts_caret.l_dir != ts_caret.t_dir) { + ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y); + ts_caret.l_caret.size.x = caret_width; + draw_rect(ts_caret.l_caret, caret_color * Color(1, 1, 1, 0.5)); } } else { // End of the line. if (gl_size > 0) { // Adjust for actual line dimensions. if (overtype_mode) { - l_caret.position.y = TS->shaped_text_get_descent(rid); - l_caret.size.y = caret_width; + ts_caret.l_caret.position.y = TS->shaped_text_get_descent(rid); + ts_caret.l_caret.size.y = caret_width; } else { - l_caret.position.y = -TS->shaped_text_get_ascent(rid); - l_caret.size.y = h; + ts_caret.l_caret.position.y = -TS->shaped_text_get_ascent(rid); + ts_caret.l_caret.size.y = h; } } else if (overtype_mode) { - l_caret.position.y += l_caret.size.y; - l_caret.size.y = caret_width; + ts_caret.l_caret.position.y += ts_caret.l_caret.size.y; + ts_caret.l_caret.size.y = caret_width; } - if (l_caret.position.x >= TS->shaped_text_get_size(rid).x) { - l_caret.size.x = font->get_char_size('m', 0, font_size).x; + if (ts_caret.l_caret.position.x >= TS->shaped_text_get_size(rid).x) { + ts_caret.l_caret.size.x = font->get_char_size('m', 0, font_size).x; } else { - l_caret.size.x = 3 * caret_width; + ts_caret.l_caret.size.x = 3 * caret_width; } - l_caret.position += Vector2(char_margin + ofs_x, ofs_y); - if (l_dir == TextServer::DIRECTION_RTL) { - l_caret.position.x -= l_caret.size.x; + ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y); + if (ts_caret.l_dir == TextServer::DIRECTION_RTL) { + ts_caret.l_caret.position.x -= ts_caret.l_caret.size.x; } - draw_rect(l_caret, caret_color, overtype_mode); + draw_rect(ts_caret.l_caret, caret_color, overtype_mode); } } else { // Normal caret. - if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) { + if (ts_caret.l_caret != Rect2() && ts_caret.l_dir == TextServer::DIRECTION_AUTO) { // Draw extra marker on top of mid caret. - Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width); + Rect2 trect = Rect2(ts_caret.l_caret.position.x - 3 * caret_width, ts_caret.l_caret.position.y, 6 * caret_width, caret_width); trect.position += Vector2(char_margin + ofs_x, ofs_y); RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color); } - l_caret.position += Vector2(char_margin + ofs_x, ofs_y); - l_caret.size.x = caret_width; + ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y); + ts_caret.l_caret.size.x = caret_width; - draw_rect(l_caret, caret_color); + draw_rect(ts_caret.l_caret, caret_color); - t_caret.position += Vector2(char_margin + ofs_x, ofs_y); - t_caret.size.x = caret_width; + ts_caret.t_caret.position += Vector2(char_margin + ofs_x, ofs_y); + ts_caret.t_caret.size.x = caret_width; - draw_rect(t_caret, caret_color); + draw_rect(ts_caret.t_caret, caret_color); } } } @@ -1636,6 +1626,32 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } } } + + // Check if user is hovering a different gutter, and update if yes. + Vector2i current_hovered_gutter = Vector2i(-1, -1); + + int left_margin = style_normal->get_margin(SIDE_LEFT); + if (mpos.x <= left_margin + gutters_width + gutter_padding) { + int hovered_row = get_line_column_at_pos(mpos).y; + for (int i = 0; i < gutters.size(); i++) { + if (!gutters[i].draw || gutters[i].width <= 0) { + continue; + } + + if (mpos.x > left_margin && mpos.x <= (left_margin + gutters[i].width) - 3) { + // We are in this gutter i's horizontal area. + current_hovered_gutter = Vector2i(i, hovered_row); + break; + } + + left_margin += gutters[i].width; + } + } + + if (current_hovered_gutter != hovered_gutter) { + hovered_gutter = current_hovered_gutter; + update(); + } } if (draw_minimap && !dragging_selection) { @@ -1942,10 +1958,10 @@ void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) { set_caret_line(caret.line - 1); set_caret_column(text[caret.line].length()); } else { - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < cc) { - cc = words[i].x; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = words.size() - 2; i >= 0; i = i - 2) { + if (words[i] < cc) { + cc = words[i]; break; } } @@ -1993,10 +2009,10 @@ void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) { set_caret_line(caret.line + 1); set_caret_column(0); } else { - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > cc) { - cc = words[i].y; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = 1; i < words.size(); i = i + 2) { + if (words[i] > cc) { + cc = words[i]; break; } } @@ -2188,10 +2204,10 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { int line = caret.line; int column = caret.column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < column) { - column = words[i].x; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = words.size() - 2; i >= 0; i = i - 2) { + if (words[i] < column) { + column = words[i]; break; } } @@ -2231,10 +2247,10 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) { int line = caret.line; int column = caret.column; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > column) { - column = words[i].y; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = 1; i < words.size(); i = i + 2) { + if (words[i] > column) { + column = words[i]; break; } } @@ -2731,7 +2747,10 @@ void TextEdit::insert_line_at(int p_at, const String &p_text) { } void TextEdit::insert_text_at_caret(const String &p_text) { - begin_complex_operation(); + bool had_selection = has_selection(); + if (had_selection) { + begin_complex_operation(); + } delete_selection(); @@ -2743,7 +2762,9 @@ void TextEdit::insert_text_at_caret(const String &p_text) { set_caret_column(new_column); update(); - end_complex_operation(); + if (had_selection) { + end_complex_operation(); + } } void TextEdit::remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { @@ -3600,10 +3621,10 @@ int TextEdit::get_caret_wrap_index() const { } String TextEdit::get_word_under_caret() const { - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].x <= caret.column && words[i].y > caret.column) { - return text[caret.line].substr(words[i].x, words[i].y - words[i].x); + PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = 0; i < words.size(); i = i + 2) { + if (words[i] <= caret.column && words[i + 1] > caret.column) { + return text[caret.line].substr(words[i], words[i + 1] - words[i]); } } return ""; @@ -3687,11 +3708,11 @@ void TextEdit::select_word_under_caret() { int begin = 0; int end = 0; - const Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if ((words[i].x < caret.column && words[i].y > caret.column) || (i == words.size() - 1 && caret.column == words[i].y)) { - begin = words[i].x; - end = words[i].y; + const PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = 0; i < words.size(); i = i + 2) { + if ((words[i] < caret.column && words[i + 1] > caret.column) || (i == words.size() - 2 && caret.column == words[i + 1])) { + begin = words[i]; + end = words[i + 1]; break; } } @@ -5051,10 +5072,12 @@ void TextEdit::_cut_internal() { } int cl = get_caret_line(); + int cc = get_caret_column(); + int indent_level = get_indent_level(cl); + double hscroll = get_h_scroll(); String clipboard = text[cl]; DisplayServer::get_singleton()->clipboard_set(clipboard); - set_caret_line(cl); set_caret_column(0); if (cl == 0 && get_line_count() > 1) { @@ -5065,6 +5088,17 @@ void TextEdit::_cut_internal() { set_caret_line(get_caret_line() + 1); } + // Correct the visualy perceived caret column taking care of identation level of the lines. + int diff_indent = indent_level - get_indent_level(get_caret_line()); + cc += diff_indent; + if (diff_indent != 0) { + cc += diff_indent > 0 ? -1 : 1; + } + + // Restore horizontal scroll and caret column modified by the backspace() call. + set_h_scroll(hscroll); + set_caret_column(cc); + cut_copy_line = clipboard; } @@ -5345,14 +5379,12 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line) const { } } - Rect2 l_caret, t_caret; - TextServer::Direction l_dir, t_dir; RID text_rid = text.get_line_data(p_line)->get_line_rid(row); - TS->shaped_text_get_carets(text_rid, caret.column, l_caret, l_dir, t_caret, t_dir); - if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - return l_caret.position.x; + CaretInfo ts_caret = TS->shaped_text_get_carets(text_rid, caret.column); + if ((ts_caret.l_caret != Rect2() && (ts_caret.l_dir == TextServer::DIRECTION_AUTO || ts_caret.l_dir == (TextServer::Direction)input_direction)) || (ts_caret.t_caret == Rect2())) { + return ts_caret.l_caret.position.x; } else { - return t_caret.position.x; + return ts_caret.t_caret.position.x; } } @@ -5409,11 +5441,11 @@ void TextEdit::_update_selection_mode_word() { int caret_pos = CLAMP(col, 0, text[line].length()); int beg = caret_pos; int end = beg; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if ((words[i].x < caret_pos && words[i].y > caret_pos) || (i == words.size() - 1 && caret_pos == words[i].y)) { - beg = words[i].x; - end = words[i].y; + PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = 0; i < words.size(); i = i + 2) { + if ((words[i] < caret_pos && words[i + 1] > caret_pos) || (i == words.size() - 2 && caret_pos == words[i + 1])) { + beg = words[i]; + end = words[i + 1]; break; } } @@ -6001,7 +6033,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i r_end_line = p_line + substrings.size() - 1; r_end_column = text[r_end_line].length() - postinsert_text.length(); - TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? caret.column : 0, r_end_column); + TextServer::Direction dir = TS->shaped_text_get_dominant_direction_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? caret.column : 0, r_end_column); if (dir != TextServer::DIRECTION_AUTO) { input_direction = (TextDirection)dir; } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index b1226f2aff..07e585847a 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -139,7 +139,7 @@ private: Vector<Gutter> gutters; String data; - Vector<Vector2i> bidi_override; + Array bidi_override; Ref<TextParagraph> data_buf; Color background_color = Color(0, 0, 0, 0); @@ -194,7 +194,7 @@ private: Vector<Vector2i> get_line_wrap_ranges(int p_line) const; const Ref<TextParagraph> get_line_data(int p_line) const; - void set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override); + void set(int p_line, const String &p_text, const Array &p_bidi_override); void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; if (!p_hidden && text[p_line].width > max_width) { @@ -204,12 +204,12 @@ private: } } bool is_hidden(int p_line) const { return text[p_line].hidden; } - void insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override); + void insert(int p_at, const String &p_text, const Array &p_bidi_override); void remove(int p_at); int size() const { return text.size(); } void clear(); - void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Vector<Vector2i> &p_bidi_override = Vector<Vector2i>()); + void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Array &p_bidi_override = Array()); void invalidate_all(); void invalidate_all_lines(); @@ -477,6 +477,7 @@ private: Vector<GutterInfo> gutters; int gutters_width = 0; int gutter_padding = 0; + Vector2i hovered_gutter = Vector2i(-1, -1); // X = gutter index, Y = row. void _update_gutter_width(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 7d7596635c..3f041bf65a 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -41,10 +41,6 @@ #include "box_container.h" -#ifdef TOOLS_ENABLED -#include "editor/editor_scale.h" -#endif - #include <limits.h> Size2 TreeItem::Cell::get_icon_size() const { @@ -1377,6 +1373,8 @@ void Tree::update_cache() { cache.title_button_hover = get_theme_stylebox(SNAME("title_button_hover")); cache.title_button_color = get_theme_color(SNAME("title_button_color")); + cache.base_scale = get_theme_default_base_scale(); + v_scroll->set_custom_step(cache.font->get_height(cache.font_size)); } @@ -2046,15 +2044,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 root_pos -= Point2i(cache.arrow->get_width(), 0); } - float line_width = cache.relationship_line_width; - float parent_line_width = cache.parent_hl_line_width; - float children_line_width = cache.children_hl_line_width; - -#ifdef TOOLS_ENABLED - line_width *= Math::round(EDSCALE); - parent_line_width *= Math::round(EDSCALE); - children_line_width *= Math::round(EDSCALE); -#endif + float line_width = cache.relationship_line_width * Math::round(cache.base_scale); + float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale); + float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale); Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; diff --git a/scene/gui/tree.h b/scene/gui/tree.h index c4a6b6b058..6ca9458e9b 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -516,6 +516,8 @@ private: Color custom_button_font_highlight; Color font_outline_color; + float base_scale = 1.0; + int hseparation = 0; int vseparation = 0; int item_margin = 0; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 64b169b1fb..916833c9a7 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -647,13 +647,13 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid); } -void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { +void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); p_font->draw_string(canvas_item, p_pos, p_text, p_align, p_width, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags); } -void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { +void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_align, p_width, p_max_lines, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 01ed47d4dc..ba9f47119d 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -235,8 +235,8 @@ public: void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1)); void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture); - void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0)); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index f5d2d2f2a2..0876c30dd1 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -41,10 +41,6 @@ #include "scene/scene_string_names.h" #include "viewport.h" -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - #include <stdint.h> VARIANT_ENUM_CAST(Node::ProcessMode); @@ -114,8 +110,8 @@ void Node::_notification(int p_notification) { memdelete(data.path_cache); data.path_cache = nullptr; } - if (data.filename.length()) { - get_multiplayer()->scene_enter_exit_notify(data.filename, this, false); + if (data.scene_file_path.length()) { + get_multiplayer()->scene_enter_exit_notify(data.scene_file_path, this, false); } } break; case NOTIFICATION_PATH_CHANGED: { @@ -146,9 +142,9 @@ void Node::_notification(int p_notification) { GDVIRTUAL_CALL(_ready); - if (data.filename.length()) { + if (data.scene_file_path.length()) { ERR_FAIL_COND(!is_inside_tree()); - get_multiplayer()->scene_enter_exit_notify(data.filename, this, true); + get_multiplayer()->scene_enter_exit_notify(data.scene_file_path, this, true); } } break; @@ -211,8 +207,8 @@ void Node::_propagate_enter_tree() { data.inside_tree = true; - for (Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) { - E->get().group = data.tree->add_to_group(E->key(), this); + for (KeyValue<StringName, GroupData> &E : data.grouped) { + E.value.group = data.tree->add_to_group(E.key, this); } notification(NOTIFICATION_ENTER_TREE); @@ -235,7 +231,7 @@ void Node::_propagate_enter_tree() { data.blocked--; #ifdef DEBUG_ENABLED - SceneDebugger::add_to_cache(data.filename, this); + SceneDebugger::add_to_cache(data.scene_file_path, this); #endif // enter groups } @@ -253,7 +249,7 @@ void Node::_propagate_exit_tree() { //block while removing children #ifdef DEBUG_ENABLED - SceneDebugger::remove_from_cache(data.filename, this); + SceneDebugger::remove_from_cache(data.scene_file_path, this); #endif data.blocked++; @@ -274,9 +270,9 @@ void Node::_propagate_exit_tree() { // exit groups - for (Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) { - data.tree->remove_from_group(E->key(), this); - E->get().group = nullptr; + for (KeyValue<StringName, GroupData> &E : data.grouped) { + data.tree->remove_from_group(E.key, this); + E.value.group = nullptr; } data.viewport = nullptr; @@ -353,9 +349,9 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { for (int i = motion_from; i <= motion_to; i++) { data.children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); } - for (const Map<StringName, GroupData>::Element *E = p_child->data.grouped.front(); E; E = E->next()) { - if (E->get().group) { - E->get().group->changed = true; + for (const KeyValue<StringName, GroupData> &E : p_child->data.grouped) { + if (E.value.group) { + E.value.group->changed = true; } } @@ -1678,10 +1674,10 @@ Array Node::_get_groups() const { } void Node::get_groups(List<GroupInfo> *p_groups) const { - for (const Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) { + for (const KeyValue<StringName, GroupData> &E : data.grouped) { GroupInfo gi; - gi.name = E->key(); - gi.persistent = E->get().persistent; + gi.name = E.key; + gi.persistent = E.value.persistent; p_groups->push_back(gi); } } @@ -1689,8 +1685,8 @@ void Node::get_groups(List<GroupInfo> *p_groups) const { int Node::get_persistent_group_count() const { int count = 0; - for (const Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) { - if (E->get().persistent) { + for (const KeyValue<StringName, GroupData> &E : data.grouped) { + if (E.value.persistent) { count += 1; } } @@ -1846,12 +1842,12 @@ void Node::remove_and_skip() { data.parent->remove_child(this); } -void Node::set_filename(const String &p_filename) { - data.filename = p_filename; +void Node::set_scene_file_path(const String &p_scene_file_path) { + data.scene_file_path = p_scene_file_path; } -String Node::get_filename() const { - return data.filename; +String Node::get_scene_file_path() const { + return data.scene_file_path; } void Node::set_editor_description(const String &p_editor_description) { @@ -1948,8 +1944,8 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const nip->set_instance_path(ip->get_instance_path()); node = nip; - } else if ((p_flags & DUPLICATE_USE_INSTANCING) && get_filename() != String()) { - Ref<PackedScene> res = ResourceLoader::load(get_filename()); + } else if ((p_flags & DUPLICATE_USE_INSTANCING) && get_scene_file_path() != String()) { + Ref<PackedScene> res = ResourceLoader::load(get_scene_file_path()); ERR_FAIL_COND_V(res.is_null(), nullptr); PackedScene::GenEditState ges = PackedScene::GEN_EDIT_STATE_DISABLED; #ifdef TOOLS_ENABLED @@ -1972,8 +1968,8 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const ERR_FAIL_COND_V(!node, nullptr); } - if (get_filename() != "") { //an instance - node->set_filename(get_filename()); + if (get_scene_file_path() != "") { //an instance + node->set_scene_file_path(get_scene_file_path()); node->data.editable_instance = data.editable_instance; } @@ -2004,7 +2000,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const node_tree.push_back(descendant); - if (descendant->get_filename() != "" && instance_roots.has(descendant->get_owner())) { + if (descendant->get_scene_file_path() != "" && instance_roots.has(descendant->get_owner())) { instance_roots.push_back(descendant); } } @@ -2313,7 +2309,7 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) { owned_by_owner[i]->set_owner(owner); } - p_node->set_filename(get_filename()); + p_node->set_scene_file_path(get_scene_file_path()); } void Node::_replace_connections_target(Node *p_new_target) { @@ -2536,17 +2532,11 @@ NodePath Node::get_import_path() const { } static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<String> *r_options) { -#ifdef TOOLS_ENABLED - const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; -#else - const String quote_style = "\""; -#endif - if (p_node != p_base && !p_node->get_owner()) { return; } String n = p_base->get_path_to(p_node); - r_options->push_back(n.quote(quote_style)); + r_options->push_back(n.quote()); for (int i = 0; i < p_node->get_child_count(); i++) { _add_nodes_to_options(p_base, p_node->get_child(i), r_options); } @@ -2693,8 +2683,8 @@ void Node::_bind_methods() { 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); - ClassDB::bind_method(D_METHOD("get_filename"), &Node::get_filename); + ClassDB::bind_method(D_METHOD("set_scene_file_path", "scene_file_path"), &Node::set_scene_file_path); + ClassDB::bind_method(D_METHOD("get_scene_file_path"), &Node::get_scene_file_path); ClassDB::bind_method(D_METHOD("propagate_notification", "what"), &Node::propagate_notification); ClassDB::bind_method(D_METHOD("propagate_call", "method", "args", "parent_first"), &Node::propagate_call, DEFVAL(Array()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_physics_process", "enable"), &Node::set_physics_process); @@ -2839,7 +2829,7 @@ void Node::_bind_methods() { ADD_SIGNAL(MethodInfo("tree_exited")); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_filename", "get_filename"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_owner", "get_owner"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "", "get_multiplayer"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_custom_multiplayer", "get_custom_multiplayer"); diff --git a/scene/main/node.h b/scene/main/node.h index 198501eeac..7d4c79cfba 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -96,7 +96,7 @@ private: }; struct Data { - String filename; + String scene_file_path; Ref<SceneState> instance_state; Ref<SceneState> inherited_state; @@ -353,8 +353,8 @@ public: void print_tree(); void print_tree_pretty(); - void set_filename(const String &p_filename); - String get_filename() const; + void set_scene_file_path(const String &p_scene_file_path); + String get_scene_file_path() const; void set_editor_description(const String &p_editor_description); String get_editor_description() const; diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp index cd9560db61..f4c90ee668 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -57,8 +57,8 @@ Array ResourcePreloader::_get_resources() const { Set<String> sorted_names; - for (Map<StringName, RES>::Element *E = resources.front(); E; E = E->next()) { - sorted_names.insert(E->key()); + for (const KeyValue<StringName, RES> &E : resources) { + sorted_names.insert(E.key); } int i = 0; @@ -131,8 +131,8 @@ Vector<String> ResourcePreloader::_get_resource_list() const { } void ResourcePreloader::get_resource_list(List<StringName> *p_list) { - for (Map<StringName, RES>::Element *E = resources.front(); E; E = E->next()) { - p_list->push_back(E->key()); + for (const KeyValue<StringName, RES> &E : resources) { + p_list->push_back(E.key); } } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 6e78193717..3d07e4473d 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1105,7 +1105,7 @@ Error SceneTree::change_scene_to(const Ref<PackedScene> &p_scene) { Error SceneTree::reload_current_scene() { ERR_FAIL_COND_V(!current_scene, ERR_UNCONFIGURED); - String fname = current_scene->get_filename(); + String fname = current_scene->get_scene_file_path(); return change_scene(fname); } diff --git a/scene/main/window.cpp b/scene/main/window.cpp index ca5a3915d0..a0f62c853f 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1266,6 +1266,18 @@ bool Window::has_theme_constant(const StringName &p_name, const StringName &p_th return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } +float Window::get_theme_default_base_scale() const { + return Control::fetch_theme_default_base_scale(theme_owner, theme_owner_window); +} + +Ref<Font> Window::get_theme_default_font() const { + return Control::fetch_theme_default_font(theme_owner, theme_owner_window); +} + +int Window::get_theme_default_font_size() const { + return Control::fetch_theme_default_font_size(theme_owner, theme_owner_window); +} + Rect2i Window::get_parent_rect() const { ERR_FAIL_COND_V(!is_inside_tree(), Rect2i()); if (is_embedded()) { @@ -1480,6 +1492,10 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Window::get_theme_default_base_scale); + ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Window::get_theme_default_font); + ClassDB::bind_method(D_METHOD("get_theme_default_font_size"), &Window::get_theme_default_font_size); + ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Window::set_layout_direction); ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction); ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl); diff --git a/scene/main/window.h b/scene/main/window.h index 4f31d9cd1f..def6eab7b8 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -280,6 +280,10 @@ public: bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + float get_theme_default_base_scale() const; + Ref<Font> get_theme_default_font() const; + int get_theme_default_font_size() const; + Rect2i get_parent_rect() const; virtual DisplayServer::WindowID get_window_id() const override; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 1e89b17044..bf8f7291be 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -46,7 +46,7 @@ #include "scene/2d/collision_shape_2d.h" #include "scene/2d/cpu_particles_2d.h" #include "scene/2d/gpu_particles_2d.h" -#include "scene/2d/joints_2d.h" +#include "scene/2d/joint_2d.h" #include "scene/2d/light_2d.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/line_2d.h" @@ -215,6 +215,8 @@ #include "scene/3d/decal.h" #include "scene/3d/gpu_particles_3d.h" #include "scene/3d/gpu_particles_collision_3d.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/3d/joint_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" @@ -227,7 +229,6 @@ #include "scene/3d/occluder_instance_3d.h" #include "scene/3d/path_3d.h" #include "scene/3d/physics_body_3d.h" -#include "scene/3d/physics_joint_3d.h" #include "scene/3d/position_3d.h" #include "scene/3d/proximity_group_3d.h" #include "scene/3d/ray_cast_3d.h" @@ -244,6 +245,7 @@ #include "scene/3d/world_environment.h" #include "scene/3d/xr_nodes.h" #include "scene/resources/environment.h" +#include "scene/resources/importer_mesh.h" #include "scene/resources/mesh_library.h" #endif @@ -441,10 +443,11 @@ void register_scene_types() { GDREGISTER_CLASS(Skin); GDREGISTER_VIRTUAL_CLASS(SkinReference); GDREGISTER_CLASS(Skeleton3D); + GDREGISTER_CLASS(ImporterMesh); + GDREGISTER_CLASS(ImporterMeshInstance3D); GDREGISTER_VIRTUAL_CLASS(VisualInstance3D); GDREGISTER_VIRTUAL_CLASS(GeometryInstance3D); GDREGISTER_CLASS(Camera3D); - GDREGISTER_CLASS(ClippedCamera3D); GDREGISTER_CLASS(AudioListener3D); GDREGISTER_CLASS(XRCamera3D); GDREGISTER_CLASS(XRController3D); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index a364a27e80..9dc76dcf44 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -966,9 +966,9 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, float p_tolerance) cons int pidx = 0; for (int i = 0; i < points.size() - 1; i++) { - for (Map<float, Vector2>::Element *E = midpoints[i].front(); E; E = E->next()) { + for (const KeyValue<float, Vector2> &E : midpoints[i]) { pidx++; - bpw[pidx] = E->get(); + bpw[pidx] = E.value; } pidx++; @@ -1652,9 +1652,9 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, float p_tolerance) cons int pidx = 0; for (int i = 0; i < points.size() - 1; i++) { - for (Map<float, Vector3>::Element *E = midpoints[i].front(); E; E = E->next()) { + for (const KeyValue<float, Vector3> &E : midpoints[i]) { pidx++; - bpw[pidx] = E->get(); + bpw[pidx] = E.value; } pidx++; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 492e0512e4..54bb7a82cf 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -790,8 +790,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("tab_selected", "Tabs", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2)); theme->set_stylebox("tab_unselected", "Tabs", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3)); theme->set_stylebox("tab_disabled", "Tabs", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3)); - theme->set_stylebox("button_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4)); - theme->set_stylebox("button", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4)); + theme->set_stylebox("close_bg_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4)); + theme->set_stylebox("close_bg_highlight", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4)); theme->set_icon("increment", "Tabs", make_icon(scroll_button_right_png)); theme->set_icon("increment_highlight", "Tabs", make_icon(scroll_button_right_hl_png)); @@ -1036,9 +1036,16 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { } Ref<Font> large_font = default_font; - fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0); + + float default_scale = 1.0; + if (p_hidpi) { + default_scale = 2.0; + } + + fill_default_theme(t, default_font, large_font, default_icon, default_style, default_scale); Theme::set_default(t); + Theme::set_default_base_scale(default_scale); Theme::set_default_icon(default_icon); Theme::set_default_style(default_style); Theme::set_default_font(default_font); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 9b403a18f0..c1d42dff09 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -502,6 +502,11 @@ void FontData::set_data(const PackedByteArray &p_data) { } PackedByteArray FontData::get_data() const { + if (unlikely((size_t)data.size() != data_size)) { + PackedByteArray *data_w = const_cast<PackedByteArray *>(&data); + data_w->resize(data_size); + memcpy(data_w->ptrw(), data_ptr, data_size); + } return data; } @@ -1399,7 +1404,7 @@ real_t Font::get_underline_thickness(int p_size) const { return ret; } -Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint8_t p_flags) const { +Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint16_t p_flags) const { ERR_FAIL_COND_V(data.is_empty(), Size2()); int size = (p_size <= 0) ? base_size : p_size; @@ -1426,7 +1431,7 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re return buffer->get_size(); } -Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint8_t p_flags) const { +Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint16_t p_flags) const { ERR_FAIL_COND_V(data.is_empty(), Size2()); int size = (p_size <= 0) ? base_size : p_size; @@ -1465,7 +1470,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int return ret; } -void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { +void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { ERR_FAIL_COND(data.is_empty()); int size = (p_size <= 0) ? base_size : p_size; @@ -1507,7 +1512,7 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t buffer->draw(p_canvas_item, ofs, p_modulate); } -void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { +void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { ERR_FAIL_COND(data.is_empty()); int size = (p_size <= 0) ? base_size : p_size; diff --git a/scene/resources/font.h b/scene/resources/font.h index 9a34edce64..e65fdb0751 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -266,11 +266,11 @@ public: virtual real_t get_underline_thickness(int p_size = -1) const; // Drawing string. - virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; + virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; - virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; // Helper functions. virtual bool has_char(char32_t p_char) const; diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp new file mode 100644 index 0000000000..af69b799cc --- /dev/null +++ b/scene/resources/importer_mesh.cpp @@ -0,0 +1,1242 @@ +/*************************************************************************/ +/* importer_mesh.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "importer_mesh.h" + +#include "core/math/random_pcg.h" +#include "core/math/static_raycaster.h" +#include "scene/resources/surface_tool.h" + +#include <cstdint> + +void ImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) { + ERR_FAIL_COND(arrays.size() != RS::ARRAY_MAX); + + const PackedVector3Array &vertices = arrays[RS::ARRAY_VERTEX]; + int current_vertex_count = vertices.size(); + int new_vertex_count = p_indices.size(); + int final_vertex_count = current_vertex_count + new_vertex_count; + const int *indices_ptr = p_indices.ptr(); + + for (int i = 0; i < arrays.size(); i++) { + if (i == RS::ARRAY_INDEX) { + continue; + } + + if (arrays[i].get_type() == Variant::NIL) { + continue; + } + + switch (arrays[i].get_type()) { + case Variant::PACKED_VECTOR3_ARRAY: { + PackedVector3Array data = arrays[i]; + data.resize(final_vertex_count); + Vector3 *data_ptr = data.ptrw(); + if (i == RS::ARRAY_NORMAL) { + const Vector3 *normals_ptr = p_normals.ptr(); + memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count); + } else { + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + } + arrays[i] = data; + } break; + case Variant::PACKED_VECTOR2_ARRAY: { + PackedVector2Array data = arrays[i]; + data.resize(final_vertex_count); + Vector2 *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + arrays[i] = data; + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + PackedFloat32Array data = arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + float *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements); + } + arrays[i] = data; + } break; + case Variant::PACKED_INT32_ARRAY: { + PackedInt32Array data = arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + int32_t *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements); + } + arrays[i] = data; + } break; + case Variant::PACKED_BYTE_ARRAY: { + PackedByteArray data = arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + uint8_t *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements); + } + arrays[i] = data; + } break; + case Variant::PACKED_COLOR_ARRAY: { + PackedColorArray data = arrays[i]; + data.resize(final_vertex_count); + Color *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + arrays[i] = data; + } break; + default: { + ERR_FAIL_MSG("Unhandled array type."); + } break; + } + } +} + +void ImporterMesh::add_blend_shape(const String &p_name) { + ERR_FAIL_COND(surfaces.size() > 0); + blend_shapes.push_back(p_name); +} + +int ImporterMesh::get_blend_shape_count() const { + return blend_shapes.size(); +} + +String ImporterMesh::get_blend_shape_name(int p_blend_shape) const { + ERR_FAIL_INDEX_V(p_blend_shape, blend_shapes.size(), String()); + return blend_shapes[p_blend_shape]; +} + +void ImporterMesh::set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode) { + blend_shape_mode = p_blend_shape_mode; +} + +Mesh::BlendShapeMode ImporterMesh::get_blend_shape_mode() const { + return blend_shape_mode; +} + +void ImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint32_t p_flags) { + ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size()); + ERR_FAIL_COND(p_arrays.size() != Mesh::ARRAY_MAX); + Surface s; + s.primitive = p_primitive; + s.arrays = p_arrays; + s.name = p_name; + s.flags = p_flags; + + Vector<Vector3> vertex_array = p_arrays[Mesh::ARRAY_VERTEX]; + int vertex_count = vertex_array.size(); + ERR_FAIL_COND(vertex_count == 0); + + for (int i = 0; i < blend_shapes.size(); i++) { + Array bsdata = p_blend_shapes[i]; + ERR_FAIL_COND(bsdata.size() != Mesh::ARRAY_MAX); + Vector<Vector3> vertex_data = bsdata[Mesh::ARRAY_VERTEX]; + ERR_FAIL_COND(vertex_data.size() != vertex_count); + Surface::BlendShape bs; + bs.arrays = bsdata; + s.blend_shape_data.push_back(bs); + } + + List<Variant> lods; + p_lods.get_key_list(&lods); + for (const Variant &E : lods) { + ERR_CONTINUE(!E.is_num()); + Surface::LOD lod; + lod.distance = E; + lod.indices = p_lods[E]; + ERR_CONTINUE(lod.indices.size() == 0); + s.lods.push_back(lod); + } + + s.material = p_material; + + surfaces.push_back(s); + mesh.unref(); +} + +int ImporterMesh::get_surface_count() const { + return surfaces.size(); +} + +Mesh::PrimitiveType ImporterMesh::get_surface_primitive_type(int p_surface) { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Mesh::PRIMITIVE_MAX); + return surfaces[p_surface].primitive; +} +Array ImporterMesh::get_surface_arrays(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array()); + return surfaces[p_surface].arrays; +} +String ImporterMesh::get_surface_name(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), String()); + return surfaces[p_surface].name; +} +void ImporterMesh::set_surface_name(int p_surface, const String &p_name) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + surfaces.write[p_surface].name = p_name; + mesh.unref(); +} + +Array ImporterMesh::get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array()); + ERR_FAIL_INDEX_V(p_blend_shape, surfaces[p_surface].blend_shape_data.size(), Array()); + return surfaces[p_surface].blend_shape_data[p_blend_shape].arrays; +} +int ImporterMesh::get_surface_lod_count(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0); + return surfaces[p_surface].lods.size(); +} +Vector<int> ImporterMesh::get_surface_lod_indices(int p_surface, int p_lod) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Vector<int>()); + ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), Vector<int>()); + + return surfaces[p_surface].lods[p_lod].indices; +} + +float ImporterMesh::get_surface_lod_size(int p_surface, int p_lod) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0); + ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), 0); + return surfaces[p_surface].lods[p_lod].distance; +} + +uint32_t ImporterMesh::get_surface_format(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0); + return surfaces[p_surface].flags; +} + +Ref<Material> ImporterMesh::get_surface_material(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Ref<Material>()); + return surfaces[p_surface].material; +} + +void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_material) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + surfaces.write[p_surface].material = p_material; + mesh.unref(); +} + +void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) { + if (!SurfaceTool::simplify_scale_func) { + return; + } + if (!SurfaceTool::simplify_with_attrib_func) { + return; + } + if (!SurfaceTool::optimize_vertex_cache_func) { + return; + } + + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) { + continue; + } + if (get_blend_shape_count()) { + continue; + } + + surfaces.write[i].lods.clear(); + Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; + PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX]; + Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL]; + Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV]; + + unsigned int index_count = indices.size(); + unsigned int vertex_count = vertices.size(); + + if (index_count == 0) { + continue; //no lods if no indices + } + + const Vector3 *vertices_ptr = vertices.ptr(); + const int *indices_ptr = indices.ptr(); + + if (normals.is_empty()) { + normals.resize(vertices.size()); + Vector3 *n_ptr = normals.ptrw(); + for (unsigned int j = 0; j < index_count; j += 3) { + const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]]; + const Vector3 &v1 = vertices_ptr[indices_ptr[j + 1]]; + const Vector3 &v2 = vertices_ptr[indices_ptr[j + 2]]; + Vector3 n = vec3_cross(v0 - v2, v0 - v1).normalized(); + n_ptr[j + 0] = n; + n_ptr[j + 1] = n; + n_ptr[j + 2] = n; + } + } + + float normal_merge_threshold = Math::cos(Math::deg2rad(p_normal_merge_angle)); + float normal_pre_split_threshold = Math::cos(Math::deg2rad(MIN(180.0f, p_normal_split_angle * 2.0f))); + float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle)); + const Vector3 *normals_ptr = normals.ptr(); + + Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices; + + LocalVector<int> vertex_remap; + LocalVector<int> vertex_inverse_remap; + LocalVector<Vector3> merged_vertices; + LocalVector<Vector3> merged_normals; + LocalVector<int> merged_normals_counts; + const Vector2 *uvs_ptr = uvs.ptr(); + + for (unsigned int j = 0; j < vertex_count; j++) { + const Vector3 &v = vertices_ptr[j]; + const Vector3 &n = normals_ptr[j]; + + Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v); + + if (E) { + const LocalVector<Pair<int, int>> &close_verts = E->get(); + + bool found = false; + for (unsigned int k = 0; k < close_verts.size(); k++) { + const Pair<int, int> &idx = close_verts[k]; + + // TODO check more attributes? + if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) { + vertex_remap.push_back(idx.first); + merged_normals[idx.first] += normals[idx.second]; + merged_normals_counts[idx.first]++; + found = true; + break; + } + } + + if (!found) { + int vcount = merged_vertices.size(); + unique_vertices[v].push_back(Pair<int, int>(vcount, j)); + vertex_inverse_remap.push_back(j); + merged_vertices.push_back(v); + vertex_remap.push_back(vcount); + merged_normals.push_back(normals_ptr[j]); + merged_normals_counts.push_back(1); + } + } else { + int vcount = merged_vertices.size(); + unique_vertices[v] = LocalVector<Pair<int, int>>(); + unique_vertices[v].push_back(Pair<int, int>(vcount, j)); + vertex_inverse_remap.push_back(j); + merged_vertices.push_back(v); + vertex_remap.push_back(vcount); + merged_normals.push_back(normals_ptr[j]); + merged_normals_counts.push_back(1); + } + } + + LocalVector<int> merged_indices; + merged_indices.resize(index_count); + for (unsigned int j = 0; j < index_count; j++) { + merged_indices[j] = vertex_remap[indices[j]]; + } + + unsigned int merged_vertex_count = merged_vertices.size(); + const Vector3 *merged_vertices_ptr = merged_vertices.ptr(); + const int32_t *merged_indices_ptr = merged_indices.ptr(); + + { + const int *counts_ptr = merged_normals_counts.ptr(); + Vector3 *merged_normals_ptrw = merged_normals.ptr(); + for (unsigned int j = 0; j < merged_vertex_count; j++) { + merged_normals_ptrw[j] /= counts_ptr[j]; + } + } + + LocalVector<float> normal_weights; + normal_weights.resize(merged_vertex_count); + for (unsigned int j = 0; j < merged_vertex_count; j++) { + normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting + } + + const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target + float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3)); + float mesh_error = 0.0f; + + unsigned int index_target = 12; // Start with the smallest target, 4 triangles + unsigned int last_index_count = 0; + + int split_vertex_count = vertex_count; + LocalVector<Vector3> split_vertex_normals; + LocalVector<int> split_vertex_indices; + split_vertex_normals.reserve(index_count / 3); + split_vertex_indices.reserve(index_count / 3); + + RandomPCG pcg; + pcg.seed(123456789); // Keep seed constant across imports + + Ref<StaticRaycaster> raycaster = StaticRaycaster::create(); + if (raycaster.is_valid()) { + raycaster->add_mesh(vertices, indices, 0); + raycaster->commit(); + } + + while (index_target < index_count) { + PackedInt32Array new_indices; + new_indices.resize(index_count); + + size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3); + + if (new_index_count < last_index_count * 1.5f) { + index_target = index_target * 1.5f; + continue; + } + + if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) { + break; + } + + new_indices.resize(new_index_count); + + LocalVector<LocalVector<int>> vertex_corners; + vertex_corners.resize(vertex_count); + { + int *ptrw = new_indices.ptrw(); + for (unsigned int j = 0; j < new_index_count; j++) { + const int &remapped = vertex_inverse_remap[ptrw[j]]; + vertex_corners[remapped].push_back(j); + ptrw[j] = remapped; + } + } + + if (raycaster.is_valid()) { + float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15)); + const float ray_bias = 0.05; + float ray_length = ray_bias + mesh_error * scale * 3.0f; + + Vector<StaticRaycaster::Ray> rays; + LocalVector<Vector2> ray_uvs; + + int32_t *new_indices_ptr = new_indices.ptrw(); + + int current_ray_count = 0; + for (unsigned int j = 0; j < new_index_count; j += 3) { + const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]]; + const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]]; + const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]]; + Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1); + float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care + + Vector3 dir = face_normal / face_area; + int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64); + + rays.resize(current_ray_count + ray_count); + StaticRaycaster::Ray *rays_ptr = rays.ptrw(); + + ray_uvs.resize(current_ray_count + ray_count); + Vector2 *ray_uvs_ptr = ray_uvs.ptr(); + + for (int k = 0; k < ray_count; k++) { + float u = pcg.randf(); + float v = pcg.randf(); + + if (u + v >= 1.0f) { + u = 1.0f - u; + v = 1.0f - v; + } + + u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge + v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third; + float w = 1.0f - u - v; + + Vector3 org = v0 * w + v1 * u + v2 * v; + org -= dir * ray_bias; + rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length); + rays_ptr[current_ray_count + k].id = j / 3; + ray_uvs_ptr[current_ray_count + k] = Vector2(u, v); + } + + current_ray_count += ray_count; + } + + raycaster->intersect(rays); + + LocalVector<Vector3> ray_normals; + LocalVector<float> ray_normal_weights; + + ray_normals.resize(new_index_count); + ray_normal_weights.resize(new_index_count); + + for (unsigned int j = 0; j < new_index_count; j++) { + ray_normal_weights[j] = 0.0f; + } + + const StaticRaycaster::Ray *rp = rays.ptr(); + for (int j = 0; j < rays.size(); j++) { + if (rp[j].geomID != 0) { // Ray missed + continue; + } + + if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face. + continue; + } + + const float &u = rp[j].u; + const float &v = rp[j].v; + const float w = 1.0f - u - v; + + const unsigned int &hit_tri_id = rp[j].primID; + const unsigned int &orig_tri_id = rp[j].id; + + const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]]; + const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]]; + const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]]; + Vector3 normal = n0 * w + n1 * u + n2 * v; + + Vector2 orig_uv = ray_uvs[j]; + float orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y }; + for (int k = 0; k < 3; k++) { + int idx = orig_tri_id * 3 + k; + float weight = orig_bary[k]; + ray_normals[idx] += normal * weight; + ray_normal_weights[idx] += weight; + } + } + + for (unsigned int j = 0; j < new_index_count; j++) { + if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess + ray_normals[j] = Vector3(); + } else { + ray_normals[j] /= ray_normal_weights[j]; + } + } + + LocalVector<LocalVector<int>> normal_group_indices; + LocalVector<Vector3> normal_group_averages; + normal_group_indices.reserve(24); + normal_group_averages.reserve(24); + + for (unsigned int j = 0; j < vertex_count; j++) { + const LocalVector<int> &corners = vertex_corners[j]; + const Vector3 &vertex_normal = normals_ptr[j]; + + for (unsigned int k = 0; k < corners.size(); k++) { + const int &corner_idx = corners[k]; + const Vector3 &ray_normal = ray_normals[corner_idx]; + + if (ray_normal.length_squared() < CMP_EPSILON2) { + continue; + } + + bool found = false; + for (unsigned int l = 0; l < normal_group_indices.size(); l++) { + LocalVector<int> &group_indices = normal_group_indices[l]; + Vector3 n = normal_group_averages[l] / group_indices.size(); + if (n.dot(ray_normal) > normal_pre_split_threshold) { + found = true; + group_indices.push_back(corner_idx); + normal_group_averages[l] += ray_normal; + break; + } + } + + if (!found) { + LocalVector<int> new_group; + new_group.push_back(corner_idx); + normal_group_indices.push_back(new_group); + normal_group_averages.push_back(ray_normal); + } + } + + for (unsigned int k = 0; k < normal_group_indices.size(); k++) { + LocalVector<int> &group_indices = normal_group_indices[k]; + Vector3 n = normal_group_averages[k] / group_indices.size(); + + if (vertex_normal.dot(n) < normal_split_threshold) { + split_vertex_indices.push_back(j); + split_vertex_normals.push_back(n); + int new_idx = split_vertex_count++; + for (unsigned int l = 0; l < group_indices.size(); l++) { + new_indices_ptr[group_indices[l]] = new_idx; + } + } + } + + normal_group_indices.clear(); + normal_group_averages.clear(); + } + } + + Surface::LOD lod; + lod.distance = MAX(mesh_error * scale, CMP_EPSILON2); + lod.indices = new_indices; + surfaces.write[i].lods.push_back(lod); + index_target = MAX(new_index_count, index_target) * 2; + last_index_count = new_index_count; + + if (mesh_error == 0.0f) { + break; + } + } + + surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals); + surfaces.write[i].lods.sort_custom<Surface::LODComparator>(); + + for (int j = 0; j < surfaces.write[i].lods.size(); j++) { + Surface::LOD &lod = surfaces.write[i].lods.write[j]; + unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw(); + SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count); + } + } +} + +bool ImporterMesh::has_mesh() const { + return mesh.is_valid(); +} + +Ref<ArrayMesh> ImporterMesh::get_mesh(const Ref<ArrayMesh> &p_base) { + ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>()); + + if (mesh.is_null()) { + if (p_base.is_valid()) { + mesh = p_base; + } + if (mesh.is_null()) { + mesh.instantiate(); + } + mesh->set_name(get_name()); + if (has_meta("import_id")) { + mesh->set_meta("import_id", get_meta("import_id")); + } + for (int i = 0; i < blend_shapes.size(); i++) { + mesh->add_blend_shape(blend_shapes[i]); + } + mesh->set_blend_shape_mode(blend_shape_mode); + for (int i = 0; i < surfaces.size(); i++) { + Array bs_data; + if (surfaces[i].blend_shape_data.size()) { + for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) { + bs_data.push_back(surfaces[i].blend_shape_data[j].arrays); + } + } + Dictionary lods; + if (surfaces[i].lods.size()) { + for (int j = 0; j < surfaces[i].lods.size(); j++) { + lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices; + } + } + + mesh->add_surface_from_arrays(surfaces[i].primitive, surfaces[i].arrays, bs_data, lods, surfaces[i].flags); + if (surfaces[i].material.is_valid()) { + mesh->surface_set_material(mesh->get_surface_count() - 1, surfaces[i].material); + } + if (surfaces[i].name != String()) { + mesh->surface_set_name(mesh->get_surface_count() - 1, surfaces[i].name); + } + } + + mesh->set_lightmap_size_hint(lightmap_size_hint); + + if (shadow_mesh.is_valid()) { + Ref<ArrayMesh> shadow = shadow_mesh->get_mesh(); + mesh->set_shadow_mesh(shadow); + } + } + + return mesh; +} + +void ImporterMesh::clear() { + surfaces.clear(); + blend_shapes.clear(); + mesh.unref(); +} + +void ImporterMesh::create_shadow_mesh() { + if (shadow_mesh.is_valid()) { + shadow_mesh.unref(); + } + + //no shadow mesh for blendshapes + if (blend_shapes.size() > 0) { + return; + } + //no shadow mesh for skeletons + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].arrays[RS::ARRAY_BONES].get_type() != Variant::NIL) { + return; + } + if (surfaces[i].arrays[RS::ARRAY_WEIGHTS].get_type() != Variant::NIL) { + return; + } + } + + shadow_mesh.instantiate(); + + for (int i = 0; i < surfaces.size(); i++) { + LocalVector<int> vertex_remap; + Vector<Vector3> new_vertices; + Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; + int vertex_count = vertices.size(); + { + Map<Vector3, int> unique_vertices; + const Vector3 *vptr = vertices.ptr(); + for (int j = 0; j < vertex_count; j++) { + const Vector3 &v = vptr[j]; + + Map<Vector3, int>::Element *E = unique_vertices.find(v); + + if (E) { + vertex_remap.push_back(E->get()); + } else { + int vcount = unique_vertices.size(); + unique_vertices[v] = vcount; + vertex_remap.push_back(vcount); + new_vertices.push_back(v); + } + } + } + + Array new_surface; + new_surface.resize(RS::ARRAY_MAX); + Dictionary lods; + + // print_line("original vertex count: " + itos(vertices.size()) + " new vertex count: " + itos(new_vertices.size())); + + new_surface[RS::ARRAY_VERTEX] = new_vertices; + + Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX]; + if (indices.size()) { + int index_count = indices.size(); + const int *index_rptr = indices.ptr(); + Vector<int> new_indices; + new_indices.resize(indices.size()); + int *index_wptr = new_indices.ptrw(); + + for (int j = 0; j < index_count; j++) { + int index = index_rptr[j]; + ERR_FAIL_INDEX(index, vertex_count); + index_wptr[j] = vertex_remap[index]; + } + + new_surface[RS::ARRAY_INDEX] = new_indices; + + // Make sure the same LODs as the full version are used. + // This makes it more coherent between rendered model and its shadows. + for (int j = 0; j < surfaces[i].lods.size(); j++) { + indices = surfaces[i].lods[j].indices; + + index_count = indices.size(); + index_rptr = indices.ptr(); + new_indices.resize(indices.size()); + index_wptr = new_indices.ptrw(); + + for (int k = 0; k < index_count; k++) { + int index = index_rptr[k]; + ERR_FAIL_INDEX(index, vertex_count); + index_wptr[k] = vertex_remap[index]; + } + + lods[surfaces[i].lods[j].distance] = new_indices; + } + } + + shadow_mesh->add_surface(surfaces[i].primitive, new_surface, Array(), lods, Ref<Material>(), surfaces[i].name, surfaces[i].flags); + } +} + +Ref<ImporterMesh> ImporterMesh::get_shadow_mesh() const { + return shadow_mesh; +} + +void ImporterMesh::_set_data(const Dictionary &p_data) { + clear(); + if (p_data.has("blend_shape_names")) { + blend_shapes = p_data["blend_shape_names"]; + } + if (p_data.has("surfaces")) { + Array surface_arr = p_data["surfaces"]; + for (int i = 0; i < surface_arr.size(); i++) { + Dictionary s = surface_arr[i]; + ERR_CONTINUE(!s.has("primitive")); + ERR_CONTINUE(!s.has("arrays")); + Mesh::PrimitiveType prim = Mesh::PrimitiveType(int(s["primitive"])); + ERR_CONTINUE(prim >= Mesh::PRIMITIVE_MAX); + Array arr = s["arrays"]; + Dictionary lods; + String name; + if (s.has("name")) { + name = s["name"]; + } + if (s.has("lods")) { + lods = s["lods"]; + } + Array b_shapes; + if (s.has("b_shapes")) { + b_shapes = s["b_shapes"]; + } + Ref<Material> material; + if (s.has("material")) { + material = s["material"]; + } + uint32_t flags = 0; + if (s.has("flags")) { + flags = s["flags"]; + } + add_surface(prim, arr, b_shapes, lods, material, name, flags); + } + } +} +Dictionary ImporterMesh::_get_data() const { + Dictionary data; + if (blend_shapes.size()) { + data["blend_shape_names"] = blend_shapes; + } + Array surface_arr; + for (int i = 0; i < surfaces.size(); i++) { + Dictionary d; + d["primitive"] = surfaces[i].primitive; + d["arrays"] = surfaces[i].arrays; + if (surfaces[i].blend_shape_data.size()) { + Array bs_data; + for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) { + bs_data.push_back(surfaces[i].blend_shape_data[j].arrays); + } + d["blend_shapes"] = bs_data; + } + if (surfaces[i].lods.size()) { + Dictionary lods; + for (int j = 0; j < surfaces[i].lods.size(); j++) { + lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices; + } + d["lods"] = lods; + } + + if (surfaces[i].material.is_valid()) { + d["material"] = surfaces[i].material; + } + + if (surfaces[i].name != String()) { + d["name"] = surfaces[i].name; + } + + if (surfaces[i].flags != 0) { + d["flags"] = surfaces[i].flags; + } + + surface_arr.push_back(d); + } + data["surfaces"] = surface_arr; + return data; +} + +Vector<Face3> ImporterMesh::get_faces() const { + Vector<Face3> faces; + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) { + Vector<Vector3> vertices = surfaces[i].arrays[Mesh::ARRAY_VERTEX]; + Vector<int> indices = surfaces[i].arrays[Mesh::ARRAY_INDEX]; + if (indices.size()) { + for (int j = 0; j < indices.size(); j += 3) { + Face3 f; + f.vertex[0] = vertices[indices[j + 0]]; + f.vertex[1] = vertices[indices[j + 1]]; + f.vertex[2] = vertices[indices[j + 2]]; + faces.push_back(f); + } + } else { + for (int j = 0; j < vertices.size(); j += 3) { + Face3 f; + f.vertex[0] = vertices[j + 0]; + f.vertex[1] = vertices[j + 1]; + f.vertex[2] = vertices[j + 2]; + faces.push_back(f); + } + } + } + } + + return faces; +} + +Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const { + ERR_FAIL_COND_V(!Mesh::convex_decomposition_function, Vector<Ref<Shape3D>>()); + + const Vector<Face3> faces = get_faces(); + int face_count = faces.size(); + + Vector<Vector3> vertices; + uint32_t vertex_count = 0; + vertices.resize(face_count * 3); + Vector<uint32_t> indices; + indices.resize(face_count * 3); + { + Map<Vector3, uint32_t> vertex_map; + Vector3 *vertex_w = vertices.ptrw(); + uint32_t *index_w = indices.ptrw(); + for (int i = 0; i < face_count; i++) { + for (int j = 0; j < 3; j++) { + const Vector3 &vertex = faces[i].vertex[j]; + Map<Vector3, uint32_t>::Element *found_vertex = vertex_map.find(vertex); + uint32_t index; + if (found_vertex) { + index = found_vertex->get(); + } else { + index = ++vertex_count; + vertex_map[vertex] = index; + vertex_w[index] = vertex; + } + index_w[i * 3 + j] = index; + } + } + } + vertices.resize(vertex_count); + + Vector<Vector<Vector3>> decomposed = Mesh::convex_decomposition_function((real_t *)vertices.ptr(), vertex_count, indices.ptr(), face_count, p_settings, nullptr); + + Vector<Ref<Shape3D>> ret; + + for (int i = 0; i < decomposed.size(); i++) { + Ref<ConvexPolygonShape3D> shape; + shape.instantiate(); + shape->set_points(decomposed[i]); + ret.push_back(shape); + } + + return ret; +} + +Ref<Shape3D> ImporterMesh::create_trimesh_shape() const { + Vector<Face3> faces = get_faces(); + if (faces.size() == 0) { + return Ref<Shape3D>(); + } + + Vector<Vector3> face_points; + face_points.resize(faces.size() * 3); + + for (int i = 0; i < face_points.size(); i += 3) { + Face3 f = faces.get(i / 3); + face_points.set(i, f.vertex[0]); + face_points.set(i + 1, f.vertex[1]); + face_points.set(i + 2, f.vertex[2]); + } + + Ref<ConcavePolygonShape3D> shape = memnew(ConcavePolygonShape3D); + shape->set_faces(face_points); + return shape; +} + +Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { + Vector<Face3> faces = get_faces(); + if (faces.size() == 0) { + return Ref<NavigationMesh>(); + } + + Map<Vector3, int> unique_vertices; + LocalVector<int> face_indices; + + for (int i = 0; i < faces.size(); i++) { + for (int j = 0; j < 3; j++) { + Vector3 v = faces[i].vertex[j]; + int idx; + if (unique_vertices.has(v)) { + idx = unique_vertices[v]; + } else { + idx = unique_vertices.size(); + unique_vertices[v] = idx; + } + face_indices.push_back(idx); + } + } + + Vector<Vector3> vertices; + vertices.resize(unique_vertices.size()); + for (const KeyValue<Vector3, int> &E : unique_vertices) { + vertices.write[E.value] = E.key; + } + + Ref<NavigationMesh> nm; + nm.instantiate(); + nm->set_vertices(vertices); + + Vector<int> v3; + v3.resize(3); + for (uint32_t i = 0; i < face_indices.size(); i += 3) { + v3.write[0] = face_indices[i + 0]; + v3.write[1] = face_indices[i + 1]; + v3.write[2] = face_indices[i + 2]; + nm->add_polygon(v3); + } + + return nm; +} + +extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); + +struct EditorSceneImporterMeshLightmapSurface { + Ref<Material> material; + LocalVector<SurfaceTool::Vertex> vertices; + Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX; + uint32_t format = 0; + String name; +}; + +Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) { + ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); + + LocalVector<float> vertices; + LocalVector<float> normals; + LocalVector<int> indices; + LocalVector<float> uv; + LocalVector<Pair<int, int>> uv_indices; + + Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces; + + // Keep only the scale + Basis basis = p_base_transform.get_basis(); + Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length()); + + Transform3D transform; + transform.scale(scale); + + Basis normal_basis = transform.basis.inverse().transposed(); + + for (int i = 0; i < get_surface_count(); i++) { + EditorSceneImporterMeshLightmapSurface s; + s.primitive = get_surface_primitive_type(i); + + ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap."); + Array arrays = get_surface_arrays(i); + s.material = get_surface_material(i); + s.name = get_surface_name(i); + + SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format); + + PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX]; + int vc = rvertices.size(); + + PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL]; + + int vertex_ofs = vertices.size() / 3; + + vertices.resize((vertex_ofs + vc) * 3); + normals.resize((vertex_ofs + vc) * 3); + uv_indices.resize(vertex_ofs + vc); + + for (int j = 0; j < vc; j++) { + Vector3 v = transform.xform(rvertices[j]); + Vector3 n = normal_basis.xform(rnormals[j]).normalized(); + + vertices[(j + vertex_ofs) * 3 + 0] = v.x; + vertices[(j + vertex_ofs) * 3 + 1] = v.y; + vertices[(j + vertex_ofs) * 3 + 2] = v.z; + normals[(j + vertex_ofs) * 3 + 0] = n.x; + normals[(j + vertex_ofs) * 3 + 1] = n.y; + normals[(j + vertex_ofs) * 3 + 2] = n.z; + uv_indices[j + vertex_ofs] = Pair<int, int>(i, j); + } + + PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX]; + int ic = rindices.size(); + + float eps = 1.19209290e-7F; // Taken from xatlas.h + if (ic == 0) { + for (int j = 0; j < vc / 3; j++) { + Vector3 p0 = transform.xform(rvertices[j * 3 + 0]); + Vector3 p1 = transform.xform(rvertices[j * 3 + 1]); + Vector3 p2 = transform.xform(rvertices[j * 3 + 2]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { + continue; + } + + indices.push_back(vertex_ofs + j * 3 + 0); + indices.push_back(vertex_ofs + j * 3 + 1); + indices.push_back(vertex_ofs + j * 3 + 2); + } + + } else { + for (int j = 0; j < ic / 3; j++) { + Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]); + Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]); + Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { + continue; + } + + indices.push_back(vertex_ofs + rindices[j * 3 + 0]); + indices.push_back(vertex_ofs + rindices[j * 3 + 1]); + indices.push_back(vertex_ofs + rindices[j * 3 + 2]); + } + } + + lightmap_surfaces.push_back(s); + } + + //unwrap + + bool use_cache = true; // Used to request cache generation and to know if cache was used + uint8_t *gen_cache; + int gen_cache_size; + float *gen_uvs; + int *gen_vertices; + int *gen_indices; + int gen_vertex_count; + int gen_index_count; + int size_x; + int size_y; + + bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y); + + if (!ok) { + return ERR_CANT_CREATE; + } + + //remove surfaces + clear(); + + //create surfacetools for each surface.. + LocalVector<Ref<SurfaceTool>> surfaces_tools; + + for (int i = 0; i < lightmap_surfaces.size(); i++) { + Ref<SurfaceTool> st; + st.instantiate(); + st->begin(Mesh::PRIMITIVE_TRIANGLES); + st->set_material(lightmap_surfaces[i].material); + st->set_meta("name", lightmap_surfaces[i].name); + surfaces_tools.push_back(st); //stay there + } + + print_verbose("Mesh: Gen indices: " + itos(gen_index_count)); + + //go through all indices + for (int i = 0; i < gen_index_count; i += 3) { + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG); + + ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG); + + int surface = uv_indices[gen_vertices[gen_indices[i + 0]]].first; + + for (int j = 0; j < 3; j++) { + SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second]; + + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) { + surfaces_tools[surface]->set_color(v.color); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) { + surfaces_tools[surface]->set_uv(v.uv); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) { + surfaces_tools[surface]->set_normal(v.normal); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) { + Plane t; + t.normal = v.tangent; + t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; + surfaces_tools[surface]->set_tangent(t); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) { + surfaces_tools[surface]->set_bones(v.bones); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) { + surfaces_tools[surface]->set_weights(v.weights); + } + + Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); + surfaces_tools[surface]->set_uv2(uv2); + + surfaces_tools[surface]->add_vertex(v.vertex); + } + } + + //generate surfaces + for (unsigned int i = 0; i < surfaces_tools.size(); i++) { + surfaces_tools[i]->index(); + Array arrays = surfaces_tools[i]->commit_to_arrays(); + add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name")); + } + + set_lightmap_size_hint(Size2(size_x, size_y)); + + if (gen_cache_size > 0) { + r_dst_cache.resize(gen_cache_size); + memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size); + memfree(gen_cache); + } + + if (!use_cache) { + // Cache was not used, free the buffers + memfree(gen_vertices); + memfree(gen_indices); + memfree(gen_uvs); + } + + return OK; +} + +void ImporterMesh::set_lightmap_size_hint(const Size2i &p_size) { + lightmap_size_hint = p_size; +} + +Size2i ImporterMesh::get_lightmap_size_hint() const { + return lightmap_size_hint; +} + +void ImporterMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ImporterMesh::add_blend_shape); + ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &ImporterMesh::get_blend_shape_count); + ClassDB::bind_method(D_METHOD("get_blend_shape_name", "blend_shape_idx"), &ImporterMesh::get_blend_shape_name); + + ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ImporterMesh::set_blend_shape_mode); + ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ImporterMesh::get_blend_shape_mode); + + ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name", "flags"), &ImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String()), DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("get_surface_count"), &ImporterMesh::get_surface_count); + ClassDB::bind_method(D_METHOD("get_surface_primitive_type", "surface_idx"), &ImporterMesh::get_surface_primitive_type); + ClassDB::bind_method(D_METHOD("get_surface_name", "surface_idx"), &ImporterMesh::get_surface_name); + ClassDB::bind_method(D_METHOD("get_surface_arrays", "surface_idx"), &ImporterMesh::get_surface_arrays); + ClassDB::bind_method(D_METHOD("get_surface_blend_shape_arrays", "surface_idx", "blend_shape_idx"), &ImporterMesh::get_surface_blend_shape_arrays); + ClassDB::bind_method(D_METHOD("get_surface_lod_count", "surface_idx"), &ImporterMesh::get_surface_lod_count); + ClassDB::bind_method(D_METHOD("get_surface_lod_size", "surface_idx", "lod_idx"), &ImporterMesh::get_surface_lod_size); + ClassDB::bind_method(D_METHOD("get_surface_lod_indices", "surface_idx", "lod_idx"), &ImporterMesh::get_surface_lod_indices); + ClassDB::bind_method(D_METHOD("get_surface_material", "surface_idx"), &ImporterMesh::get_surface_material); + ClassDB::bind_method(D_METHOD("get_surface_format", "surface_idx"), &ImporterMesh::get_surface_format); + + ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name); + ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material); + + ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>())); + ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &ImporterMesh::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &ImporterMesh::_get_data); + + ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &ImporterMesh::set_lightmap_size_hint); + ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &ImporterMesh::get_lightmap_size_hint); + + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); +} diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h new file mode 100644 index 0000000000..89909f17f0 --- /dev/null +++ b/scene/resources/importer_mesh.h @@ -0,0 +1,132 @@ +/*************************************************************************/ +/* importer_mesh.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SCENE_IMPORTER_MESH_H +#define SCENE_IMPORTER_MESH_H + +#include "core/io/resource.h" +#include "core/templates/local_vector.h" +#include "scene/resources/concave_polygon_shape_3d.h" +#include "scene/resources/convex_polygon_shape_3d.h" +#include "scene/resources/mesh.h" +#include "scene/resources/navigation_mesh.h" + +#include <cstdint> + +// The following classes are used by importers instead of ArrayMesh and MeshInstance3D +// so the data is not registered (hence, quality loss), importing happens faster and +// its easier to modify before saving + +class ImporterMesh : public Resource { + GDCLASS(ImporterMesh, Resource) + + struct Surface { + Mesh::PrimitiveType primitive; + Array arrays; + struct BlendShape { + Array arrays; + }; + Vector<BlendShape> blend_shape_data; + struct LOD { + Vector<int> indices; + float distance = 0.0f; + }; + Vector<LOD> lods; + Ref<Material> material; + String name; + uint32_t flags = 0; + + struct LODComparator { + _FORCE_INLINE_ bool operator()(const LOD &l, const LOD &r) const { + return l.distance < r.distance; + } + }; + + void split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals); + }; + Vector<Surface> surfaces; + Vector<String> blend_shapes; + Mesh::BlendShapeMode blend_shape_mode = Mesh::BLEND_SHAPE_MODE_NORMALIZED; + + Ref<ArrayMesh> mesh; + + Ref<ImporterMesh> shadow_mesh; + + Size2i lightmap_size_hint; + +protected: + void _set_data(const Dictionary &p_data); + Dictionary _get_data() const; + + static void _bind_methods(); + +public: + void add_blend_shape(const String &p_name); + int get_blend_shape_count() const; + String get_blend_shape_name(int p_blend_shape) const; + + void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint32_t p_flags = 0); + int get_surface_count() const; + + void set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode); + Mesh::BlendShapeMode get_blend_shape_mode() const; + + Mesh::PrimitiveType get_surface_primitive_type(int p_surface); + String get_surface_name(int p_surface) const; + void set_surface_name(int p_surface, const String &p_name); + Array get_surface_arrays(int p_surface) const; + Array get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const; + int get_surface_lod_count(int p_surface) const; + Vector<int> get_surface_lod_indices(int p_surface, int p_lod) const; + float get_surface_lod_size(int p_surface, int p_lod) const; + Ref<Material> get_surface_material(int p_surface) const; + uint32_t get_surface_format(int p_surface) const; + + void set_surface_material(int p_surface, const Ref<Material> &p_material); + + void generate_lods(float p_normal_merge_angle, float p_normal_split_angle); + + void create_shadow_mesh(); + Ref<ImporterMesh> get_shadow_mesh() const; + + Vector<Face3> get_faces() const; + Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const; + Ref<Shape3D> create_trimesh_shape() const; + Ref<NavigationMesh> create_navigation_mesh(); + Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache); + + void set_lightmap_size_hint(const Size2i &p_size); + Size2i get_lightmap_size_hint() const; + + bool has_mesh() const; + Ref<ArrayMesh> get_mesh(const Ref<ArrayMesh> &p_base = Ref<ArrayMesh>()); + void clear(); +}; +#endif // SCENE_IMPORTER_MESH_H diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 3a6af3afb0..66f04f0292 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -32,11 +32,6 @@ #include "core/config/engine.h" #include "core/version.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - #include "scene/main/scene_tree.h" #include "scene/scene_string_names.h" @@ -80,6 +75,9 @@ void Material::_validate_property(PropertyInfo &property) const { if (!_can_do_next_pass() && property.name == "next_pass") { property.usage = PROPERTY_USAGE_NONE; } + if (!_can_use_render_priority() && property.name == "render_priority") { + property.usage = PROPERTY_USAGE_NONE; + } } void Material::inspect_native_shader_code() { @@ -268,19 +266,13 @@ void ShaderMaterial::_bind_methods() { } void ShaderMaterial::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { -#ifdef TOOLS_ENABLED - const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; -#else - const String quote_style = "\""; -#endif - String f = p_function.operator String(); if ((f == "get_shader_param" || f == "set_shader_param") && p_idx == 0) { if (shader.is_valid()) { List<PropertyInfo> pl; shader->get_param_list(&pl); for (const PropertyInfo &E : pl) { - r_options->push_back(E.name.replace_first("shader_param/", "").quote(quote_style)); + r_options->push_back(E.name.replace_first("shader_param/", "").quote()); } } } @@ -291,6 +283,10 @@ bool ShaderMaterial::_can_do_next_pass() const { return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL; } +bool ShaderMaterial::_can_use_render_priority() const { + return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL; +} + Shader::Mode ShaderMaterial::get_shader_mode() const { if (shader.is_valid()) { return shader->get_mode(); diff --git a/scene/resources/material.h b/scene/resources/material.h index 5d7a5324ca..798f7568df 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -53,6 +53,7 @@ protected: _FORCE_INLINE_ RID _get_material() const { return material; } static void _bind_methods(); virtual bool _can_do_next_pass() const { return false; } + virtual bool _can_use_render_priority() const { return false; } void _validate_property(PropertyInfo &property) const override; @@ -93,6 +94,7 @@ protected: void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; virtual bool _can_do_next_pass() const override; + virtual bool _can_use_render_priority() const override; void _shader_changed(); @@ -535,6 +537,7 @@ protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const override; virtual bool _can_do_next_pass() const override { return true; } + virtual bool _can_use_render_priority() const override { return true; } public: void set_albedo(const Color &p_albedo); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 67db8c1a10..18e6a51118 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -373,8 +373,8 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { //normalize - for (Map<Vector3, Vector3>::Element *E = normal_accum.front(); E; E = E->next()) { - E->get().normalize(); + for (KeyValue<Vector3, Vector3> &E : normal_accum) { + E.value.normalize(); } //displace normals diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index cfb7c3e037..309670e0b1 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -97,8 +97,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { } void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { - for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) { - String name = "item/" + itos(E->key()) + "/"; + for (const KeyValue<int, Item> &E : item_map) { + String name = "item/" + itos(E.key) + "/"; p_list->push_back(PropertyInfo(Variant::STRING, name + "name")); p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "mesh_transform")); @@ -230,17 +230,17 @@ Vector<int> MeshLibrary::get_item_list() const { Vector<int> ret; ret.resize(item_map.size()); int idx = 0; - for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) { - ret.write[idx++] = E->key(); + for (const KeyValue<int, Item> &E : item_map) { + ret.write[idx++] = E.key; } return ret; } int MeshLibrary::find_item_by_name(const String &p_name) const { - for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) { - if (E->get().name == p_name) { - return E->key(); + for (const KeyValue<int, Item> &E : item_map) { + if (E.value.name == p_name) { + return E.key; } } return -1; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 00cee9269b..d87056f55d 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -41,6 +41,8 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { continue; } Array arr = p_mesh->surface_get_arrays(i); + ERR_CONTINUE(arr.size() != Mesh::ARRAY_MAX); + Vector<Vector3> varr = arr[Mesh::ARRAY_VERTEX]; Vector<int> iarr = arr[Mesh::ARRAY_INDEX]; if (varr.size() == 0 || iarr.size() == 0) { @@ -367,10 +369,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() { } List<Vector3> lines; - for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) { - if (E->get()) { - lines.push_back(E->key().from); - lines.push_back(E->key().to); + for (const KeyValue<_EdgeKey, bool> &E : edge_map) { + if (E.value) { + lines.push_back(E.key.from); + lines.push_back(E.key.to); } } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 59faa50114..c8d3ea5e37 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -299,8 +299,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } - for (Map<Ref<Resource>, Ref<Resource>>::Element *E = resources_local_to_scene.front(); E; E = E->next()) { - E->get()->setup_local_to_scene(); + for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) { + E.value->setup_local_to_scene(); } //do connections @@ -384,7 +384,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // save the child instantiated scenes that are chosen as editable, so they can be restored // upon load back - if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) { + if (p_node != p_owner && p_node->get_scene_file_path() != String() && p_owner->is_editable_instance(p_node)) { editable_instances.push_back(p_owner->get_path_to(p_node)); // Node is the root of an editable instance. is_editable_instance = true; @@ -437,14 +437,14 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map } } - if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) { + if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) { if (p_node->get_scene_instance_load_placeholder()) { //it's a placeholder, use the placeholder path - nd.instance = _vm_get_variant(p_node->get_filename(), variant_map); + nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map); nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER; } else { //must instance ourselves - Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename()); + Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path()); if (!instance.is_valid()) { return ERR_CANT_OPEN; } @@ -454,7 +454,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map } n = nullptr; } else { - if (n->get_filename() != String()) { + if (n->get_scene_file_path() != String()) { //is an instance Ref<SceneState> state = n->get_scene_instance_state(); if (state.is_valid()) { @@ -714,7 +714,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName ERR_CONTINUE(!common_parent); - if (common_parent != p_owner && common_parent->get_filename() == String()) { + if (common_parent != p_owner && common_parent->get_scene_file_path() == String()) { common_parent = common_parent->get_owner(); } @@ -774,7 +774,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName nl = nullptr; } else { - if (nl->get_filename() != String()) { + if (nl->get_scene_file_path() != String()) { //is an instance Ref<SceneState> state = nl->get_scene_instance_state(); if (state.is_valid()) { @@ -887,8 +887,8 @@ Error SceneState::pack(Node *p_scene) { names.resize(name_map.size()); - for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) { - names.write[E->get()] = E->key(); + for (const KeyValue<StringName, int> &E : name_map) { + names.write[E.value] = E.key; } variants.resize(variant_map.size()); @@ -899,14 +899,14 @@ Error SceneState::pack(Node *p_scene) { } node_paths.resize(nodepath_map.size()); - for (Map<Node *, int>::Element *E = nodepath_map.front(); E; E = E->next()) { - node_paths.write[E->get()] = scene->get_path_to(E->key()); + for (const KeyValue<Node *, int> &E : nodepath_map) { + node_paths.write[E.value] = scene->get_path_to(E.key); } if (Engine::get_singleton()->is_editor_hint()) { // Build node path cache - for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) { - node_path_cache[scene->get_path_to(E->key())] = E->get(); + for (const KeyValue<Node *, int> &E : node_map) { + node_path_cache[scene->get_path_to(E.key)] = E.value; } } @@ -977,9 +977,9 @@ int SceneState::find_node_by_path(const NodePath &p_node) const { } int SceneState::_find_base_scene_node_remap_key(int p_idx) const { - for (Map<int, int>::Element *E = base_scene_node_remap.front(); E; E = E->next()) { - if (E->value() == p_idx) { - return E->key(); + for (const KeyValue<int, int> &E : base_scene_node_remap) { + if (E.value == p_idx) { + return E.key; } } return -1; @@ -1652,7 +1652,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const { } if (get_path() != "" && get_path().find("::") == -1) { - s->set_filename(get_path()); + s->set_scene_file_path(get_path()); } s->notification(Node::NOTIFICATION_INSTANCED); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 341ce22185..77d915aef9 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1653,55 +1653,55 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r #ifdef TOOLS_ENABLED // Keep order from cached ids. Set<String> cached_ids_found; - for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) { - String cached_id = E->key()->get_id_for_path(local_path); + for (KeyValue<RES, String> &E : external_resources) { + String cached_id = E.key->get_id_for_path(local_path); if (cached_id == "" || cached_ids_found.has(cached_id)) { - int sep_pos = E->get().find("_"); + int sep_pos = E.value.find("_"); if (sep_pos != -1) { - E->get() = E->get().substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance. + E.value = E.value.substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance. } else { - E->get() = ""; + E.value = ""; } } else { - E->get() = cached_id; + E.value = cached_id; cached_ids_found.insert(cached_id); } } // Create IDs for non cached resources. - for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) { - if (cached_ids_found.has(E->get())) { // Already cached, go on. + for (KeyValue<RES, String> &E : external_resources) { + if (cached_ids_found.has(E.value)) { // Already cached, go on. continue; } String attempt; while (true) { - attempt = E->get() + Resource::generate_scene_unique_id(); + attempt = E.value + Resource::generate_scene_unique_id(); if (!cached_ids_found.has(attempt)) { break; } } cached_ids_found.insert(attempt); - E->get() = attempt; + E.value = attempt; // Update also in resource. - Ref<Resource> res = E->key(); + Ref<Resource> res = E.key; res->set_id_for_path(local_path, attempt); } #else // Make sure to start from one, as it makes format more readable. int counter = 1; - for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) { - E->get() = itos(counter++); + for (KeyValue<RES, String> &E : external_resources) { + E.value = itos(counter++); } #endif Vector<ResourceSort> sorted_er; - for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) { + for (const KeyValue<RES, String> &E : external_resources) { ResourceSort rs; - rs.resource = E->key(); - rs.id = E->get(); + rs.resource = E.key; + rs.id = E.value; sorted_er.push_back(rs); } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 44d524f142..242e20f3b0 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -116,8 +116,8 @@ Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param) cons } void Shader::get_default_texture_param_list(List<StringName> *r_textures) const { - for (const Map<StringName, Ref<Texture2D>>::Element *E = default_textures.front(); E; E = E->next()) { - r_textures->push_back(E->key()); + for (const KeyValue<StringName, Ref<Texture2D>> &E : default_textures) { + r_textures->push_back(E.key); } } diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 39082b6f7a..b6d3c96cb7 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -125,10 +125,6 @@ float ProceduralSkyMaterial::get_sun_curve() const { return sun_curve; } -bool ProceduralSkyMaterial::_can_do_next_pass() const { - return false; -} - Shader::Mode ProceduralSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } @@ -312,10 +308,6 @@ Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const { return panorama; } -bool PanoramaSkyMaterial::_can_do_next_pass() const { - return false; -} - Shader::Mode PanoramaSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } @@ -482,10 +474,6 @@ Ref<Texture2D> PhysicalSkyMaterial::get_night_sky() const { return night_sky; } -bool PhysicalSkyMaterial::_can_do_next_pass() const { - return false; -} - Shader::Mode PhysicalSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 63e730617b..daeda212d4 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -58,7 +58,6 @@ private: protected: static void _bind_methods(); - virtual bool _can_do_next_pass() const override; public: void set_sky_top_color(const Color &p_sky_top); @@ -117,7 +116,6 @@ private: protected: static void _bind_methods(); - virtual bool _can_do_next_pass() const override; public: void set_panorama(const Ref<Texture2D> &p_panorama); @@ -159,7 +157,6 @@ private: protected: static void _bind_methods(); - virtual bool _can_do_next_pass() const override; public: void set_rayleigh_coefficient(float p_rayleigh); diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index 140c6f821f..01afb00283 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -108,15 +108,15 @@ Vector<String> SpriteFrames::_get_animation_list() const { } void SpriteFrames::get_animation_list(List<StringName> *r_animations) const { - for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { - r_animations->push_back(E->key()); + for (const KeyValue<StringName, Anim> &E : animations) { + r_animations->push_back(E.key); } } Vector<String> SpriteFrames::get_animation_names() const { Vector<String> names; - for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { - names.push_back(E->key()); + for (const KeyValue<StringName, Anim> &E : animations) { + names.push_back(E.key); } names.sort(); return names; @@ -164,14 +164,14 @@ Array SpriteFrames::_get_frames() const { Array SpriteFrames::_get_animations() const { Array anims; - for (Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { + for (const KeyValue<StringName, Anim> &E : animations) { Dictionary d; - d["name"] = E->key(); - d["speed"] = E->get().speed; - d["loop"] = E->get().loop; + d["name"] = E.key; + d["speed"] = E.value.speed; + d["loop"] = E.value.loop; Array frames; - for (int i = 0; i < E->get().frames.size(); i++) { - frames.push_back(E->get().frames[i]); + for (int i = 0; i < E.value.frames.size(); i++) { + frames.push_back(E.value.frames[i]); } d["frames"] = frames; anims.push_back(d); diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index d2f38ba836..0094518967 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -53,7 +53,7 @@ void TextLine::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control"); - ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override); + ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override); ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL("")); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1)); @@ -112,7 +112,7 @@ void TextLine::_shape() { TS->shaped_text_tab_align(rid, tab_stops); } - uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; + uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; if (overrun_behavior != OVERRUN_NO_TRIMMING) { switch (overrun_behavior) { case OVERRUN_TRIM_WORD_ELLIPSIS: @@ -195,15 +195,7 @@ TextServer::Orientation TextLine::get_orientation() const { return TS->shaped_text_get_orientation(rid); } -void TextLine::_set_bidi_override(const Array &p_override) { - Vector<Vector2i> overrides; - for (int i = 0; i < p_override.size(); i++) { - overrides.push_back(p_override[i]); - } - set_bidi_override(overrides); -} - -void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) { +void TextLine::set_bidi_override(const Array &p_override) { TS->shaped_text_set_bidi_override(rid, p_override); dirty = true; } @@ -256,14 +248,14 @@ void TextLine::tab_align(const Vector<float> &p_tab_stops) { dirty = true; } -void TextLine::set_flags(uint8_t p_flags) { +void TextLine::set_flags(uint16_t p_flags) { if (flags != p_flags) { flags = p_flags; dirty = true; } } -uint8_t TextLine::get_flags() const { +uint16_t TextLine::get_flags() const { return flags; } diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index 9ed9c2f177..43739f27ec 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -56,7 +56,7 @@ private: bool dirty = true; float width = -1.0; - uint8_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + uint16_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; HAlign align = HALIGN_LEFT; OverrunBehavior overrun_behavior = OVERRUN_TRIM_ELLIPSIS; @@ -75,7 +75,7 @@ public: void set_direction(TextServer::Direction p_direction); TextServer::Direction get_direction() const; - void set_bidi_override(const Vector<Vector2i> &p_override); + void set_bidi_override(const Array &p_override); void set_orientation(TextServer::Orientation p_orientation); TextServer::Orientation get_orientation() const; @@ -95,8 +95,8 @@ public: void tab_align(const Vector<float> &p_tab_stops); - void set_flags(uint8_t p_flags); - uint8_t get_flags() const; + void set_flags(uint16_t p_flags); + uint16_t get_flags() const; void set_text_overrun_behavior(OverrunBehavior p_behavior); OverrunBehavior get_text_overrun_behavior() const; @@ -120,8 +120,6 @@ public: int hit_test(float p_coords) const; - void _set_bidi_override(const Array &p_override); - TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextLine(); ~TextLine(); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 62949b9b98..b2e18e2451 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -53,7 +53,7 @@ void TextParagraph::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control"); - ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::_set_bidi_override); + ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::set_bidi_override); ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL("")); ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); @@ -158,9 +158,9 @@ void TextParagraph::_shape_lines() { if (h_offset > 0) { // Dropcap, flow around. - Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags); - for (int i = 0; i < line_breaks.size(); i++) { - RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags); + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x; if (v_offset < h) { TS->free(line); @@ -171,21 +171,21 @@ void TextParagraph::_shape_lines() { } dropcap_lines++; v_offset -= h; - start = line_breaks[i].y; + start = line_breaks[i + 1]; lines_rid.push_back(line); } } // Use fixed for the rest of lines. - Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags); - for (int i = 0; i < line_breaks.size(); i++) { - RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags); + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); if (!tab_stops.is_empty()) { TS->shaped_text_tab_align(line, tab_stops); } lines_rid.push_back(line); } - uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; + uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; if (overrun_behavior != OVERRUN_NO_TRIMMING) { switch (overrun_behavior) { case OVERRUN_TRIM_WORD_ELLIPSIS: @@ -347,15 +347,7 @@ int TextParagraph::get_spacing_bottom() const { return spacing_bottom; } -void TextParagraph::_set_bidi_override(const Array &p_override) { - Vector<Vector2i> overrides; - for (int i = 0; i < p_override.size(); i++) { - overrides.push_back(p_override[i]); - } - set_bidi_override(overrides); -} - -void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) { +void TextParagraph::set_bidi_override(const Array &p_override) { TS->shaped_text_set_bidi_override(rid, p_override); lines_dirty = true; } @@ -392,14 +384,14 @@ void TextParagraph::tab_align(const Vector<float> &p_tab_stops) { lines_dirty = true; } -void TextParagraph::set_flags(uint8_t p_flags) { +void TextParagraph::set_flags(uint16_t p_flags) { if (flags != p_flags) { flags = p_flags; lines_dirty = true; } } -uint8_t TextParagraph::get_flags() const { +uint16_t TextParagraph::get_flags() const { return flags; } diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index ee7bbab9c5..69c50559df 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -63,7 +63,7 @@ private: float width = -1.0; int max_lines_visible = -1; - uint8_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + uint16_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; OverrunBehavior overrun_behavior = OVERRUN_NO_TRIMMING; HAlign align = HALIGN_LEFT; @@ -94,7 +94,7 @@ public: void set_preserve_control(bool p_enabled); bool get_preserve_control() const; - void set_bidi_override(const Vector<Vector2i> &p_override); + void set_bidi_override(const Array &p_override); bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); void clear_dropcap(); @@ -108,8 +108,8 @@ public: void tab_align(const Vector<float> &p_tab_stops); - void set_flags(uint8_t p_flags); - uint8_t get_flags() const; + void set_flags(uint16_t p_flags); + uint16_t get_flags() const; void set_text_overrun_behavior(OverrunBehavior p_behavior); OverrunBehavior get_text_overrun_behavior() const; @@ -153,8 +153,6 @@ public: int hit_test(const Point2 &p_coords) const; - void _set_bidi_override(const Array &p_override); - TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextParagraph(); ~TextParagraph(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 80cab99373..1b3a7a5f2a 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1249,7 +1249,7 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { } Ref<Image> AtlasTexture::get_image() const { - if (!atlas.is_valid()) { + if (!atlas.is_valid() || !atlas->get_image().is_valid()) { return Ref<Image>(); } diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index e49d883ba4..65cdc1e24e 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -29,270 +29,20 @@ /*************************************************************************/ #include "theme.h" -#include "core/io/file_access.h" #include "core/string/print_string.h" -void Theme::_emit_theme_changed() { - if (no_change_propagation) { - return; - } - - notify_property_list_changed(); - emit_changed(); -} - -Vector<String> Theme::_get_icon_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_icon_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_icon_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_icon_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_stylebox_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_stylebox_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_stylebox_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_font_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_font_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_font_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_font_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_font_size_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_font_size_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_font_size_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_color_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_color_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_color_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_color_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_constant_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_constant_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_constant_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_constant_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const { - switch (p_data_type) { - case DATA_TYPE_COLOR: - return _get_color_list(p_theme_type); - case DATA_TYPE_CONSTANT: - return _get_constant_list(p_theme_type); - case DATA_TYPE_FONT: - return _get_font_list(p_theme_type); - case DATA_TYPE_FONT_SIZE: - return _get_font_size_list(p_theme_type); - case DATA_TYPE_ICON: - return _get_icon_list(p_theme_type); - case DATA_TYPE_STYLEBOX: - return _get_stylebox_list(p_theme_type); - case DATA_TYPE_MAX: - break; // Can't happen, but silences warning. - } - - return Vector<String>(); -} - -Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const { - switch (p_data_type) { - case DATA_TYPE_COLOR: - return _get_color_type_list(); - case DATA_TYPE_CONSTANT: - return _get_constant_type_list(); - case DATA_TYPE_FONT: - return _get_font_type_list(); - case DATA_TYPE_FONT_SIZE: - return _get_font_size_type_list(); - case DATA_TYPE_ICON: - return _get_icon_type_list(); - case DATA_TYPE_STYLEBOX: - return _get_stylebox_type_list(); - case DATA_TYPE_MAX: - break; // Can't happen, but silences warning. - } - - return Vector<String>(); -} - -Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_type_variation_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_type_list(&il); - ilret.resize(il.size()); +// Universal Theme resources used when no other theme has the item. +Ref<Theme> Theme::default_theme; +Ref<Theme> Theme::project_default_theme; - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} +// Universal default values, final fallback for every theme. +float Theme::default_base_scale = 1.0; +Ref<Texture2D> Theme::default_icon; +Ref<StyleBox> Theme::default_style; +Ref<Font> Theme::default_font; +int Theme::default_font_size = 16; +// Dynamic properties. bool Theme::_set(const StringName &p_name, const Variant &p_value) { String sname = p_name; @@ -452,6 +202,63 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { } } +// Universal fallback Theme resources. +Ref<Theme> Theme::get_default() { + return default_theme; +} + +void Theme::set_default(const Ref<Theme> &p_default) { + default_theme = p_default; +} + +Ref<Theme> Theme::get_project_default() { + return project_default_theme; +} + +void Theme::set_project_default(const Ref<Theme> &p_project_default) { + project_default_theme = p_project_default; +} + +// Universal fallback values for theme item types. +void Theme::set_default_base_scale(float p_base_scale) { + default_base_scale = p_base_scale; +} + +void Theme::set_default_icon(const Ref<Texture2D> &p_icon) { + default_icon = p_icon; +} + +void Theme::set_default_style(const Ref<StyleBox> &p_style) { + default_style = p_style; +} + +void Theme::set_default_font(const Ref<Font> &p_font) { + default_font = p_font; +} + +void Theme::set_default_font_size(int p_font_size) { + default_font_size = p_font_size; +} + +// Fallback values for theme item types, configurable per theme. +void Theme::set_default_theme_base_scale(float p_base_scale) { + if (default_theme_base_scale == p_base_scale) { + return; + } + + default_theme_base_scale = p_base_scale; + + _emit_theme_changed(); +} + +float Theme::get_default_theme_base_scale() const { + return default_theme_base_scale; +} + +bool Theme::has_default_theme_base_scale() const { + return default_theme_base_scale > 0.0; +} + void Theme::set_default_theme_font(const Ref<Font> &p_default_font) { if (default_theme_font == p_default_font) { return; @@ -464,7 +271,7 @@ void Theme::set_default_theme_font(const Ref<Font> &p_default_font) { default_theme_font = p_default_font; if (default_theme_font.is_valid()) { - default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(); @@ -474,6 +281,10 @@ Ref<Font> Theme::get_default_theme_font() const { return default_theme_font; } +bool Theme::has_default_theme_font() const { + return default_theme_font.is_valid(); +} + void Theme::set_default_theme_font_size(int p_font_size) { if (default_theme_font_size == p_font_size) { return; @@ -488,57 +299,25 @@ int Theme::get_default_theme_font_size() const { return default_theme_font_size; } -Ref<Theme> Theme::project_default_theme; -Ref<Theme> Theme::default_theme; -Ref<Texture2D> Theme::default_icon; -Ref<StyleBox> Theme::default_style; -Ref<Font> Theme::default_font; -int Theme::default_font_size = 16; - -Ref<Theme> Theme::get_default() { - return default_theme; -} - -void Theme::set_default(const Ref<Theme> &p_default) { - default_theme = p_default; -} - -Ref<Theme> Theme::get_project_default() { - return project_default_theme; -} - -void Theme::set_project_default(const Ref<Theme> &p_project_default) { - project_default_theme = p_project_default; -} - -void Theme::set_default_icon(const Ref<Texture2D> &p_icon) { - default_icon = p_icon; -} - -void Theme::set_default_style(const Ref<StyleBox> &p_style) { - default_style = p_style; -} - -void Theme::set_default_font(const Ref<Font> &p_font) { - default_font = p_font; -} - -void Theme::set_default_font_size(int p_font_size) { - default_font_size = p_font_size; +bool Theme::has_default_theme_font_size() const { + return default_theme_font_size > 0; } +// Icons. void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) { + bool existing = false; if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { + existing = true; icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } icon_map[p_theme_type][p_name] = p_icon; if (p_icon.is_valid()) { - icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); } - _emit_theme_changed(); + _emit_theme_changed(!existing); } Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const { @@ -565,7 +344,7 @@ void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name]; icon_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) { @@ -578,7 +357,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) icon_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -611,18 +390,21 @@ void Theme::get_icon_type_list(List<StringName> *p_list) const { } } +// Styleboxes. void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) { + bool existing = false; if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { + existing = true; style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } style_map[p_theme_type][p_name] = p_style; if (p_style.is_valid()) { - style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); } - _emit_theme_changed(); + _emit_theme_changed(!existing); } Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const { @@ -649,7 +431,7 @@ void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_na style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name]; style_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) { @@ -662,7 +444,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t style_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -695,24 +477,27 @@ void Theme::get_stylebox_type_list(List<StringName> *p_list) const { } } +// Fonts. void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) { + bool existing = false; if (font_map[p_theme_type][p_name].is_valid()) { + existing = true; font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } font_map[p_theme_type][p_name] = p_font; if (p_font.is_valid()) { - font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); } - _emit_theme_changed(); + _emit_theme_changed(!existing); } Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const { if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) { return font_map[p_theme_type][p_name]; - } else if (default_theme_font.is_valid()) { + } else if (has_default_theme_font()) { return default_theme_font; } else { return default_font; @@ -720,7 +505,7 @@ Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_ty } bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const { - return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || default_theme_font.is_valid()); + return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || has_default_theme_font()); } bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const { @@ -735,7 +520,7 @@ void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name]; font_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) { @@ -748,7 +533,7 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) font_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -781,16 +566,18 @@ void Theme::get_font_type_list(List<StringName> *p_list) const { } } +// Font sizes. void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) { + bool existing = has_font_size_nocheck(p_name, p_theme_type); font_size_map[p_theme_type][p_name] = p_font_size; - _emit_theme_changed(); + _emit_theme_changed(!existing); } int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const { if (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) { return font_size_map[p_theme_type][p_name]; - } else if (default_theme_font_size > 0) { + } else if (has_default_theme_font_size()) { return default_theme_font_size; } else { return default_font_size; @@ -798,7 +585,7 @@ int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_typ } bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const { - return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || (default_theme_font_size > 0)); + return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || has_default_theme_font_size()); } bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const { @@ -813,7 +600,7 @@ void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_n font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name]; font_size_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) { @@ -822,7 +609,7 @@ void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_ font_size_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -855,10 +642,12 @@ void Theme::get_font_size_type_list(List<StringName> *p_list) const { } } +// Colors. void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) { + bool existing = has_color_nocheck(p_name, p_theme_type); color_map[p_theme_type][p_name] = p_color; - _emit_theme_changed(); + _emit_theme_changed(!existing); } Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const { @@ -885,7 +674,7 @@ void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name]; color_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) { @@ -894,7 +683,7 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type color_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -927,10 +716,12 @@ void Theme::get_color_type_list(List<StringName> *p_list) const { } } +// Theme constants. void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) { + bool existing = has_constant_nocheck(p_name, p_theme_type); constant_map[p_theme_type][p_name] = p_constant; - _emit_theme_changed(); + _emit_theme_changed(!existing); } int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const { @@ -957,7 +748,7 @@ void Theme::rename_constant(const StringName &p_old_name, const StringName &p_na constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name]; constant_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) { @@ -966,7 +757,7 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_t constant_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -999,6 +790,7 @@ void Theme::get_constant_type_list(List<StringName> *p_list) const { } } +// Generic methods for managing theme items. void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) { switch (p_data_type) { case DATA_TYPE_COLOR: { @@ -1230,6 +1022,7 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l } } +// Theme type variations. void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) { ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type."); ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type."); @@ -1243,7 +1036,7 @@ void Theme::set_type_variation(const StringName &p_theme_type, const StringName variation_map[p_theme_type] = p_base_type; variation_base_map[p_base_type].push_back(p_theme_type); - _emit_theme_changed(); + _emit_theme_changed(true); } bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const { @@ -1257,7 +1050,7 @@ void Theme::clear_type_variation(const StringName &p_theme_type) { variation_base_map[base_type].erase(p_theme_type); variation_map.erase(p_theme_type); - _emit_theme_changed(); + _emit_theme_changed(true); } StringName Theme::get_type_variation_base(const StringName &p_theme_type) const { @@ -1287,67 +1080,355 @@ void Theme::get_type_variation_list(const StringName &p_base_type, List<StringNa } } -void Theme::_freeze_change_propagation() { - no_change_propagation = true; -} +// Theme types. +void Theme::get_type_list(List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); -void Theme::_unfreeze_and_propagate_changes() { - no_change_propagation = false; - _emit_theme_changed(); + Set<StringName> types; + const StringName *key = nullptr; + + // Icons. + while ((key = icon_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // StyleBoxes. + while ((key = style_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Fonts. + while ((key = font_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Font sizes. + while ((key = font_size_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Colors. + while ((key = color_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Constants. + while ((key = constant_map.next(key))) { + types.insert(*key); + } + + for (Set<StringName>::Element *E = types.front(); E; E = E->next()) { + p_list->push_back(E->get()); + } } -void Theme::clear() { - // These items need disconnecting. - { - const StringName *K = nullptr; - while ((K = icon_map.next(K))) { - const StringName *L = nullptr; - while ((L = icon_map[*K].next(L))) { - Ref<Texture2D> icon = icon_map[*K][*L]; - if (icon.is_valid()) { - icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); - } +void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) { + ERR_FAIL_NULL(p_list); + + // Build the dependency chain for type variations. + if (p_type_variation != StringName()) { + StringName variation_name = p_type_variation; + while (variation_name != StringName()) { + p_list->push_back(variation_name); + variation_name = get_type_variation_base(variation_name); + + // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme). + if (variation_name == p_base_type) { + break; } } } - { - const StringName *K = nullptr; - while ((K = style_map.next(K))) { - const StringName *L = nullptr; - while ((L = style_map[*K].next(L))) { - Ref<StyleBox> style = style_map[*K][*L]; - if (style.is_valid()) { - style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); - } - } - } + // Continue building the chain using native class hierarchy. + StringName class_name = p_base_type; + while (class_name != StringName()) { + p_list->push_back(class_name); + class_name = ClassDB::get_parent_class_nocheck(class_name); } +} - { - const StringName *K = nullptr; - while ((K = font_map.next(K))) { - const StringName *L = nullptr; - while ((L = font_map[*K].next(L))) { - Ref<Font> font = font_map[*K][*L]; - if (font.is_valid()) { - font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); - } - } - } +// Internal methods for getting lists as a Vector of String (compatible with public API). +Vector<String> Theme::_get_icon_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_icon_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); } + return ilret; +} - icon_map.clear(); - style_map.clear(); - font_map.clear(); - font_size_map.clear(); - color_map.clear(); - constant_map.clear(); +Vector<String> Theme::_get_icon_type_list() const { + Vector<String> ilret; + List<StringName> il; - variation_map.clear(); - variation_base_map.clear(); + get_icon_type_list(&il); + ilret.resize(il.size()); - _emit_theme_changed(); + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_stylebox_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_stylebox_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_stylebox_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_font_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_font_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_font_size_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_size_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_font_size_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_color_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_color_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_color_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_color_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_constant_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_constant_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_constant_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_constant_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return _get_color_list(p_theme_type); + case DATA_TYPE_CONSTANT: + return _get_constant_list(p_theme_type); + case DATA_TYPE_FONT: + return _get_font_list(p_theme_type); + case DATA_TYPE_FONT_SIZE: + return _get_font_size_list(p_theme_type); + case DATA_TYPE_ICON: + return _get_icon_list(p_theme_type); + case DATA_TYPE_STYLEBOX: + return _get_stylebox_list(p_theme_type); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return Vector<String>(); +} + +Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return _get_color_type_list(); + case DATA_TYPE_CONSTANT: + return _get_constant_type_list(); + case DATA_TYPE_FONT: + return _get_font_type_list(); + case DATA_TYPE_FONT_SIZE: + return _get_font_size_type_list(); + case DATA_TYPE_ICON: + return _get_icon_type_list(); + case DATA_TYPE_STYLEBOX: + return _get_stylebox_type_list(); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return Vector<String>(); +} + +Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_type_variation_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +// Theme bulk manipulations. +void Theme::_emit_theme_changed(bool p_notify_list_changed) { + if (no_change_propagation) { + return; + } + + if (p_notify_list_changed) { + notify_property_list_changed(); + } + emit_changed(); +} + +void Theme::_freeze_change_propagation() { + no_change_propagation = true; +} + +void Theme::_unfreeze_and_propagate_changes() { + no_change_propagation = false; + _emit_theme_changed(true); } void Theme::merge_with(const Ref<Theme> &p_other) { @@ -1434,80 +1515,58 @@ void Theme::merge_with(const Ref<Theme> &p_other) { _unfreeze_and_propagate_changes(); } -void Theme::get_type_list(List<StringName> *p_list) const { - ERR_FAIL_NULL(p_list); - - Set<StringName> types; - const StringName *key = nullptr; - - // Icons. - while ((key = icon_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // StyleBoxes. - while ((key = style_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Fonts. - while ((key = font_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Font sizes. - while ((key = font_size_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Colors. - while ((key = color_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Constants. - while ((key = constant_map.next(key))) { - types.insert(*key); +void Theme::clear() { + // These items need disconnecting. + { + const StringName *K = nullptr; + while ((K = icon_map.next(K))) { + const StringName *L = nullptr; + while ((L = icon_map[*K].next(L))) { + Ref<Texture2D> icon = icon_map[*K][*L]; + if (icon.is_valid()) { + icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + } + } + } } - for (Set<StringName>::Element *E = types.front(); E; E = E->next()) { - p_list->push_back(E->get()); + { + const StringName *K = nullptr; + while ((K = style_map.next(K))) { + const StringName *L = nullptr; + while ((L = style_map[*K].next(L))) { + Ref<StyleBox> style = style_map[*K][*L]; + if (style.is_valid()) { + style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + } + } + } } -} -void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) { - ERR_FAIL_NULL(p_list); - - // Build the dependency chain for type variations. - if (p_type_variation != StringName()) { - StringName variation_name = p_type_variation; - while (variation_name != StringName()) { - p_list->push_back(variation_name); - variation_name = get_type_variation_base(variation_name); - - // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme). - if (variation_name == p_base_type) { - break; + { + const StringName *K = nullptr; + while ((K = font_map.next(K))) { + const StringName *L = nullptr; + while ((L = font_map[*K].next(L))) { + Ref<Font> font = font_map[*K][*L]; + if (font.is_valid()) { + font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + } } } } - // Continue building the chain using native class hierarchy. - StringName class_name = p_base_type; - while (class_name != StringName()) { - p_list->push_back(class_name); - class_name = ClassDB::get_parent_class_nocheck(class_name); - } + icon_map.clear(); + style_map.clear(); + font_map.clear(); + font_size_map.clear(); + color_map.clear(); + constant_map.clear(); + + variation_map.clear(); + variation_base_map.clear(); + + _emit_theme_changed(true); } void Theme::reset_state() { @@ -1563,11 +1622,17 @@ void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list); ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list); + ClassDB::bind_method(D_METHOD("set_default_base_scale", "font_size"), &Theme::set_default_theme_base_scale); + ClassDB::bind_method(D_METHOD("get_default_base_scale"), &Theme::get_default_theme_base_scale); + ClassDB::bind_method(D_METHOD("has_default_base_scale"), &Theme::has_default_theme_base_scale); + ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font); ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font); + ClassDB::bind_method(D_METHOD("has_default_font"), &Theme::has_default_theme_font); ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size); ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size); + ClassDB::bind_method(D_METHOD("has_default_font_size"), &Theme::has_default_theme_font_size); ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item); ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item); @@ -1588,6 +1653,7 @@ void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with); ClassDB::bind_method(D_METHOD("clear"), &Theme::clear); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_default_base_scale", "get_default_base_scale"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font"); ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size"); diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 15f21b91b8..d170d53ae3 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -61,7 +61,7 @@ public: private: bool no_change_propagation = false; - void _emit_theme_changed(); + void _emit_theme_changed(bool p_notify_list_changed = false); HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map; HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map; @@ -96,13 +96,19 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - static Ref<Theme> project_default_theme; + // Universal Theme resources used when no other theme has the item. static Ref<Theme> default_theme; + static Ref<Theme> project_default_theme; + + // Universal default values, final fallback for every theme. + static float default_base_scale; static Ref<Texture2D> default_icon; static Ref<StyleBox> default_style; static Ref<Font> default_font; static int default_font_size; + // Default values configurable for each individual theme. + float default_theme_base_scale = 0.0; Ref<Font> default_theme_font; int default_theme_font_size = -1; @@ -120,16 +126,23 @@ public: static Ref<Theme> get_project_default(); static void set_project_default(const Ref<Theme> &p_project_default); + static void set_default_base_scale(float p_base_scale); static void set_default_icon(const Ref<Texture2D> &p_icon); static void set_default_style(const Ref<StyleBox> &p_style); static void set_default_font(const Ref<Font> &p_font); static void set_default_font_size(int p_font_size); + void set_default_theme_base_scale(float p_base_scale); + float get_default_theme_base_scale() const; + bool has_default_theme_base_scale() const; + void set_default_theme_font(const Ref<Font> &p_default_font); Ref<Font> get_default_theme_font() const; + bool has_default_theme_font() const; void set_default_theme_font_size(int p_font_size); int get_default_theme_font_size() const; + bool has_default_theme_font_size() const; void set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_theme_type) const; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 5a8c5b3782..40eedb63c1 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -66,8 +66,8 @@ const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { void TileSet::set_tile_shape(TileSet::TileShape p_shape) { tile_shape = p_shape; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } terrain_bits_meshes_dirty = true; @@ -89,8 +89,8 @@ TileSet::TileLayout TileSet::get_tile_layout() const { void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) { tile_offset_axis = p_alignment; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } terrain_bits_meshes_dirty = true; @@ -386,8 +386,8 @@ void TileSet::remove_terrain_set(int p_index) { void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) { ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); terrain_sets.write[p_terrain_set].mode = p_terrain_mode; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } notify_property_list_changed(); @@ -719,8 +719,8 @@ void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) { ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size()); custom_data_layers.write[p_layer_id].type = p_value; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } emit_changed(); @@ -858,10 +858,10 @@ void TileSet::remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_ Array TileSet::get_source_level_tile_proxies() const { Array output; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<int, int> &E : source_level_proxies) { Array proxy; - proxy.push_back(E->key()); - proxy.push_back(E->get()); + proxy.push_back(E.key); + proxy.push_back(E.value); output.push_back(proxy); } return output; @@ -869,10 +869,10 @@ Array TileSet::get_source_level_tile_proxies() const { Array TileSet::get_coords_level_tile_proxies() const { Array output; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<Array, Array> &E : coords_level_proxies) { Array proxy; - proxy.append_array(E->key()); - proxy.append_array(E->get()); + proxy.append_array(E.key); + proxy.append_array(E.value); output.push_back(proxy); } return output; @@ -880,10 +880,10 @@ Array TileSet::get_coords_level_tile_proxies() const { Array TileSet::get_alternative_level_tile_proxies() const { Array output; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { Array proxy; - proxy.append_array(E->key()); - proxy.append_array(E->get()); + proxy.append_array(E.key); + proxy.append_array(E.value); output.push_back(proxy); } return output; @@ -935,9 +935,9 @@ Array TileSet::map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_a void TileSet::cleanup_invalid_tile_proxies() { // Source level. Vector<int> source_to_remove; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { - if (has_source(E->key())) { - source_to_remove.append(E->key()); + for (const KeyValue<int, int> &E : source_level_proxies) { + if (has_source(E.key)) { + source_to_remove.append(E.key); } } for (int i = 0; i < source_to_remove.size(); i++) { @@ -946,8 +946,8 @@ void TileSet::cleanup_invalid_tile_proxies() { // Coords level. Vector<Array> coords_to_remove; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { - Array a = E->key(); + for (const KeyValue<Array, Array> &E : coords_level_proxies) { + Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) { coords_to_remove.append(a); } @@ -959,8 +959,8 @@ void TileSet::cleanup_invalid_tile_proxies() { // Alternative level. Vector<Array> alternative_to_remove; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { - Array a = E->key(); + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { + Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) { alternative_to_remove.append(a); } @@ -1911,8 +1911,8 @@ const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1; #ifndef DISABLE_DEPRECATED void TileSet::_compatibility_conversion() { - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - CompatibilityTileData *ctd = E->value(); + for (KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + CompatibilityTileData *ctd = E.value; // Add the texture TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource); @@ -1950,11 +1950,11 @@ void TileSet::_compatibility_conversion() { value_array.push_back(coords); value_array.push_back(alternative_tile); - if (!compatibility_tilemap_mapping.has(E->key())) { - compatibility_tilemap_mapping[E->key()] = Map<Array, Array>(); + if (!compatibility_tilemap_mapping.has(E.key)) { + compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); } - compatibility_tilemap_mapping[E->key()][key_array] = value_array; - compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; + compatibility_tilemap_mapping[E.key][key_array] = value_array; + compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); @@ -2042,11 +2042,11 @@ void TileSet::_compatibility_conversion() { value_array.push_back(coords); value_array.push_back(alternative_tile); - if (!compatibility_tilemap_mapping.has(E->key())) { - compatibility_tilemap_mapping[E->key()] = Map<Array, Array>(); + if (!compatibility_tilemap_mapping.has(E.key)) { + compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); } - compatibility_tilemap_mapping[E->key()][key_array] = value_array; - compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; + compatibility_tilemap_mapping[E.key][key_array] = value_array; + compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); @@ -2128,8 +2128,8 @@ void TileSet::_compatibility_conversion() { } // Reset compatibility data - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - memdelete(E->get()); + for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + memdelete(E.value); } compatibility_data = Map<int, CompatibilityTileData *>(); } @@ -2579,25 +2579,25 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { } else if (components.size() == 2 && components[0] == "tile_proxies") { if (components[1] == "source_level") { Array a; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<int, int> &E : source_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; } else if (components[1] == "coords_level") { Array a; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<Array, Array> &E : coords_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; } else if (components[1] == "alternative_level") { Array a; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; @@ -2673,8 +2673,8 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { // Sources. // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first. - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + for (const KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } // Tile Proxies. @@ -2850,8 +2850,8 @@ TileSet::TileSet() { TileSet::~TileSet() { #ifndef DISABLE_DEPRECATED - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - memdelete(E->get()); + for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + memdelete(E.value); } #endif // DISABLE_DEPRECATED while (!source_ids.is_empty()) { @@ -2883,18 +2883,18 @@ void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; // Set the TileSet on all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->set_tile_set(tile_set); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->set_tile_set(tile_set); } } } void TileSetAtlasSource::notify_tile_data_properties_should_change() { // Set the TileSet on all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->notify_tile_data_properties_should_change(); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->notify_tile_data_properties_should_change(); } } } @@ -3045,9 +3045,9 @@ void TileSetAtlasSource::remove_custom_data_layer(int p_index) { void TileSetAtlasSource::reset_state() { // Reset all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->reset_state(); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->reset_state(); } } } @@ -3124,7 +3124,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const { } bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> components = String(p_name).split("/", true, 3); + Vector<String> components = String(p_name).split("/", true, 2); // Compute the vector2i if we have coordinates. Vector<String> coords_split = components[0].split(":"); @@ -3260,40 +3260,40 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { // Atlases data. PropertyInfo property_info; - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (const KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { List<PropertyInfo> tile_property_list; // size_in_atlas property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().size_in_atlas == Vector2i(1, 1)) { + if (E_tile.value.size_in_atlas == Vector2i(1, 1)) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // next_alternative_id property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().next_alternative_id == 1) { + if (E_tile.value.next_alternative_id == 1) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // animation_columns. property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().animation_columns == 0) { + if (E_tile.value.animation_columns == 0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // animation_separation. property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().animation_separation == Vector2i()) { + if (E_tile.value.animation_separation == Vector2i()) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // animation_speed. property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().animation_speed == 1.0) { + if (E_tile.value.animation_speed == 1.0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); @@ -3302,8 +3302,8 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK)); // animation_frame_*. - bool store_durations = tiles[E_tile->key()].animation_frames_durations.size() >= 2; - for (int i = 0; i < (int)tiles[E_tile->key()].animation_frames_durations.size(); i++) { + bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2; + for (int i = 0; i < (int)tiles[E_tile.key].animation_frames_durations.size(); i++) { property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); if (!store_durations) { property_info.usage ^= PROPERTY_USAGE_STORAGE; @@ -3311,27 +3311,27 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { tile_property_list.push_back(property_info); } - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { // Add a dummy property to show the alternative exists. - tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); // Get the alternative tile's properties and append them to the list of properties. List<PropertyInfo> alternative_property_list; - E_alternative->get()->get_property_list(&alternative_property_list); + E_alternative.value->get_property_list(&alternative_property_list); for (PropertyInfo &alternative_property_info : alternative_property_list) { Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name); - Variant value = E_alternative->get()->get(alternative_property_info.name); + Variant value = E_alternative.value->get(alternative_property_info.name); if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE; } - alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name); + alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative.key), alternative_property_info.name); tile_property_list.push_back(alternative_property_info); } } // Add all alternative. for (PropertyInfo &tile_property_info : tile_property_list) { - tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name); + tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile.key.x, E_tile.key.y), tile_property_info.name); p_list->push_back(tile_property_info); } } @@ -3373,8 +3373,8 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { _clear_coords_mapping_cache(p_atlas_coords); // Free tile data. - for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { - memdelete(E_tile_data->get()); + for (const KeyValue<int, TileData *> &E_tile_data : tiles[p_atlas_coords].alternatives) { + memdelete(E_tile_data.value); } // Delete the tile @@ -3619,8 +3619,8 @@ bool TileSetAtlasSource::has_tiles_outside_texture() { Vector2i grid_size = get_atlas_grid_size(); Vector<Vector2i> to_remove; - for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) { - if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { + for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { + if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) { return true; } } @@ -3632,9 +3632,9 @@ void TileSetAtlasSource::clear_tiles_outside_texture() { Vector2i grid_size = get_atlas_grid_size(); Vector<Vector2i> to_remove; - for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) { - if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { - to_remove.append(E->key()); + for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { + if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) { + to_remove.append(E.key); } } @@ -3775,9 +3775,9 @@ void TileSetAtlasSource::_bind_methods() { TileSetAtlasSource::~TileSetAtlasSource() { // Free everything needed. - for (Map<Vector2i, TileAlternativesData>::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) { - for (Map<int, TileData *>::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { - memdelete(E_tile_data->get()); + for (KeyValue<Vector2i, TileAlternativesData> &E_alternatives : tiles) { + for (KeyValue<int, TileData *> &E_tile_data : E_alternatives.value.alternatives) { + memdelete(E_tile_data.value); } } } @@ -4316,9 +4316,26 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const { } // Physics -int TileData::get_collision_polygons_count(int p_layer_id) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); - return physics[p_layer_id].polygons.size(); +void TileData::set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + physics.write[p_layer_id].linear_velocity = p_velocity; + emit_signal(SNAME("changed")); +} + +Vector2 TileData::get_constant_linear_velocity(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector2()); + return physics[p_layer_id].linear_velocity; +} + +void TileData::set_constant_angular_velocity(int p_layer_id, real_t p_velocity) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + physics.write[p_layer_id].angular_velocity = p_velocity; + emit_signal(SNAME("changed")); +} + +real_t TileData::get_constant_angular_velocity(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); + return physics[p_layer_id].angular_velocity; } void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) { @@ -4329,6 +4346,11 @@ void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count emit_signal(SNAME("changed")); } +int TileData::get_collision_polygons_count(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + return physics[p_layer_id].polygons.size(); +} + void TileData::add_collision_polygon(int p_layer_id) { ERR_FAIL_INDEX(p_layer_id, physics.size()); physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData()); @@ -4530,11 +4552,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { // Physics layers. int layer_index = components[0].trim_prefix("physics_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); - if (components.size() == 2 && components[1] == "polygons_count") { - if (p_value.get_type() != Variant::INT) { - return false; - } - + if (components.size() == 2) { if (layer_index >= physics.size()) { if (tile_set) { return false; @@ -4542,8 +4560,19 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { physics.resize(layer_index + 1); } } - set_collision_polygons_count(layer_index, p_value); - return true; + if (components[1] == "linear_velocity") { + set_constant_linear_velocity(layer_index, p_value); + return true; + } else if (components[1] == "angular_velocity") { + set_constant_angular_velocity(layer_index, p_value); + return true; + } else if (components[1] == "polygons_count") { + if (p_value.get_type() != Variant::INT) { + return false; + } + set_collision_polygons_count(layer_index, p_value); + return true; + } } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { int polygon_index = components[1].trim_prefix("polygon_").to_int(); ERR_FAIL_COND_V(polygon_index < 0, false); @@ -4645,9 +4674,18 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { if (layer_index >= physics.size()) { return false; } - if (components.size() == 2 && components[1] == "polygons_count") { - r_ret = get_collision_polygons_count(layer_index); - return true; + + if (components.size() == 2) { + if (components[1] == "linear_velocity") { + r_ret = get_constant_linear_velocity(layer_index); + return true; + } else if (components[1] == "angular_velocity") { + r_ret = get_constant_angular_velocity(layer_index); + return true; + } else if (components[1] == "polygons_count") { + r_ret = get_collision_polygons_count(layer_index); + return true; + } } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { int polygon_index = components[1].trim_prefix("polygon_").to_int(); ERR_FAIL_COND_V(polygon_index < 0, false); @@ -4718,6 +4756,8 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Physics layers. p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < physics.size(); i++) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, vformat("physics_layer_%d/linear_velocity", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/angular_velocity", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); for (int j = 0; j < physics[i].polygons.size(); j++) { @@ -4807,8 +4847,12 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); // Physics. - ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); + ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("get_constant_linear_velocity", "layer_id"), &TileData::get_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "layer_id", "velocity"), &TileData::set_constant_angular_velocity); + ClassDB::bind_method(D_METHOD("get_constant_angular_velocity", "layer_id"), &TileData::get_constant_angular_velocity); ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count); + ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon); ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon); ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 46cb1fb36d..2b4929b3df 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -645,6 +645,8 @@ private: float one_way_margin = 1.0; }; + Vector2 linear_velocity; + float angular_velocity = 0.0; Vector<PolygonShapeTileData> polygons; }; Vector<PhysicsLayerTileData> physics; @@ -718,8 +720,12 @@ public: Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const; // Physics - int get_collision_polygons_count(int p_layer_id) const; + void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity); + Vector2 get_constant_linear_velocity(int p_layer_id) const; + void set_constant_angular_velocity(int p_layer_id, real_t p_velocity); + real_t get_constant_angular_velocity(int p_layer_id) const; void set_collision_polygons_count(int p_layer_id, int p_shapes_count); + int get_collision_polygons_count(int p_layer_id) const; void add_collision_polygon(int p_layer_id); void remove_collision_polygon(int p_layer_id, int p_polygon_index); void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index e8fe3ff3cd..934d16bd7e 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -197,9 +197,9 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const { Array VisualShaderNode::get_default_input_values() const { Array ret; - for (Map<int, Variant>::Element *E = default_input_values.front(); E; E = E->next()) { - ret.push_back(E->key()); - ret.push_back(E->get()); + for (const KeyValue<int, Variant> &E : default_input_values) { + ret.push_back(E.key); + ret.push_back(E.value); } return ret; } @@ -460,14 +460,14 @@ Dictionary VisualShader::get_engine_version() const { void VisualShader::update_engine_version(const Dictionary &p_new_version) { if (engine_version.is_empty()) { // before 4.0 for (int i = 0; i < TYPE_MAX; i++) { - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E->get().node.ptr()); + for (KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E.value.node.ptr()); if (input.is_valid()) { if (input->get_input_name() == "side") { input->set_input_name("front_facing"); } } - Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()); + Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()); if (expression.is_valid()) { for (int j = 0; j < expression->get_input_port_count(); j++) { int type = expression->get_input_port_type(j); @@ -484,7 +484,7 @@ void VisualShader::update_engine_version(const Dictionary &p_new_version) { expression->set_output_port_type(j, type); } } - Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E->get().node.ptr()); + Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E.value.node.ptr()); if (compare.is_valid()) { int ctype = int(compare->get_comparison_type()); if (int(ctype) > 0) { // + PORT_TYPE_SCALAR_INT @@ -561,8 +561,8 @@ Vector<int> VisualShader::get_node_list(Type p_type) const { const Graph *g = &graph[p_type]; Vector<int> ret; - for (Map<int, Node>::Element *E = g->nodes.front(); E; E = E->next()) { - ret.push_back(E->key()); + for (const KeyValue<int, Node> &E : g->nodes) { + ret.push_back(E.key); } return ret; @@ -575,9 +575,9 @@ int VisualShader::get_valid_node_id(Type p_type) const { } int VisualShader::find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const { - for (const Map<int, Node>::Element *E = graph[p_type].nodes.front(); E; E = E->next()) { - if (E->get().node == p_node) { - return E->key(); + for (const KeyValue<int, Node> &E : graph[p_type].nodes) { + if (E.value.node == p_node) { + return E.key; } } @@ -819,8 +819,8 @@ void VisualShader::set_mode(Mode p_mode) { flags.clear(); shader_mode = p_mode; for (int i = 0; i < TYPE_MAX; i++) { - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeInput> input = E->get().node; + for (KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeInput> input = E.value.node; if (input.is_valid()) { input->shader_mode = shader_mode; //input->input_index = 0; @@ -1041,8 +1041,8 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua while (true) { bool exists = false; for (int i = 0; i < TYPE_MAX; i++) { - for (const Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeUniform> node = E->get().node; + for (const KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeUniform> node = E.value.node; if (node == p_uniform) { //do not test on self continue; } @@ -1271,8 +1271,8 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { } } - for (Map<String, String>::Element *E = blend_mode_enums.front(); E; E = E->next()) { - p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E->key(), PROPERTY_HINT_ENUM, E->get())); + for (const KeyValue<String, String> &E : blend_mode_enums) { + p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E.key, PROPERTY_HINT_ENUM, E.value)); } for (Set<String>::Element *E = toggles.front(); E; E = E->next()) { @@ -1280,22 +1280,22 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { } for (int i = 0; i < TYPE_MAX; i++) { - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + for (const KeyValue<int, Node> &E : graph[i].nodes) { String prop_name = "nodes/"; prop_name += type_string[i]; - prop_name += "/" + itos(E->key()); + prop_name += "/" + itos(E.key); - if (E->key() != NODE_ID_OUTPUT) { + if (E.key != NODE_ID_OUTPUT) { p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - if (Object::cast_to<VisualShaderNodeGroupBase>(E->get().node.ptr()) != nullptr) { + if (Object::cast_to<VisualShaderNodeGroupBase>(E.value.node.ptr()) != nullptr) { p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } - if (Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()) != nullptr) { + if (Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()) != nullptr) { p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } } @@ -1716,9 +1716,9 @@ void VisualShader::_update_shader() const { emitters.insert(i, List<int>()); } - for (Map<int, Node>::Element *M = graph[i].nodes.front(); M; M = M->next()) { - if (M->get().node == emit_particle.ptr()) { - emitters[i].push_back(M->key()); + for (const KeyValue<int, Node> &M : graph[i].nodes) { + if (M.value.node == emit_particle.ptr()) { + emitters[i].push_back(M.key); break; } } |