diff options
Diffstat (limited to 'scene')
143 files changed, 5891 insertions, 3570 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 96a3134691..fad4784d51 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -94,7 +94,7 @@ Rect2 AnimatedSprite2D::_get_rect() const { Point2 ofs = offset; if (centered) { - ofs -= Size2(s) / 2; + ofs -= s / 2; } if (s == Size2(0, 0)) { @@ -228,8 +228,7 @@ void AnimatedSprite2D::_notification(int p_what) { RID ci = get_canvas_item(); - Size2i s; - s = texture->get_size(); + Size2 s = texture->get_size(); Point2 ofs = offset; if (centered) { ofs -= s / 2; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index e497ea48c4..fff9c47d4d 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/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index bddc342c1a..18a50ae098 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -68,7 +68,7 @@ void AudioStreamPlayer2D::_notification(int p_what) { active.set(); Ref<AudioStreamPlayback> new_playback = stream->instance_playback(); ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); - AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get()); + AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale); stream_playbacks.push_back(new_playback); setplay.set(-1); } 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/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 5bce705dd5..3637594e1b 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -163,8 +163,8 @@ void GPUParticles2D::set_trail_sections(int p_sections) { } void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) { - ERR_FAIL_COND(trail_section_subdivisions < 1); - ERR_FAIL_COND(trail_section_subdivisions > 1024); + ERR_FAIL_COND(p_subdivisions < 1); + ERR_FAIL_COND(p_subdivisions > 1024); trail_section_subdivisions = p_subdivisions; update(); diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joint_2d.cpp index 4a6606256e..8a528151cf 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" @@ -74,6 +74,8 @@ void Joint2D::_update_joint(bool p_only_free) { PhysicsBody2D *body_a = Object::cast_to<PhysicsBody2D>(node_a); PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b); + bool valid = false; + if (node_a && !body_a && node_b && !body_b) { warning = TTR("Node A and Node B must be PhysicsBody2Ds"); } else if (node_a && !body_a) { @@ -86,11 +88,12 @@ void Joint2D::_update_joint(bool p_only_free) { warning = TTR("Node A and Node B must be different PhysicsBody2Ds"); } else { warning = String(); + valid = true; } update_configuration_warnings(); - if (!warning.is_empty()) { + if (!valid) { PhysicsServer2D::get_singleton()->joint_clear(joint); return; } 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/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index 67e35cc7a3..797e2e59cb 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -123,12 +123,12 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s Point2 new_ofs = (screen_offset + (p_offset - screen_offset) * motion_scale) + motion_offset * p_scale + orig_offset * p_scale; if (mirroring.x) { - double den = mirroring.x * p_scale; + real_t den = mirroring.x * p_scale; new_ofs.x -= den * ceil(new_ofs.x / den); } if (mirroring.y) { - double den = mirroring.y * p_scale; + real_t den = mirroring.y * p_scale; new_ofs.y -= den * ceil(new_ofs.y / den); } diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index c4b2608812..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: { @@ -150,27 +152,15 @@ void PhysicalBone2D::_start_physics_simulation() { return; } - // Reset to Bone2D position + // Reset to Bone2D position. _position_at_bone2d(); - // Apply the layers and masks + // Apply the layers and masks. PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - // Apply the correct mode - RigidDynamicBody2D::Mode rigid_mode = get_mode(); - if (rigid_mode == RigidDynamicBody2D::MODE_STATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_KINEMATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC_LOCKED) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); - } else { - // Default to Dynamic. - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } + // Apply the correct mode. + _apply_body_mode(); _internal_simulate_physics = true; set_physics_process_internal(true); diff --git a/scene/2d/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 1ca5a9c0b6..41288d646f 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -34,8 +34,8 @@ #include "scene/scene_string_names.h" void PhysicsBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with); @@ -54,10 +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, 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(); @@ -71,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) { @@ -90,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; } } @@ -99,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. @@ -108,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); } @@ -124,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; @@ -133,7 +138,12 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result); } - return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r); + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + 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() { @@ -282,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: { @@ -307,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) { @@ -430,7 +445,7 @@ void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsD void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { set_block_transform_notify(true); // don't want notify (would feedback loop) - if (mode != MODE_KINEMATIC) { + if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) { set_global_transform(p_state->get_transform()); } @@ -451,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++; } } @@ -497,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++; } } @@ -524,29 +539,60 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) } } -void RigidDynamicBody2D::set_mode(Mode p_mode) { - mode = p_mode; - switch (p_mode) { - case MODE_DYNAMIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } break; - case MODE_STATIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); +void RigidDynamicBody2D::_apply_body_mode() { + if (freeze) { + switch (freeze_mode) { + case FREEZE_MODE_STATIC: { + set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); + } break; + case FREEZE_MODE_KINEMATIC: { + set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); + } break; + } + } else if (lock_rotation) { + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LINEAR); + } else { + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); + } +} - } break; - case MODE_KINEMATIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); +void RigidDynamicBody2D::set_lock_rotation_enabled(bool p_lock_rotation) { + if (p_lock_rotation == lock_rotation) { + return; + } - } break; - case MODE_DYNAMIC_LOCKED: { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); + lock_rotation = p_lock_rotation; + _apply_body_mode(); +} - } break; +bool RigidDynamicBody2D::is_lock_rotation_enabled() const { + return lock_rotation; +} + +void RigidDynamicBody2D::set_freeze_enabled(bool p_freeze) { + if (p_freeze == freeze) { + return; + } + + freeze = p_freeze; + _apply_body_mode(); +} + +bool RigidDynamicBody2D::is_freeze_enabled() const { + return freeze; +} + +void RigidDynamicBody2D::set_freeze_mode(FreezeMode p_freeze_mode) { + if (p_freeze_mode == freeze_mode) { + return; } + + freeze_mode = p_freeze_mode; + _apply_body_mode(); } -RigidDynamicBody2D::Mode RigidDynamicBody2D::get_mode() const { - return mode; +RigidDynamicBody2D::FreezeMode RigidDynamicBody2D::get_freeze_mode() const { + return freeze_mode; } void RigidDynamicBody2D::set_mass(real_t p_mass) { @@ -778,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 { @@ -798,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) { @@ -844,17 +890,14 @@ TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const { TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings(); - if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to RigidDynamicBody2D (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05) { + warnings.push_back(TTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } void RigidDynamicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody2D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody2D::get_mode); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody2D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody2D::get_mass); @@ -918,11 +961,19 @@ void RigidDynamicBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody2D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody2D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody2D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody2D::is_lock_rotation_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody2D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody2D::is_freeze_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody2D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody2D::get_freeze_mode); + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody2D::get_colliding_bodies); GDVIRTUAL_BIND(_integrate_forces, "state"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); @@ -936,6 +987,9 @@ void RigidDynamicBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); @@ -952,10 +1006,8 @@ void RigidDynamicBody2D::_bind_methods() { ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); - BIND_ENUM_CONSTANT(MODE_DYNAMIC); - BIND_ENUM_CONSTANT(MODE_STATIC); - BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); - BIND_ENUM_CONSTANT(MODE_KINEMATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); @@ -1004,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; @@ -1016,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); } @@ -1026,6 +1079,7 @@ bool CharacterBody2D::move_and_slide() { } motion_results.clear(); + last_motion = Vector2(); bool was_on_floor = on_floor; on_floor = false; @@ -1033,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); } @@ -1048,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(); @@ -1073,30 +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) { - gt.elements[2] -= result.travel.slide(up_direction); - } else { + if (result.travel.length() <= margin + CMP_EPSILON) { gt.elements[2] -= result.travel; } set_global_transform(gt); - linear_velocity = Vector2(); + motion_velocity = Vector2(); + last_motion = Vector2(); motion = Vector2(); break; } @@ -1111,7 +1196,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Avoid to move forward on a wall if floor_block_on_wall is true. if (p_was_on_floor && !on_floor && !vel_dir_facing_up) { // If the movement is large the body can be prevented from reaching the walls. - if (result.travel.length() <= margin) { + if (result.travel.length() <= margin + CMP_EPSILON) { // Cancels the motion. Transform2D gt = get_global_transform(); gt.elements[2] -= result.travel; @@ -1119,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; } @@ -1141,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(); @@ -1151,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); } } } @@ -1162,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); } } @@ -1196,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(); @@ -1226,28 +1322,32 @@ 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; } } void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_equal_approx(floor_snap_length, 0) || on_floor || !was_on_floor || vel_dir_facing_up) { + if (on_floor || !was_on_floor || vel_dir_facing_up) { return; } - Transform2D gt = get_global_transform(); + // 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 * -floor_snap_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; @@ -1262,24 +1362,26 @@ 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); } } } bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_equal_approx(floor_snap_length, 0) || up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { + if (up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { return false; } + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + + PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.collide_separation_ray = true; + PhysicsServer2D::MotionResult result; - if (move_and_collide(up_direction * -floor_snap_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; } @@ -1297,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); @@ -1306,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 { @@ -1342,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; } @@ -1451,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; } @@ -1500,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(); @@ -1511,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); @@ -1542,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); @@ -1550,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); @@ -1557,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_"); @@ -1568,14 +1704,19 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length"); ADD_GROUP("Moving platform", "moving_platform"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_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 { @@ -1670,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); @@ -1687,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 5d0d98a2df..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 @@ -92,7 +92,7 @@ class AnimatableBody2D : public StaticBody2D { GDCLASS(AnimatableBody2D, StaticBody2D); private: - bool sync_to_physics = false; + bool sync_to_physics = true; Transform2D last_valid_transform; @@ -117,11 +117,9 @@ class RigidDynamicBody2D : public PhysicsBody2D { GDCLASS(RigidDynamicBody2D, PhysicsBody2D); public: - enum Mode { - MODE_DYNAMIC, - MODE_STATIC, - MODE_DYNAMIC_LOCKED, - MODE_KINEMATIC, + enum FreezeMode { + FREEZE_MODE_STATIC, + FREEZE_MODE_KINEMATIC, }; enum CenterOfMassMode { @@ -137,7 +135,9 @@ public: private: bool can_sleep = true; - Mode mode = MODE_DYNAMIC; + bool lock_rotation = false; + bool freeze = false; + FreezeMode freeze_mode = FREEZE_MODE_STATIC; real_t mass = 1.0; real_t inertia = 0.0; @@ -211,9 +211,17 @@ protected: GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *) + void _apply_body_mode(); + public: - void set_mode(Mode p_mode); - Mode get_mode() const; + void set_lock_rotation_enabled(bool p_lock_rotation); + bool is_lock_rotation_enabled() const; + + void set_freeze_enabled(bool p_freeze); + bool is_freeze_enabled() const; + + void set_freeze_mode(FreezeMode p_freeze_mode); + FreezeMode get_freeze_mode() const; void set_mass(real_t p_mass); real_t get_mass() const; @@ -290,7 +298,7 @@ private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody2D::Mode); +VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode); @@ -302,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; @@ -313,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; @@ -326,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_stop_on_slope = false; bool floor_constant_speed = false; + bool floor_stop_on_slope = true; bool floor_block_on_wall = true; bool slide_on_ceiling = true; int max_slides = 4; - int platform_layer; + int platform_layer = 0; real_t floor_max_angle = Math::deg2rad((real_t)45.0); - real_t floor_snap_length = 0; + real_t floor_snap_length = 1; real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0); Vector2 up_direction = Vector2(0.0, -1.0); uint32_t moving_platform_floor_layers = UINT32_MAX; 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; @@ -387,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); @@ -406,6 +434,7 @@ protected: }; VARIANT_ENUM_CAST(CharacterBody2D::MotionMode); +VARIANT_ENUM_CAST(CharacterBody2D::MovingPlatformApplyVelocityOnLeave); class KinematicCollision2D : public RefCounted { GDCLASS(KinematicCollision2D, RefCounted); @@ -431,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 03db9c0d32..c2f150ce00 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)); }; @@ -409,6 +409,19 @@ bool TileMap::is_layer_enabled(int p_layer) const { return layers[p_layer].enabled; } +void TileMap::set_layer_modulate(int p_layer, Color p_modulate) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].modulate = p_modulate; + _clear_internals(); + _recreate_internals(); + emit_signal(SNAME("changed")); +} + +Color TileMap::get_layer_modulate(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Color()); + return layers[p_layer].modulate; +} + void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].y_sort_enabled = p_y_sort_enabled; @@ -452,6 +465,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 +534,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 +551,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 +635,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 +644,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 +713,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 +742,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 +761,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); } @@ -786,8 +811,9 @@ void TileMap::_rendering_cleanup_layer(int p_layer) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); RenderingServer *rs = RenderingServer::get_singleton(); - if (!layers[p_layer].canvas_item.is_valid()) { + if (layers[p_layer].canvas_item.is_valid()) { rs->free(layers[p_layer].canvas_item); + layers[p_layer].canvas_item = RID(); } } @@ -820,9 +846,22 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List int prev_z_index = 0; RID prev_canvas_item; + Color modulate = get_self_modulate(); + modulate *= get_layer_modulate(q.layer); + if (selected_layer >= 0) { + int z1 = get_layer_z_index(q.layer); + int z2 = get_layer_z_index(selected_layer); + if (z1 < z2 || (z1 == z2 && q.layer < selected_layer)) { + modulate = modulate.darkened(0.5); + } else if (z1 > z2 || (z1 == z2 && q.layer > selected_layer)) { + modulate = modulate.darkened(0.5); + modulate.a *= 0.3; + } + } + // 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)) { @@ -882,21 +921,12 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List } // Drawing the tile in the canvas item. - Color modulate = get_self_modulate(); - if (selected_layer >= 0) { - if (q.layer < selected_layer) { - modulate = modulate.darkened(0.5); - } else if (q.layer > selected_layer) { - modulate = modulate.darkened(0.5); - 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 +952,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 +1122,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 +1193,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 +1225,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 +1283,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 +1303,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 +1317,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); - - 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; - } + Vector<Color> color; + color.push_back(debug_collision_color); - 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 +1353,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 +1391,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 +1444,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 +1547,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 +1595,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 +1898,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 +2035,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; } @@ -2034,16 +2079,31 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { return false; } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { int index = components[0].trim_prefix("layer_").to_int(); - if (index < 0 || index >= (int)layers.size()) { + if (index < 0) { return false; } + if (index >= (int)layers.size()) { + _clear_internals(); + while (index >= (int)layers.size()) { + layers.push_back(TileMapLayer()); + } + _recreate_internals(); + + notify_property_list_changed(); + emit_signal(SNAME("changed")); + update_configuration_warnings(); + } + if (components[1] == "name") { set_layer_name(index, p_value); return true; } else if (components[1] == "enabled") { set_layer_enabled(index, p_value); return true; + } else if (components[1] == "modulate") { + set_layer_modulate(index, p_value); + return true; } else if (components[1] == "y_sort_enabled") { set_layer_y_sort_enabled(index, p_value); return true; @@ -2080,6 +2140,9 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "enabled") { r_ret = is_layer_enabled(index); return true; + } else if (components[1] == "modulate") { + r_ret = get_layer_modulate(index); + return true; } else if (components[1] == "y_sort_enabled") { r_ret = is_layer_y_sort_enabled(index); return true; @@ -2105,6 +2168,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { for (unsigned int i = 0; i < layers.size(); i++) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("layer_%d/name", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/enabled", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::COLOR, vformat("layer_%d/modulate", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/y_sort_enabled", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/y_sort_origin", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/z_index", i), PROPERTY_HINT_NONE)); @@ -2739,8 +2803,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; } @@ -2761,8 +2825,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)); } } } @@ -2782,8 +2846,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()); } } @@ -2797,8 +2861,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()); } @@ -2813,8 +2877,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()); } @@ -2988,6 +3052,8 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name); ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled); ClassDB::bind_method(D_METHOD("is_layer_enabled", "layer"), &TileMap::is_layer_enabled); + ClassDB::bind_method(D_METHOD("set_layer_modulate", "layer", "enabled"), &TileMap::set_layer_modulate); + ClassDB::bind_method(D_METHOD("get_layer_modulate", "layer"), &TileMap::get_layer_modulate); ClassDB::bind_method(D_METHOD("set_layer_y_sort_enabled", "layer", "y_sort_enabled"), &TileMap::set_layer_y_sort_enabled); ClassDB::bind_method(D_METHOD("is_layer_y_sort_enabled", "layer"), &TileMap::is_layer_y_sort_enabled); ClassDB::bind_method(D_METHOD("set_layer_y_sort_origin", "layer", "y_sort_origin"), &TileMap::set_layer_y_sort_origin); @@ -2995,6 +3061,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); @@ -3006,10 +3074,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); @@ -3023,8 +3095,11 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data); ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data); + ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update); + 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"); @@ -3041,8 +3116,16 @@ void TileMap::_bind_methods() { void TileMap::_tile_set_changed() { emit_signal(SNAME("changed")); - _clear_internals(); - _recreate_internals(); + _tile_set_changed_deferred_update_needed = true; + call_deferred("_tile_set_changed_deferred_update"); +} + +void TileMap::_tile_set_changed_deferred_update() { + if (_tile_set_changed_deferred_update_needed) { + _clear_internals(); + _recreate_internals(); + _tile_set_changed_deferred_update_needed = false; + } } TileMap::TileMap() { diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index ca38baa550..e5809deabb 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; @@ -218,6 +219,7 @@ private: struct TileMapLayer { String name; bool enabled = true; + Color modulate = Color(1, 1, 1, 1); bool y_sort_enabled = false; int y_sort_origin = 0; int z_index = 0; @@ -229,6 +231,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 +264,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); @@ -279,6 +285,8 @@ private: Vector<int> _get_tile_data(int p_layer) const; void _tile_set_changed(); + bool _tile_set_changed_deferred_update_needed = false; + void _tile_set_changed_deferred_update(); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -316,6 +324,8 @@ public: String get_layer_name(int p_layer) const; void set_layer_enabled(int p_layer, bool p_visible); bool is_layer_enabled(int p_layer) const; + void set_layer_modulate(int p_layer, Color p_modulate); + Color get_layer_modulate(int p_layer) const; void set_layer_y_sort_enabled(int p_layer, bool p_enabled); bool is_layer_y_sort_enabled(int p_layer) const; void set_layer_y_sort_origin(int p_layer, int p_y_sort_origin); @@ -325,6 +335,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 +377,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 8f4bb67a42..d411525707 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/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index a54b10ba70..c422070480 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -282,7 +282,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); Map<StringName, Vector<AudioFrame>> bus_map; bus_map[_get_actual_bus()] = volume_vector; - AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), linear_attenuation, attenuation_filter_cutoff_hz, actual_pitch_scale); + AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz); stream_playbacks.push_back(new_playback); setplay.set(-1); } diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index c34c150145..8e89f4fc54 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -110,7 +110,7 @@ TypedArray<String> BoneAttachment3D::get_configuration_warnings() const { } else { Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent()); if (!parent) { - warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an extenral Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); + warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); } } @@ -215,8 +215,6 @@ void BoneAttachment3D::_transform_changed() { sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans), 1.0, true); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans)); } } } @@ -273,8 +271,6 @@ void BoneAttachment3D::set_override_pose(bool p_override) { sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, Transform3D()); } } _transform_changed(); @@ -294,8 +290,6 @@ void BoneAttachment3D::set_override_mode(int p_mode) { sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, Transform3D()); } } override_mode = p_mode; diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index cf681cace8..57b9854e0e 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -47,7 +47,6 @@ class BoneAttachment3D : public Node3D { enum OVERRIDE_MODES { MODE_GLOBAL_POSE, MODE_LOCAL_POSE, - MODE_CUSTOM_POSE }; bool use_external_skeleton = false; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 9aad338d15..588d2b5018 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -238,8 +238,8 @@ void Camera3D::clear_current(bool p_enable_next) { } } -void Camera3D::set_current(bool p_current) { - if (p_current) { +void Camera3D::set_current(bool p_enabled) { + if (p_enabled) { make_current(); } else { clear_current(); @@ -460,7 +460,7 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera3D::set_frustum); ClassDB::bind_method(D_METHOD("make_current"), &Camera3D::make_current); ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera3D::clear_current, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("set_current"), &Camera3D::set_current); + ClassDB::bind_method(D_METHOD("set_current", "enabled"), &Camera3D::set_current); ClassDB::bind_method(D_METHOD("is_current"), &Camera3D::is_current); ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera3D::get_camera_transform); ClassDB::bind_method(D_METHOD("get_fov"), &Camera3D::get_fov); @@ -468,13 +468,13 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size); ClassDB::bind_method(D_METHOD("get_far"), &Camera3D::get_far); ClassDB::bind_method(D_METHOD("get_near"), &Camera3D::get_near); - ClassDB::bind_method(D_METHOD("set_fov"), &Camera3D::set_fov); - ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera3D::set_frustum_offset); - ClassDB::bind_method(D_METHOD("set_size"), &Camera3D::set_size); - ClassDB::bind_method(D_METHOD("set_far"), &Camera3D::set_far); - ClassDB::bind_method(D_METHOD("set_near"), &Camera3D::set_near); + ClassDB::bind_method(D_METHOD("set_fov", "fov"), &Camera3D::set_fov); + ClassDB::bind_method(D_METHOD("set_frustum_offset", "offset"), &Camera3D::set_frustum_offset); + ClassDB::bind_method(D_METHOD("set_size", "size"), &Camera3D::set_size); + ClassDB::bind_method(D_METHOD("set_far", "far"), &Camera3D::set_far); + ClassDB::bind_method(D_METHOD("set_near", "near"), &Camera3D::set_near); ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection); - ClassDB::bind_method(D_METHOD("set_projection"), &Camera3D::set_projection); + ClassDB::bind_method(D_METHOD("set_projection", "mode"), &Camera3D::set_projection); ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera3D::set_h_offset); ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera3D::get_h_offset); ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera3D::set_v_offset); @@ -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,47 @@ 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() { + ERR_FAIL_COND_V_MSG(!is_inside_tree(), RID(), "Camera is not inside scene."); + 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..73126611d5 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -44,8 +44,10 @@ public: PROJECTION_FRUSTUM }; - enum KeepAspect { KEEP_WIDTH, - KEEP_HEIGHT }; + enum KeepAspect { + KEEP_WIDTH, + KEEP_HEIGHT + }; enum DopplerTracking { DOPPLER_TRACKING_DISABLED, @@ -86,6 +88,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(); @@ -109,7 +114,7 @@ public: void make_current(); void clear_current(bool p_enable_next = true); - void set_current(bool p_current); + void set_current(bool p_enabled); bool is_current() const; RID get_camera() const; @@ -168,6 +173,8 @@ public: Vector3 get_doppler_tracked_velocity() const; + RID get_pyramid_shape_rid(); + Camera3D(); ~Camera3D(); }; @@ -176,63 +183,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/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index c79f956642..4e496fba47 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -124,8 +124,7 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const { if (shape.is_valid() && Object::cast_to<RigidDynamicBody3D>(get_parent()) && - Object::cast_to<ConcavePolygonShape3D>(*shape) && - Object::cast_to<RigidDynamicBody3D>(get_parent())->get_mode() != RigidDynamicBody3D::MODE_STATIC) { + Object::cast_to<ConcavePolygonShape3D>(*shape)) { warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static.")); } diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index baf28ae102..32a62d8c7e 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -469,7 +469,7 @@ void GPUParticles3D::_skinning_changed() { if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { xforms.resize(draw_pass->get_builtin_bind_pose_count()); for (int j = 0; j < draw_pass->get_builtin_bind_pose_count(); j++) { - xforms.write[i] = draw_pass->get_builtin_bind_pose(j); + xforms.write[j] = draw_pass->get_builtin_bind_pose(j); } break; } diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 4fa34615bf..9127168c58 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -475,7 +475,7 @@ Ref<Image> GPUParticlesCollisionSDF::bake() { _create_bvh(bvh, face_pos.ptr(), face_pos.size(), faces.ptr(), th); Vector<uint8_t> data; - data.resize(sdf_size.z * sdf_size.y * sdf_size.x * sizeof(float)); + data.resize(sdf_size.z * sdf_size.y * sdf_size.x * (int)sizeof(float)); if (bake_step_function) { bake_step_function(0, "Baking SDF"); 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/animation/animation_cache.h b/scene/3d/importer_mesh_instance_3d.h index c856e644f7..0cf7dbe86b 100644 --- a/scene/animation/animation_cache.h +++ b/scene/3d/importer_mesh_instance_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* animation_cache.h */ +/* importer_mesh_instance_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,57 +28,37 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef ANIMATION_CACHE_H -#define ANIMATION_CACHE_H +#ifndef SCENE_IMPORTER_MESH_INSTANCE_3D_H +#define SCENE_IMPORTER_MESH_INSTANCE_3D_H -#include "scene/3d/skeleton_3d.h" -#include "scene/resources/animation.h" +#include "scene/3d/node_3d.h" +#include "scene/resources/immediate_mesh.h" +#include "scene/resources/skin.h" -class AnimationCache : public Object { - GDCLASS(AnimationCache, Object); +class ImporterMesh; - struct Path { - RES resource; - Object *object = nullptr; -#ifndef _3D_DISABLED - Skeleton3D *skeleton = nullptr; - Node3D *node_3d = nullptr; -#endif // _3D_DISABLED - Node *node = nullptr; +class ImporterMeshInstance3D : public Node3D { + GDCLASS(ImporterMeshInstance3D, Node3D) - int bone_idx = -1; - Vector<StringName> subpath; - bool valid = false; - }; - - Set<Node *> connected_nodes; - Vector<Path> path_cache; - - Node *root = nullptr; - Ref<Animation> animation; - bool cache_dirty = true; - bool cache_valid = false; - - void _node_exit_tree(Node *p_node); - - void _clear_cache(); - void _update_cache(); - void _animation_changed(); + Ref<ImporterMesh> mesh; + Ref<Skin> skin; + NodePath skeleton_path; + Vector<Ref<Material>> surface_materials; protected: static void _bind_methods(); public: - void set_track_transform(int p_idx, const Transform3D &p_transform); - void set_track_value(int p_idx, const Variant &p_value); - void call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + void set_mesh(const Ref<ImporterMesh> &p_mesh); + Ref<ImporterMesh> get_mesh() const; - void set_all(float p_time, float p_delta = 0); + void set_skin(const Ref<Skin> &p_skin); + Ref<Skin> get_skin() const; - void set_animation(const Ref<Animation> &p_animation); - void set_root(Node *p_root); + void set_surface_material(int p_idx, const Ref<Material> &p_material); + Ref<Material> get_surface_material(int p_idx) const; - AnimationCache(); + void set_skeleton_path(const NodePath &p_path); + NodePath get_skeleton_path() const; }; - -#endif // ANIMATION_CACHE_H +#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..cfd90e5da0 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(); @@ -146,11 +146,13 @@ void MeshInstance3D::_resolve_skeleton_path() { if (!skeleton_path.is_empty()) { Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(get_node(skeleton_path)); if (skeleton) { - new_skin_reference = skeleton->register_skin(skin_internal); if (skin_internal.is_null()) { + new_skin_reference = skeleton->register_skin(skeleton->create_skin_from_rest_transforms()); //a skin was created for us skin_internal = new_skin_reference->get_skin(); notify_property_list_changed(); + } else { + new_skin_reference = skeleton->register_skin(skin_internal); } } } @@ -369,6 +371,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/node_3d.cpp b/scene/3d/node_3d.cpp index 12470939f5..272a06bce4 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -376,6 +376,18 @@ void Node3D::update_gizmos() { #endif } +void Node3D::set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform) { +#ifdef TOOLS_ENABLED + if (!is_inside_world()) { + return; + } + + if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { + get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_set_subgizmo_selection, this, p_gizmo, p_id, p_transform); + } +#endif +} + void Node3D::clear_subgizmo_selection() { #ifdef TOOLS_ENABLED if (!is_inside_world()) { @@ -792,6 +804,7 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_gizmo", "gizmo"), &Node3D::add_gizmo); ClassDB::bind_method(D_METHOD("get_gizmos"), &Node3D::get_gizmos_bind); ClassDB::bind_method(D_METHOD("clear_gizmos"), &Node3D::clear_gizmos); + ClassDB::bind_method(D_METHOD("set_subgizmo_selection", "gizmo", "id", "transform"), &Node3D::set_subgizmo_selection); ClassDB::bind_method(D_METHOD("clear_subgizmo_selection"), &Node3D::clear_subgizmo_selection); ClassDB::bind_method(D_METHOD("set_visible", "visible"), &Node3D::set_visible); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 0fd0c4e205..d6dcdd96fe 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -92,6 +92,7 @@ class Node3D : public Node { Vector<Ref<Node3DGizmo>> gizmos; bool gizmos_disabled = false; bool gizmos_dirty = false; + bool transform_gizmo_visible = true; #endif } data; @@ -145,6 +146,8 @@ public: #ifdef TOOLS_ENABLED virtual Transform3D get_global_gizmo_transform() const; virtual Transform3D get_local_gizmo_transform() const; + virtual void set_transform_gizmo_visible(bool p_enabled) { data.transform_gizmo_visible = p_enabled; }; + virtual bool is_transform_gizmo_visible() const { return data.transform_gizmo_visible; }; #endif void set_as_top_level(bool p_enabled); @@ -155,6 +158,7 @@ public: void set_disable_gizmos(bool p_enabled); void update_gizmos(); + void set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D()); void clear_subgizmo_selection(); Vector<Ref<Node3DGizmo>> get_gizmos() const; Array get_gizmos_bind() const; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 918d1bb61f..8cb348d6e3 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -33,13 +33,9 @@ #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", "rel_vec", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1)); ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock); ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock); @@ -95,9 +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) { +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(); + + 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(p_motion, result, p_margin, p_test_only, p_max_collisions)) { + 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(); @@ -112,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; } } @@ -137,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. @@ -146,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; } } } @@ -161,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); } @@ -168,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; @@ -177,7 +179,12 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion r = const_cast<PhysicsServer3D::MotionResult *>(&r_collision->result); } - return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r, p_max_collisions); + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + 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) { @@ -334,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: { @@ -361,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) { @@ -506,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++; } } @@ -554,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++; } } @@ -599,27 +610,60 @@ void RigidDynamicBody3D::_notification(int p_what) { #endif } -void RigidDynamicBody3D::set_mode(Mode p_mode) { - mode = p_mode; - switch (p_mode) { - case MODE_DYNAMIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); - } break; - case MODE_STATIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); - } break; - case MODE_DYNAMIC_LOCKED: { - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED); - } break; - case MODE_KINEMATIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); - } break; +void RigidDynamicBody3D::_apply_body_mode() { + if (freeze) { + switch (freeze_mode) { + case FREEZE_MODE_STATIC: { + set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); + } break; + case FREEZE_MODE_KINEMATIC: { + set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); + } break; + } + } else if (lock_rotation) { + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR); + } else { + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); } - update_configuration_warnings(); } -RigidDynamicBody3D::Mode RigidDynamicBody3D::get_mode() const { - return mode; +void RigidDynamicBody3D::set_lock_rotation_enabled(bool p_lock_rotation) { + if (p_lock_rotation == lock_rotation) { + return; + } + + lock_rotation = p_lock_rotation; + _apply_body_mode(); +} + +bool RigidDynamicBody3D::is_lock_rotation_enabled() const { + return lock_rotation; +} + +void RigidDynamicBody3D::set_freeze_enabled(bool p_freeze) { + if (p_freeze == freeze) { + return; + } + + freeze = p_freeze; + _apply_body_mode(); +} + +bool RigidDynamicBody3D::is_freeze_enabled() const { + return freeze; +} + +void RigidDynamicBody3D::set_freeze_mode(FreezeMode p_freeze_mode) { + if (p_freeze_mode == freeze_mode) { + return; + } + + freeze_mode = p_freeze_mode; + _apply_body_mode(); +} + +RigidDynamicBody3D::FreezeMode RigidDynamicBody3D::get_freeze_mode() const { + return freeze_mode; } void RigidDynamicBody3D::set_mass(real_t p_mass) { @@ -846,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) { @@ -875,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 { @@ -892,17 +936,14 @@ TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); - if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to RigidDynamicBody (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05) { + warnings.push_back(TTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } void RigidDynamicBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody3D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody3D::get_mode); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody3D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody3D::get_mass); @@ -963,11 +1004,19 @@ void RigidDynamicBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody3D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody3D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody3D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody3D::is_lock_rotation_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody3D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody3D::is_freeze_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody3D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody3D::get_freeze_mode); + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody3D::get_colliding_bodies); GDVIRTUAL_BIND(_integrate_forces, "state"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); @@ -981,6 +1030,9 @@ void RigidDynamicBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); @@ -994,10 +1046,8 @@ void RigidDynamicBody3D::_bind_methods() { ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); - BIND_ENUM_CONSTANT(MODE_DYNAMIC); - BIND_ENUM_CONSTANT(MODE_STATIC); - BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); - BIND_ENUM_CONSTANT(MODE_KINEMATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); @@ -1040,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()) { @@ -1060,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); } @@ -1074,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; @@ -1101,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); @@ -1144,21 +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(); - real_t travel_total = result.travel.length(); - if (travel_total <= margin + CMP_EPSILON) { + if (result.travel.length() <= margin + CMP_EPSILON) { gt.origin -= result.travel; } set_global_transform(gt); - 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; } @@ -1186,7 +1261,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo Transform3D gt = get_global_transform(); real_t travel_total = result.travel.length(); real_t cancel_dist_max = MIN(0.1, margin * 20); - if (travel_total < margin + CMP_EPSILON) { + if (travel_total <= margin + CMP_EPSILON) { gt.origin -= result.travel; } else if (travel_total < cancel_dist_max) { // If the movement is large the body can be prevented from reaching the walls. gt.origin -= result.travel.slide(up_direction); @@ -1207,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. @@ -1236,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(); } } } @@ -1247,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; } @@ -1256,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); } } } @@ -1278,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. @@ -1300,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); @@ -1345,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(); @@ -1359,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; } } @@ -1377,10 +1476,15 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) return; } + // Snap by at least collision margin to keep floor state consistent. real_t length = MAX(floor_snap_length, margin); - Transform3D gt = get_global_transform(); + + PhysicsServer3D::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)); @@ -1396,19 +1500,26 @@ 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); } } } bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_zero_approx(floor_snap_length) || up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) { + if (up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) { return false; } + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + + PhysicsServer3D::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 * floor_snap_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()); @@ -1452,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; @@ -1503,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); } @@ -1515,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 { @@ -1547,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; } @@ -1563,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(); } @@ -1721,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; @@ -1730,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); @@ -1784,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"); @@ -1909,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); @@ -1968,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"); } /////////////////////////////////////// @@ -2932,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 96f3d7d747..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; @@ -105,7 +105,7 @@ private: Vector3 linear_velocity; Vector3 angular_velocity; - bool sync_to_physics = false; + bool sync_to_physics = true; Transform3D last_valid_transform; @@ -133,11 +133,9 @@ class RigidDynamicBody3D : public PhysicsBody3D { GDCLASS(RigidDynamicBody3D, PhysicsBody3D); public: - enum Mode { - MODE_DYNAMIC, - MODE_STATIC, - MODE_DYNAMIC_LOCKED, - MODE_KINEMATIC, + enum FreezeMode { + FREEZE_MODE_STATIC, + FREEZE_MODE_KINEMATIC, }; enum CenterOfMassMode { @@ -145,11 +143,11 @@ public: CENTER_OF_MASS_MODE_CUSTOM, }; - GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) - -protected: +private: bool can_sleep = true; - Mode mode = MODE_DYNAMIC; + bool lock_rotation = false; + bool freeze = false; + FreezeMode freeze_mode = FREEZE_MODE_STATIC; real_t mass = 1.0; Vector3 inertia; @@ -214,16 +212,28 @@ protected: void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); - virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); +protected: void _notification(int p_what); static void _bind_methods(); virtual void _validate_property(PropertyInfo &property) const override; + GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) + + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); + + void _apply_body_mode(); + public: - void set_mode(Mode p_mode); - Mode get_mode() const; + void set_lock_rotation_enabled(bool p_lock_rotation); + bool is_lock_rotation_enabled() const; + + void set_freeze_enabled(bool p_freeze); + bool is_freeze_enabled() const; + + void set_freeze_mode(FreezeMode p_freeze_mode); + FreezeMode get_freeze_mode() const; void set_mass(real_t p_mass); real_t get_mass() const; @@ -298,7 +308,7 @@ private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody3D::Mode); +VARIANT_ENUM_CAST(RigidDynamicBody3D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody3D::CenterOfMassMode); class KinematicCollision3D; @@ -318,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; @@ -327,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; @@ -364,24 +376,27 @@ private: }; CollisionState collision_state; - bool floor_stop_on_slope = false; bool floor_constant_speed = false; + bool floor_stop_on_slope = true; bool floor_block_on_wall = true; bool slide_on_ceiling = true; int max_slides = 6; - 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; @@ -473,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 { @@ -652,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/proximity_group_3d.cpp b/scene/3d/proximity_group_3d.cpp index c8c61a9f00..23df00c1f6 100644 --- a/scene/3d/proximity_group_3d.cpp +++ b/scene/3d/proximity_group_3d.cpp @@ -34,9 +34,9 @@ void ProximityGroup3D::_clear_groups() { Map<StringName, uint32_t>::Element *E; + const int size = 16; - { - const int size = 16; + do { StringName remove_list[size]; E = groups.front(); int num = 0; @@ -50,11 +50,7 @@ void ProximityGroup3D::_clear_groups() { for (int i = 0; i < num; i++) { groups.erase(remove_list[i]); } - } - - if (E) { - _clear_groups(); // call until we go through the whole list - } + } while (E); } void ProximityGroup3D::_update_groups() { diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index b7a79a2645..14506fd9bd 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -32,6 +32,7 @@ #include "core/object/message_queue.h" #include "core/variant/type_info.h" +#include "editor/plugins/skeleton_3d_editor_plugin.h" #include "scene/3d/physics_body_3d.h" #include "scene/resources/skeleton_modification_3d.h" #include "scene/resources/surface_tool.h" @@ -98,8 +99,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { set_bone_rest(which, p_value); } else if (what == "enabled") { set_bone_enabled(which, p_value); - } else if (what == "pose") { - set_bone_pose(which, p_value); + } else if (what == "position") { + set_bone_pose_position(which, p_value); + } else if (what == "rotation") { + set_bone_pose_rotation(which, p_value); + } else if (what == "scale") { + set_bone_pose_scale(which, p_value); } else { return false; } @@ -134,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_bone_rest(which); } else if (what == "enabled") { r_ret = is_bone_enabled(which); - } else if (what == "pose") { - r_ret = get_bone_pose(which); + } else if (what == "position") { + r_ret = get_bone_pose_position(which); + } else if (what == "rotation") { + r_ret = get_bone_pose_rotation(which); + } else if (what == "scale") { + r_ret = get_bone_pose_scale(which); } else { return false; } @@ -150,7 +159,9 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } #ifndef _3D_DISABLED @@ -160,6 +171,45 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { "SkeletonModificationStack3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); #endif //_3D_DISABLED + + for (PropertyInfo &E : *p_list) { + _validate_property(E); + } +} + +void Skeleton3D::_validate_property(PropertyInfo &property) const { + PackedStringArray spr = property.name.split("/"); + if (spr.size() == 3 && spr[0] == "bones") { + if (spr[2] == "rest") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (is_show_rest_only()) { + if (spr[2] == "enabled") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "position") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "rotation") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "scale") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } else if (!is_bone_enabled(spr[1].to_int())) { + if (spr[2] == "position") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "rotation") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (spr[2] == "scale") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } + } + + Node3D::_validate_property(property); } void Skeleton3D::_update_process_order() { @@ -178,7 +228,7 @@ void Skeleton3D::_update_process_order() { for (int i = 0; i < len; i++) { if (bonesptr[i].parent >= len) { - //validate this just in case + // Validate this just in case. ERR_PRINT("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent)); bonesptr[i].parent = -1; } @@ -186,9 +236,9 @@ void Skeleton3D::_update_process_order() { if (bonesptr[i].parent != -1) { int parent_bone_idx = bonesptr[i].parent; - // Check to see if this node is already added to the parent: + // Check to see if this node is already added to the parent. if (bonesptr[parent_bone_idx].child_bones.find(i) < 0) { - // Add the child node + // Add the child node. bonesptr[parent_bone_idx].child_bones.push_back(i); } else { ERR_PRINT("Skeleton3D parenthood graph is cyclic"); @@ -210,10 +260,10 @@ void Skeleton3D::_notification(int p_what) { int len = bones.size(); dirty = false; - // Update bone transforms + // Update bone transforms. force_update_all_bone_transforms(); - //update skins + // Update skins. for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { const Skin *skin = E->get()->skin.operator->(); RID skeleton = E->get()->skeleton; @@ -231,7 +281,7 @@ void Skeleton3D::_notification(int p_what) { StringName bind_name = skin->get_bind_name(i); if (bind_name != StringName()) { - //bind name used, use this + // Bind name used, use this. bool found = false; for (int j = 0; j < len; j++) { if (bonesptr[j].name == bind_name) { @@ -453,7 +503,8 @@ int Skeleton3D::get_bone_axis_forward_enum(int p_bone) { return bones[p_bone].rest_bone_forward_axis; } -// skeleton creation api +// Skeleton creation api + void Skeleton3D::add_bone(const String &p_name) { ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1); @@ -545,18 +596,6 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { _make_dirty(); } -void Skeleton3D::set_bone_disable_rest(int p_bone, bool p_disable) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].disable_rest = p_disable; -} - -bool Skeleton3D::is_bone_rest_disabled(int p_bone) const { - const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, false); - return bones[p_bone].disable_rest; -} - int Skeleton3D::get_bone_parent(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, -1); @@ -626,6 +665,7 @@ void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].enabled = p_enabled; + emit_signal(SceneStringNames::get_singleton()->bone_enabled_changed, p_bone); _make_dirty(); } @@ -635,6 +675,16 @@ bool Skeleton3D::is_bone_enabled(int p_bone) const { return bones[p_bone].enabled; } +void Skeleton3D::set_show_rest_only(bool p_enabled) { + show_rest_only = p_enabled; + emit_signal(SceneStringNames::get_singleton()->show_rest_only_changed); + _make_dirty(); +} + +bool Skeleton3D::is_show_rest_only() const { + return show_rest_only; +} + void Skeleton3D::clear_bones() { bones.clear(); process_order_dirty = true; @@ -642,38 +692,62 @@ void Skeleton3D::clear_bones() { _make_dirty(); } -// posing api +// Posing api -void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) { +void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].pose = p_pose; + bones.write[p_bone].pose_position = p_position; + bones.write[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); } } -Transform3D Skeleton3D::get_bone_pose(int p_bone) const { +void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) { const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - return bones[p_bone].pose; -} + ERR_FAIL_INDEX(p_bone, bone_size); -void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) { + bones.write[p_bone].pose_rotation = p_rotation; + bones.write[p_bone].pose_cache_dirty = true; + if (is_inside_tree()) { + _make_dirty(); + } +} +void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - //ERR_FAIL_COND( !is_inside_scene() ); - bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform3D()); - bones.write[p_bone].custom_pose = p_custom_pose; + bones.write[p_bone].pose_scale = p_scale; + bones.write[p_bone].pose_cache_dirty = true; + if (is_inside_tree()) { + _make_dirty(); + } +} - _make_dirty(); +Vector3 Skeleton3D::get_bone_pose_position(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3()); + return bones[p_bone].pose_position; +} + +Quaternion Skeleton3D::get_bone_pose_rotation(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Quaternion()); + return bones[p_bone].pose_rotation; +} + +Vector3 Skeleton3D::get_bone_pose_scale(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3()); + return bones[p_bone].pose_scale; } -Transform3D Skeleton3D::get_bone_custom_pose(int p_bone) const { +Transform3D Skeleton3D::get_bone_pose(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - return bones[p_bone].custom_pose; + ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache(); + return bones[p_bone].pose_cache; } void Skeleton3D::_make_dirty() { @@ -697,7 +771,7 @@ void Skeleton3D::localize_rests() { set_bone_rest(current_bone_idx, bones[bones[current_bone_idx].parent].rest.affine_inverse() * bones[current_bone_idx].rest); } - // Add the bone's children to the list of bones to be processed + // Add the bone's children to the list of bones to be processed. int child_bone_size = bones[current_bone_idx].child_bones.size(); for (int i = 0; i < child_bone_size; i++) { bones_to_process.push_back(bones[current_bone_idx].child_bones[i]); @@ -705,8 +779,8 @@ void Skeleton3D::localize_rests() { } } -void Skeleton3D::set_animate_physical_bones(bool p_animate) { - animate_physical_bones = p_animate; +void Skeleton3D::set_animate_physical_bones(bool p_enabled) { + animate_physical_bones = p_enabled; if (Engine::get_singleton()->is_editor_hint() == false) { bool sim = false; @@ -718,7 +792,7 @@ void Skeleton3D::set_animate_physical_bones(bool p_animate) { } } } - set_physics_process_internal(sim == false && p_animate); + set_physics_process_internal(sim == false && p_enabled); } } @@ -831,7 +905,7 @@ void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> Vector<int> sim_bones; if (p_bones.size() <= 0) { - sim_bones.push_back(0); // if no bones is specified, activate ragdoll on full body + sim_bones.push_back(0); // If no bones is specified, activate ragdoll on full body. } else { sim_bones.resize(p_bones.size()); int c = 0; @@ -874,59 +948,57 @@ void Skeleton3D::_skin_changed() { _make_dirty(); } -Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - if (E->get()->skin == p_skin) { - return Ref<SkinReference>(E->get()); +Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { + Ref<Skin> skin; + + skin.instantiate(); + skin->set_bind_count(bones.size()); + _update_process_order(); // Just in case. + + // Pose changed, rebuild cache of inverses. + const Bone *bonesptr = bones.ptr(); + int len = bones.size(); + + // Calculate global rests and invert them. + LocalVector<int> bones_to_process; + bones_to_process = get_parentless_bones(); + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + const Bone &b = bonesptr[current_bone_idx]; + bones_to_process.erase(current_bone_idx); + LocalVector<int> child_bones_vector; + child_bones_vector = get_bone_children(current_bone_idx); + int child_bones_size = child_bones_vector.size(); + if (b.parent < 0) { + skin->set_bind_pose(current_bone_idx, b.rest); + } + for (int i = 0; i < child_bones_size; i++) { + int child_bone_idx = child_bones_vector[i]; + const Bone &cb = bonesptr[child_bone_idx]; + skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest); + // Add the bone's children to the list of bones to be processed. + bones_to_process.push_back(child_bones_vector[i]); } } - Ref<Skin> skin = p_skin; - - if (skin.is_null()) { - //need to create one from existing code, this is for compatibility only - //when skeletons did not support skins. It is also used by gizmo - //to display the skeleton. - - skin.instantiate(); - skin->set_bind_count(bones.size()); - _update_process_order(); //just in case - - // pose changed, rebuild cache of inverses - const Bone *bonesptr = bones.ptr(); - int len = bones.size(); - - // calculate global rests and invert them - LocalVector<int> bones_to_process; - bones_to_process = get_parentless_bones(); - while (bones_to_process.size() > 0) { - int current_bone_idx = bones_to_process[0]; - const Bone &b = bonesptr[current_bone_idx]; - bones_to_process.erase(current_bone_idx); - LocalVector<int> child_bones_vector; - child_bones_vector = get_bone_children(current_bone_idx); - int child_bones_size = child_bones_vector.size(); - if (b.parent < 0) { - skin->set_bind_pose(current_bone_idx, b.rest); - } - for (int i = 0; i < child_bones_size; i++) { - int child_bone_idx = child_bones_vector[i]; - const Bone &cb = bonesptr[child_bone_idx]; - skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest); - // Add the bone's children to the list of bones to be processed. - bones_to_process.push_back(child_bones_vector[i]); - } - } + for (int i = 0; i < len; i++) { + // The inverse is what is actually required. + skin->set_bind_bone(i, i); + skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse()); + } + + return skin; +} + +Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { + ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>()); - for (int i = 0; i < len; i++) { - //the inverse is what is actually required - skin->set_bind_bone(i, i); - skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse()); + for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { + if (E->get()->skin == p_skin) { + return Ref<SkinReference>(E->get()); } } - ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>()); - Ref<SkinReference> skin_ref; skin_ref.instantiate(); @@ -934,17 +1006,23 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { skin_ref->bind_count = 0; skin_ref->skeleton = RenderingServer::get_singleton()->skeleton_create(); skin_ref->skeleton_node = this; - skin_ref->skin = skin; + skin_ref->skin = p_skin; skin_bindings.insert(skin_ref.operator->()); - skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); + skin_ref->skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); - _make_dirty(); //skin needs to be updated, so update skeleton + _make_dirty(); // Skin needs to be updated, so update skeleton. return skin_ref; } +void Skeleton3D::force_update_all_dirty_bones() { + if (dirty) { + const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); + } +} + void Skeleton3D::force_update_all_bone_transforms() { _update_process_order(); @@ -966,60 +1044,35 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { bones_to_process.erase(current_bone_idx); Bone &b = bonesptr[current_bone_idx]; + bool bone_enabled = b.enabled && !show_rest_only; - if (b.disable_rest) { - if (b.enabled) { - Transform3D pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * pose; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = pose; - b.pose_global_no_override = b.pose_global; - } + if (bone_enabled) { + b.update_pose_cache(); + Transform3D pose = b.pose_cache; + + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * pose; + b.pose_global_no_override = b.pose_global; } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = Transform3D(); - b.pose_global_no_override = b.pose_global; - } + b.pose_global = pose; + b.pose_global_no_override = b.pose_global; } - } else { - if (b.enabled) { - Transform3D pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = b.rest * pose; - b.pose_global_no_override = b.pose_global; - } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * b.rest; + b.pose_global_no_override = b.pose_global; } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = b.rest; - b.pose_global_no_override = b.pose_global; - } + b.pose_global = b.rest; + b.pose_global_no_override = b.pose_global; } } if (b.local_pose_override_amount >= CMP_EPSILON) { Transform3D override_local_pose; if (b.parent >= 0) { - override_local_pose = bonesptr[b.parent].pose_global * (b.rest * b.local_pose_override); + override_local_pose = bonesptr[b.parent].pose_global * b.local_pose_override; } else { - override_local_pose = (b.rest * b.local_pose_override); + override_local_pose = b.local_pose_override; } b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount); } @@ -1035,7 +1088,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { b.global_pose_override_amount = 0.0; } - // Add the bone's children to the list of bones to be processed + // Add the bone's children to the list of bones to be processed. int child_bone_size = b.child_bones.size(); for (int i = 0; i < child_bone_size; i++) { bones_to_process.push_back(b.child_bones[i]); @@ -1045,7 +1098,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { } } -// helper functions +// Helper functions Transform3D Skeleton3D::global_pose_to_world_transform(Transform3D p_global_pose) { return get_global_transform() * p_global_pose; @@ -1060,8 +1113,8 @@ Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); if (bones[p_bone_idx].parent >= 0) { int parent_bone_idx = bones[p_bone_idx].parent; - Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); - return conversion_transform.affine_inverse() * p_global_pose; + Transform3D conversion_transform = bones[parent_bone_idx].pose_global.affine_inverse(); + return conversion_transform * p_global_pose; } else { return p_global_pose; } @@ -1072,8 +1125,7 @@ Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); if (bones[p_bone_idx].parent >= 0) { int parent_bone_idx = bones[p_bone_idx].parent; - Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); - return conversion_transform * p_local_pose; + return bones[parent_bone_idx].pose_global * p_local_pose; } else { return p_local_pose; } @@ -1163,17 +1215,24 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest); + ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms"), &Skeleton3D::create_skin_from_rest_transforms); ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton3D::register_skin); ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton3D::localize_rests); - ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton3D::set_bone_disable_rest); - ClassDB::bind_method(D_METHOD("is_bone_rest_disabled", "bone_idx"), &Skeleton3D::is_bone_rest_disabled); - ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones); ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose); - ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose); + ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position); + ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation); + ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale); + + ClassDB::bind_method(D_METHOD("get_bone_pose_position", "bone_idx"), &Skeleton3D::get_bone_pose_position); + ClassDB::bind_method(D_METHOD("get_bone_pose_rotation", "bone_idx"), &Skeleton3D::get_bone_pose_rotation); + ClassDB::bind_method(D_METHOD("get_bone_pose_scale", "bone_idx"), &Skeleton3D::get_bone_pose_scale); + + ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled); + ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true)); ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override); ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false)); @@ -1185,9 +1244,6 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_local_pose_override, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton3D::get_bone_local_pose_override); - ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose); - ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose); - ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms); ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms); @@ -1198,7 +1254,10 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("local_pose_to_global_pose", "bone_idx", "local_pose"), &Skeleton3D::local_pose_to_global_pose); ClassDB::bind_method(D_METHOD("global_pose_z_forward_to_bone_forward", "bone_idx", "basis"), &Skeleton3D::global_pose_z_forward_to_bone_forward); - ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones); + ClassDB::bind_method(D_METHOD("set_show_rest_only", "enabled"), &Skeleton3D::set_show_rest_only); + ClassDB::bind_method(D_METHOD("is_show_rest_only"), &Skeleton3D::is_show_rest_only); + + ClassDB::bind_method(D_METHOD("set_animate_physical_bones", "enabled"), &Skeleton3D::set_animate_physical_bones); ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones); ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton3D::physical_bones_stop_simulation); @@ -1212,6 +1271,7 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications); #ifndef _3D_DISABLED + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones"); #endif // _3D_DISABLED @@ -1220,6 +1280,8 @@ void Skeleton3D::_bind_methods() { #endif // TOOLS_ENABLED ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx"))); + ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx"))); + ADD_SIGNAL(MethodInfo("show_rest_only_changed")); BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } @@ -1228,7 +1290,7 @@ Skeleton3D::Skeleton3D() { } Skeleton3D::~Skeleton3D() { - //some skins may remain bound + // Some skins may remain bound. for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { E->get()->skeleton_node = nullptr; } diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index c8a19db813..f7bc3df94e 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -76,16 +76,24 @@ private: bool enabled; int parent; - bool disable_rest = false; Transform3D rest; - Transform3D pose; + _FORCE_INLINE_ void update_pose_cache() { + if (pose_cache_dirty) { + pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale); + pose_cache.origin = pose_position; + pose_cache_dirty = false; + } + } + bool pose_cache_dirty = true; + Transform3D pose_cache; + Vector3 pose_position; + Quaternion pose_rotation; + Vector3 pose_scale = Vector3(1, 1, 1); + Transform3D pose_global; Transform3D pose_global_no_override; - bool custom_pose_enable = false; - Transform3D custom_pose; - real_t global_pose_override_amount = 0.0; bool global_pose_override_reset = false; Transform3D global_pose_override; @@ -107,8 +115,6 @@ private: Bone() { parent = -1; enabled = true; - disable_rest = false; - custom_pose_enable = false; global_pose_override_amount = 0; global_pose_override_reset = false; #ifndef _3D_DISABLED @@ -137,6 +143,8 @@ private: void _make_dirty(); bool dirty = false; + bool show_rest_only = false; + uint64_t version = 1; void _update_process_order(); @@ -145,6 +153,7 @@ protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List<PropertyInfo> *p_list) const; + virtual void _validate_property(PropertyInfo &property) const override; void _notification(int p_what); static void _bind_methods(); @@ -185,9 +194,6 @@ public: void remove_bone_child(int p_bone, int p_child); Vector<int> get_parentless_bones(); - void set_bone_disable_rest(int p_bone, bool p_disable); - bool is_bone_rest_disabled(int p_bone) const; - int get_bone_count() const; void set_bone_rest(int p_bone, const Transform3D &p_rest); @@ -197,15 +203,22 @@ public: void set_bone_enabled(int p_bone, bool p_enabled); bool is_bone_enabled(int p_bone) const; + + void set_show_rest_only(bool p_enabled); + bool is_show_rest_only() const; void clear_bones(); // posing api - void set_bone_pose(int p_bone, const Transform3D &p_pose); + void set_bone_pose_position(int p_bone, const Vector3 &p_position); + void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation); + void set_bone_pose_scale(int p_bone, const Vector3 &p_scale); + Transform3D get_bone_pose(int p_bone) const; - void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose); - Transform3D get_bone_custom_pose(int p_bone) const; + Vector3 get_bone_pose_position(int p_bone) const; + Quaternion get_bone_pose_rotation(int p_bone) const; + Vector3 get_bone_pose_scale(int p_bone) const; void clear_bones_global_pose_override(); Transform3D get_bone_global_pose_override(int p_bone) const; @@ -217,8 +230,11 @@ public: void localize_rests(); // used for loaders and tools + Ref<Skin> create_skin_from_rest_transforms(); + Ref<SkinReference> register_skin(const Ref<Skin> &p_skin); + void force_update_all_dirty_bones(); void force_update_all_bone_transforms(); void force_update_bone_children_transforms(int bone_idx); @@ -244,7 +260,7 @@ public: // Physical bone API - void set_animate_physical_bones(bool p_animate); + void set_animate_physical_bones(bool p_enabled); bool get_animate_physical_bones() const; void bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone); diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 466f67afb8..2e788051f4 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -99,7 +99,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain child_ci->current_pos = child_ci->initial_transform.origin; if (child_ci->parent_item) { - child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + child_ci->length = child_ci->parent_item->current_pos.distance_to(child_ci->current_pos); } } @@ -140,7 +140,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vec solve_simple_backwards(p_task->chain, p_solve_magnet); solve_simple_forwards(p_task->chain, p_solve_magnet, p_origin_pos); - distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + distance_to_goal = p_task->chain.tips[0].end_effector->goal_transform.origin.distance_to(p_task->chain.tips[0].chain_item->current_pos); } } @@ -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..9fceb21790 100644 --- a/scene/3d/soft_dynamic_body_3d.cpp +++ b/scene/3d/soft_dynamic_body_3d.cpp @@ -411,8 +411,9 @@ void SoftDynamicBody3D::_draw_soft_mesh() { return; } - if (!rendering_server_handler.is_ready()) { - rendering_server_handler.prepare(get_mesh()->get_rid(), 0); + const RID mesh_rid = get_mesh()->get_rid(); + if (!rendering_server_handler.is_ready(mesh_rid)) { + rendering_server_handler.prepare(mesh_rid, 0); /// Necessary in order to render the mesh correctly (Soft body nodes are in global space) simulation_started = true; @@ -433,9 +434,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 +445,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/soft_dynamic_body_3d.h b/scene/3d/soft_dynamic_body_3d.h index 0b4b3021cd..5e7fbfe29e 100644 --- a/scene/3d/soft_dynamic_body_3d.h +++ b/scene/3d/soft_dynamic_body_3d.h @@ -50,7 +50,7 @@ class SoftDynamicBodyRenderingServerHandler : public RenderingServerHandler { private: SoftDynamicBodyRenderingServerHandler(); - bool is_ready() { return mesh.is_valid(); } + bool is_ready(RID p_mesh_rid) const { return mesh.is_valid() && mesh == p_mesh_rid; } void prepare(RID p_mesh_rid, int p_surface); void clear(); void open(); 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/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index b9a2736918..349a534680 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -712,7 +712,7 @@ Rect2 Sprite3D::get_item_rect() const { return CanvasItem::get_item_rect(); */ - Size2i s; + Size2 s; if (region) { s = region_rect.size; @@ -807,22 +807,20 @@ void AnimatedSprite3D::_draw() { set_base(RID()); return; //no texuture no life } - Vector2 tsize = texture->get_size(); + Size2 tsize = texture->get_size(); if (tsize.x == 0 || tsize.y == 0) { return; } - Size2i s = tsize; Rect2 src_rect; - - src_rect.size = s; + src_rect.size = tsize; Point2 ofs = get_offset(); if (is_centered()) { - ofs -= s / 2; + ofs -= tsize / 2; } - Rect2 dst_rect(ofs, s); + Rect2 dst_rect(ofs, tsize); Rect2 final_rect; Rect2 final_src_rect; @@ -1133,7 +1131,7 @@ Rect2 AnimatedSprite3D::get_item_rect() const { if (t.is_null()) { return Rect2(0, 0, 1, 1); } - Size2i s = t->get_size(); + Size2 s = t->get_size(); Point2 ofs = get_offset(); if (centered) { diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index bc3bb81ed4..6761fdd944 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -275,7 +275,7 @@ void VehicleWheel3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel3D::get_steering); ADD_GROUP("Per-Wheel Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); ADD_GROUP("VehicleBody3D Motion", ""); @@ -470,7 +470,7 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) { } void VehicleBody3D::_update_suspension(PhysicsDirectBodyState3D *s) { - real_t chassisMass = mass; + real_t chassisMass = get_mass(); for (int w_it = 0; w_it < wheels.size(); w_it++) { VehicleWheel3D &wheel_info = *wheels[w_it]; @@ -558,7 +558,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const rel_pos2, normal, s->get_inverse_inertia_tensor().get_main_diagonal(), - 1.0 / mass, + 1.0 / get_mass(), b2invinertia, b2invmass); @@ -584,7 +584,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const #define ONLY_USE_LINEAR_MASS #ifdef ONLY_USE_LINEAR_MASS - real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass); + real_t massTerm = real_t(1.) / ((1.0 / get_mass()) + b2invmass); impulse = -contactDamping * rel_vel * massTerm; #else real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv; @@ -914,7 +914,7 @@ void VehicleBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody3D::get_steering); ADD_GROUP("Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); } diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index d3d12d94e9..377abd5b38 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -398,7 +398,7 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { baker.end_bake(); - //create the data for visual server + //create the data for rendering server if (p_create_visual_debug) { MultiMeshInstance3D *mmi = memnew(MultiMeshInstance3D); diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 04f371f4b2..aa1236521d 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -618,7 +618,6 @@ void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds) { bake_cells.resize(1); material_cache.clear(); - print_line("subdiv: " + itos(p_subdiv)); //find out the actual real bounds, power of 2, which gets the highest subdivision po2_bounds = p_bounds; int longest_axis = po2_bounds.get_longest_axis_index(); @@ -661,7 +660,7 @@ void Voxelizer::end_bake() { _fixup_plot(0, 0); } -//create the data for visual server +//create the data for rendering server int Voxelizer::get_voxel_gi_octree_depth() const { return cell_subdiv; 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_cache.cpp b/scene/animation/animation_cache.cpp deleted file mode 100644 index 56743007e4..0000000000 --- a/scene/animation/animation_cache.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/*************************************************************************/ -/* animation_cache.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 "animation_cache.h" - -void AnimationCache::_node_exit_tree(Node *p_node) { - //it is one shot, so it disconnects upon arrival - - ERR_FAIL_COND(!connected_nodes.has(p_node)); - - connected_nodes.erase(p_node); - - for (int i = 0; i < path_cache.size(); i++) { - if (path_cache[i].node != p_node) { - continue; - } - - path_cache.write[i].valid = false; //invalidate path cache - } -} - -void AnimationCache::_animation_changed() { - _clear_cache(); -} - -void AnimationCache::_clear_cache() { - while (connected_nodes.size()) { - connected_nodes.front()->get()->disconnect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree)); - connected_nodes.erase(connected_nodes.front()); - } - path_cache.clear(); - cache_valid = false; - cache_dirty = true; -} - -void AnimationCache::_update_cache() { - cache_valid = false; - - ERR_FAIL_COND(!root); - ERR_FAIL_COND(!root->is_inside_tree()); - ERR_FAIL_COND(animation.is_null()); - - for (int i = 0; i < animation->get_track_count(); i++) { - NodePath np = animation->track_get_path(i); - - Node *node = root->get_node(np); - if (!node) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(!node, "Invalid track path in animation '" + np + "'."); - } - - Path path; - - Ref<Resource> res; - - if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D) { -#ifndef _3D_DISABLED - if (np.get_subname_count() > 1) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D, "Transform tracks can't have a subpath '" + np + "'."); - } - - Node3D *sp = Object::cast_to<Node3D>(node); - - if (!sp) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(!sp, "Transform track not of type Node3D '" + np + "'."); - } - - if (np.get_subname_count() == 1) { - StringName property = np.get_subname(0); - String ps = property; - - Skeleton3D *sk = Object::cast_to<Skeleton3D>(node); - if (!sk) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(!sk, "Property defined in Transform track, but not a Skeleton! '" + np + "'."); - } - - int idx = sk->find_bone(ps); - if (idx == -1) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(idx == -1, "Property defined in Transform track, but not a Skeleton Bone! '" + np + "'."); - } - - path.bone_idx = idx; - path.skeleton = sk; - } - - path.node_3d = sp; -#endif // _3D_DISABLED - } else { - if (np.get_subname_count() > 0) { - RES res2; - Vector<StringName> leftover_subpath; - - // We don't want to cache the last resource unless it is a method call - bool is_method = animation->track_get_type(i) == Animation::TYPE_METHOD; - root->get_node_and_resource(np, res2, leftover_subpath, is_method); - - if (res2.is_valid()) { - path.resource = res2; - } else { - path.node = node; - } - path.object = res2.is_valid() ? res2.ptr() : (Object *)node; - path.subpath = leftover_subpath; - - } else { - path.node = node; - path.object = node; - path.subpath = np.get_subnames(); - } - } - - if (animation->track_get_type(i) == Animation::TYPE_VALUE) { - if (np.get_subname_count() == 0) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(np.get_subname_count() == 0, "Value Track lacks property: " + np + "."); - } - - } else if (animation->track_get_type(i) == Animation::TYPE_METHOD) { - if (path.subpath.size() != 0) { // Trying to call a method of a non-resource - - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(path.subpath.size() != 0, "Method Track has property: " + np + "."); - } - } - - path.valid = true; - - path_cache.push_back(path); - - if (!connected_nodes.has(path.node)) { - connected_nodes.insert(path.node); - path.node->connect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree), Node::make_binds(path.node), CONNECT_ONESHOT); - } - } - - cache_dirty = false; - cache_valid = true; -} - -void AnimationCache::set_track_transform(int p_idx, const Transform3D &p_transform) { - if (cache_dirty) { - _update_cache(); - } - - ERR_FAIL_COND(!cache_valid); - ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache.write[p_idx]; - if (!p.valid) { - return; - } - -#ifndef _3D_DISABLED - ERR_FAIL_COND(!p.node); - ERR_FAIL_COND(!p.node_3d); - - if (p.skeleton) { - p.skeleton->set_bone_pose(p.bone_idx, p_transform); - } else { - p.node_3d->set_transform(p_transform); - } -#endif // _3D_DISABLED -} - -void AnimationCache::set_track_value(int p_idx, const Variant &p_value) { - if (cache_dirty) { - _update_cache(); - } - - ERR_FAIL_COND(!cache_valid); - ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache.write[p_idx]; - if (!p.valid) { - return; - } - - ERR_FAIL_COND(!p.object); - p.object->set_indexed(p.subpath, p_value); -} - -void AnimationCache::call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (cache_dirty) { - _update_cache(); - } - - ERR_FAIL_COND(!cache_valid); - ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache.write[p_idx]; - if (!p.valid) { - return; - } - - ERR_FAIL_COND(!p.object); - p.object->call(p_method, p_args, p_argcount, r_error); -} - -void AnimationCache::set_all(float p_time, float p_delta) { - if (cache_dirty) { - _update_cache(); - } - - ERR_FAIL_COND(!cache_valid); - - int tc = animation->get_track_count(); - for (int i = 0; i < tc; i++) { - switch (animation->track_get_type(i)) { - case Animation::TYPE_TRANSFORM3D: { - Vector3 loc, scale; - Quaternion rot; - animation->transform_track_interpolate(i, p_time, &loc, &rot, &scale); - Transform3D tr(Basis(rot), loc); - tr.basis.scale(scale); - - set_track_transform(i, tr); - - } break; - case Animation::TYPE_VALUE: { - if (animation->value_track_get_update_mode(i) == Animation::UPDATE_CONTINUOUS || (animation->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE && p_delta == 0)) { - Variant v = animation->value_track_interpolate(i, p_time); - set_track_value(i, v); - } else { - List<int> indices; - animation->value_track_get_key_indices(i, p_time, p_delta, &indices); - - for (int &E : indices) { - Variant v = animation->track_get_key_value(i, E); - set_track_value(i, v); - } - } - - } break; - case Animation::TYPE_METHOD: { - List<int> indices; - animation->method_track_get_key_indices(i, p_time, p_delta, &indices); - - for (int &E : indices) { - Vector<Variant> args = animation->method_track_get_params(i, E); - StringName name = animation->method_track_get_name(i, E); - Callable::CallError err; - - if (!args.size()) { - call_track(i, name, nullptr, 0, err); - } else { - Vector<const Variant *> argptrs; - argptrs.resize(args.size()); - for (int j = 0; j < args.size(); j++) { - argptrs.write[j] = &args.write[j]; - } - - call_track(i, name, (const Variant **)&argptrs[0], args.size(), err); - } - } - - } break; - default: { - } - } - } -} - -void AnimationCache::set_animation(const Ref<Animation> &p_animation) { - _clear_cache(); - - if (animation.is_valid()) { - animation->disconnect("changed", callable_mp(this, &AnimationCache::_animation_changed)); - } - - animation = p_animation; - - if (animation.is_valid()) { - animation->connect("changed", callable_mp(this, &AnimationCache::_animation_changed)); - } -} - -void AnimationCache::_bind_methods() { -} - -void AnimationCache::set_root(Node *p_root) { - _clear_cache(); - root = p_root; -} - -AnimationCache::AnimationCache() { -} 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..8407e0dd99 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() { @@ -61,7 +60,12 @@ void AnimatedValuesBackup::restore() const { if (entry->bone_idx == -1) { entry->object->set_indexed(entry->subpath, entry->value); } else { - Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value); + Array arr = entry->value; + if (arr.size() == 3) { + Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]); + Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]); + Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[0]); + } } } } @@ -124,8 +128,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 +151,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 +171,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)); } } @@ -243,6 +247,8 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov p_anim->node_cache.resize(a->get_track_count()); + setup_pass++; + for (int i = 0; i < a->get_track_count(); i++) { p_anim->node_cache.write[i] = nullptr; RES resource; @@ -276,46 +282,68 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov node_cache_map[key] = TrackNodeCache(); } - p_anim->node_cache.write[i] = &node_cache_map[key]; - p_anim->node_cache[i]->path = a->track_get_path(i); - p_anim->node_cache[i]->node = child; - p_anim->node_cache[i]->resource = resource; - p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child); + TrackNodeCache *node_cache = &node_cache_map[key]; + p_anim->node_cache.write[i] = node_cache; + + node_cache->path = a->track_get_path(i); + node_cache->node = child; + node_cache->resource = resource; + node_cache->node_2d = Object::cast_to<Node2D>(child); #ifndef _3D_DISABLED - if (a->track_get_type(i) == Animation::TYPE_TRANSFORM3D) { + if (a->track_get_type(i) == Animation::TYPE_POSITION_3D || a->track_get_type(i) == Animation::TYPE_ROTATION_3D || a->track_get_type(i) == Animation::TYPE_SCALE_3D) { // special cases and caches for transform tracks + if (node_cache->last_setup_pass != setup_pass) { + node_cache->loc_used = false; + node_cache->rot_used = false; + node_cache->scale_used = false; + } + // cache node_3d - p_anim->node_cache[i]->node_3d = Object::cast_to<Node3D>(child); + node_cache->node_3d = Object::cast_to<Node3D>(child); // cache skeleton - p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton3D>(child); - if (p_anim->node_cache[i]->skeleton) { + node_cache->skeleton = Object::cast_to<Skeleton3D>(child); + if (node_cache->skeleton) { if (a->track_get_path(i).get_subname_count() == 1) { StringName bone_name = a->track_get_path(i).get_subname(0); - p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name); - if (p_anim->node_cache[i]->bone_idx < 0) { + node_cache->bone_idx = node_cache->skeleton->find_bone(bone_name); + if (node_cache->bone_idx < 0) { // broken track (nonexistent bone) - p_anim->node_cache[i]->skeleton = nullptr; - p_anim->node_cache[i]->node_3d = nullptr; - ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0); + node_cache->skeleton = nullptr; + node_cache->node_3d = nullptr; + ERR_CONTINUE(node_cache->bone_idx < 0); } } else { // no property, just use spatialnode - p_anim->node_cache[i]->skeleton = nullptr; + node_cache->skeleton = nullptr; + } + } + + switch (a->track_get_type(i)) { + case Animation::TYPE_POSITION_3D: { + node_cache->loc_used = true; + } break; + case Animation::TYPE_ROTATION_3D: { + node_cache->rot_used = true; + } break; + case Animation::TYPE_SCALE_3D: { + node_cache->scale_used = true; + } break; + default: { } } } #endif // _3D_DISABLED if (a->track_get_type(i) == Animation::TYPE_VALUE) { - if (!p_anim->node_cache[i]->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) { + if (!node_cache->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) { TrackNodeCache::PropertyAnim pa; pa.subpath = leftover_path; pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; pa.special = SP_NONE; pa.owner = p_anim->node_cache[i]; - if (false && p_anim->node_cache[i]->node_2d) { + if (false && node_cache->node_2d) { if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) { pa.special = SP_NODE2D_POS; } else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) { @@ -324,20 +352,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov pa.special = SP_NODE2D_SCALE; } } - p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa; + node_cache->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa; } } if (a->track_get_type(i) == Animation::TYPE_BEZIER && leftover_path.size()) { - if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) { + if (!node_cache->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) { TrackNodeCache::BezierAnim ba; ba.bezier_property = leftover_path; ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; ba.owner = p_anim->node_cache[i]; - p_anim->node_cache[i]->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba; + node_cache->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba; } } + + node_cache->last_setup_pass = setup_pass; } } @@ -370,17 +400,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } switch (a->track_get_type(i)) { - case Animation::TYPE_TRANSFORM3D: { + case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED if (!nc->node_3d) { continue; } Vector3 loc; - Quaternion rot; - Vector3 scale; - Error err = a->transform_track_interpolate(i, p_time, &loc, &rot, &scale); + Error err = a->position_track_interpolate(i, p_time, &loc); //ERR_CONTINUE(err!=OK); //used for testing, should be removed if (err != OK) { @@ -392,12 +420,63 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double cache_update[cache_update_size++] = nc; nc->accum_pass = accum_pass; nc->loc_accum = loc; - nc->rot_accum = rot; - nc->scale_accum = scale; - + nc->rot_accum = Quaternion(); + nc->scale_accum = Vector3(); } else { nc->loc_accum = nc->loc_accum.lerp(loc, p_interp); + } +#endif // _3D_DISABLED + } break; + case Animation::TYPE_ROTATION_3D: { +#ifndef _3D_DISABLED + if (!nc->node_3d) { + continue; + } + + Quaternion rot; + + Error err = a->rotation_track_interpolate(i, p_time, &rot); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + if (nc->accum_pass != accum_pass) { + ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); + cache_update[cache_update_size++] = nc; + nc->accum_pass = accum_pass; + nc->loc_accum = Vector3(); + nc->rot_accum = rot; + nc->scale_accum = Vector3(); + } else { nc->rot_accum = nc->rot_accum.slerp(rot, p_interp); + } +#endif // _3D_DISABLED + } break; + case Animation::TYPE_SCALE_3D: { +#ifndef _3D_DISABLED + if (!nc->node_3d) { + continue; + } + + Vector3 scale; + + Error err = a->scale_track_interpolate(i, p_time, &scale); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + if (nc->accum_pass != accum_pass) { + ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); + cache_update[cache_update_size++] = nc; + nc->accum_pass = accum_pass; + nc->loc_accum = Vector3(); + nc->rot_accum = Quaternion(); + nc->scale_accum = scale; + } else { nc->scale_accum = nc->scale_accum.lerp(scale, p_interp); } #endif // _3D_DISABLED @@ -856,15 +935,30 @@ void AnimationPlayer::_animation_update_transforms() { TrackNodeCache *nc = cache_update[i]; ERR_CONTINUE(nc->accum_pass != accum_pass); - - t.origin = nc->loc_accum; - t.basis.set_quaternion_scale(nc->rot_accum, nc->scale_accum); #ifndef _3D_DISABLED if (nc->skeleton && nc->bone_idx >= 0) { - nc->skeleton->set_bone_pose(nc->bone_idx, t); + if (nc->loc_used) { + nc->skeleton->set_bone_pose_position(nc->bone_idx, nc->loc_accum); + } + if (nc->rot_used) { + nc->skeleton->set_bone_pose_rotation(nc->bone_idx, nc->rot_accum); + } + if (nc->scale_used) { + nc->skeleton->set_bone_pose_scale(nc->bone_idx, nc->scale_accum); + } + } else if (nc->node_3d) { - nc->node_3d->set_transform(t); + if (nc->loc_used) { + nc->node_3d->set_position(nc->loc_accum); + } + if (nc->rot_used) { + nc->node_3d->set_rotation(nc->rot_accum.get_euler()); + } + if (nc->scale_used) { + nc->node_3d->set_scale(nc->scale_accum); + } } + #endif // _3D_DISABLED } } @@ -1019,8 +1113,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 +1128,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 +1165,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 +1459,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 +1482,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 +1587,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); @@ -1534,7 +1622,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o AnimatedValuesBackup::Entry entry; entry.object = nc->skeleton; entry.bone_idx = nc->bone_idx; - entry.value = nc->skeleton->get_bone_pose(nc->bone_idx); + Array arr; + arr.resize(3); + arr[0] = nc->skeleton->get_bone_pose_position(nc->bone_idx); + arr[1] = nc->skeleton->get_bone_pose_rotation(nc->bone_idx); + arr[2] = nc->skeleton->get_bone_pose_scale(nc->bone_idx); + entry.value = nc; backup->entries.push_back(entry); } else { if (nc->node_3d) { @@ -1545,12 +1638,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/animation/animation_player.h b/scene/animation/animation_player.h index b693e29bdf..f8e72a67b6 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -88,6 +88,8 @@ private: SP_NODE2D_SCALE, }; + uint32_t setup_pass = 1; + struct TrackNodeCache { NodePath path; uint32_t id = 0; @@ -101,6 +103,10 @@ private: int bone_idx = -1; // accumulated transforms + bool loc_used = false; + bool rot_used = false; + bool scale_used = false; + Vector3 loc_accum; Quaternion rot_accum; Vector3 scale_accum; @@ -134,6 +140,7 @@ private: Map<StringName, BezierAnim> bezier_anim; + uint32_t last_setup_pass = 0; TrackNodeCache() {} }; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 9ca8d478b1..dd5fe46223 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -544,13 +544,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { NodePath path = anim->track_get_path(i); Animation::TrackType track_type = anim->track_get_type(i); + Animation::TrackType track_cache_type = track_type; + if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) { + track_cache_type = Animation::TYPE_POSITION_3D; //reference them as position3D tracks, even if they modify rotation or scale + } + TrackCache *track = nullptr; if (track_cache.has(path)) { track = track_cache.get(path); } //if not valid, delete track - if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == nullptr)) { + if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) { playing_caches.erase(track); memdelete(track); track_cache.erase(path); @@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track = track_value; } break; - case Animation::TYPE_TRANSFORM3D: { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { #ifndef _3D_DISABLED Node3D *node_3d = Object::cast_to<Node3D>(child); @@ -597,6 +604,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } TrackCacheTransform *track_xform = memnew(TrackCacheTransform); + track_xform->type = Animation::TYPE_POSITION_3D; track_xform->node_3d = node_3d; track_xform->skeleton = nullptr; @@ -615,6 +623,21 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_xform->object_id = track_xform->object->get_instance_id(); track = track_xform; + + switch (track_type) { + case Animation::TYPE_POSITION_3D: { + track_xform->loc_used = true; + } break; + case Animation::TYPE_ROTATION_3D: { + track_xform->rot_used = true; + } break; + case Animation::TYPE_SCALE_3D: { + track_xform->scale_used = true; + } break; + default: { + } + } + #endif // _3D_DISABLED } break; case Animation::TYPE_METHOD: { @@ -670,6 +693,26 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } track_cache[path] = track; + } else if (track_cache_type == Animation::TYPE_POSITION_3D) { + TrackCacheTransform *track_xform = static_cast<TrackCacheTransform *>(track); + if (track->setup_pass != setup_pass) { + track_xform->loc_used = false; + track_xform->rot_used = false; + track_xform->scale_used = false; + } + switch (track_type) { + case Animation::TYPE_POSITION_3D: { + track_xform->loc_used = true; + } break; + case Animation::TYPE_ROTATION_3D: { + track_xform->rot_used = true; + } break; + case Animation::TYPE_SCALE_3D: { + track_xform->scale_used = true; + } break; + default: { + } + } } track->setup_pass = setup_pass; @@ -831,8 +874,11 @@ void AnimationTree::_process_graph(real_t p_delta) { ERR_CONTINUE(!track_cache.has(path)); TrackCache *track = track_cache[path]; - if (track->type != a->track_get_type(i)) { - continue; //may happen should not + + Animation::TrackType ttype = a->track_get_type(i); + if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) { + //broken animation, but avoid error spamming + continue; } track->root_motion = root_motion_track == path; @@ -848,20 +894,81 @@ void AnimationTree::_process_graph(real_t p_delta) { continue; //nothing to blend } - switch (track->type) { - case Animation::TYPE_TRANSFORM3D: { + switch (ttype) { + case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(); + t->rot = Quaternion(); + t->rot_blend_accum = 0; + t->scale = Vector3(1, 1, 1); + } + if (track->root_motion) { - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = Vector3(); - t->rot = Quaternion(); - t->rot_blend_accum = 0; - t->scale = Vector3(1, 1, 1); + real_t prev_time = time - delta; + if (prev_time < 0) { + if (!a->has_loop()) { + prev_time = 0; + } else { + prev_time = a->get_length() + prev_time; + } } + Vector3 loc[2]; + + if (prev_time > time) { + Error err = a->position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + + a->position_track_interpolate(i, a->get_length(), &loc[1]); + + t->loc += (loc[1] - loc[0]) * blend; + prev_time = 0; + } + + Error err = a->position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + + a->position_track_interpolate(i, time, &loc[1]); + + t->loc += (loc[1] - loc[0]) * blend; + + prev_time = 0; + + } else { + Vector3 loc; + + Error err = a->position_track_interpolate(i, time, &loc); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + t->loc = t->loc.lerp(loc, blend); + } +#endif // _3D_DISABLED + } break; + case Animation::TYPE_ROTATION_3D: { +#ifndef _3D_DISABLED + TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); + + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(); + t->rot = Quaternion(); + t->rot_blend_accum = 0; + t->scale = Vector3(1, 1, 1); + } + + if (track->root_motion) { real_t prev_time = time - delta; if (prev_time < 0) { if (!a->has_loop()) { @@ -871,61 +978,44 @@ void AnimationTree::_process_graph(real_t p_delta) { } } - Vector3 loc[2]; Quaternion rot[2]; - Vector3 scale[2]; if (prev_time > time) { - Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]); + Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]); if (err != OK) { continue; } - a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]); + a->rotation_track_interpolate(i, a->get_length(), &rot[1]); - t->loc += (loc[1] - loc[0]) * blend; - t->scale += (scale[1] - scale[0]) * blend; Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized(); t->rot = (t->rot * q).normalized(); prev_time = 0; } - Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]); + Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]); if (err != OK) { continue; } - a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]); + a->rotation_track_interpolate(i, time, &rot[1]); - t->loc += (loc[1] - loc[0]) * blend; - t->scale += (scale[1] - scale[0]) * blend; Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized(); t->rot = (t->rot * q).normalized(); prev_time = 0; } else { - Vector3 loc; Quaternion rot; - Vector3 scale; - Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale); + Error err = a->rotation_track_interpolate(i, time, &rot); //ERR_CONTINUE(err!=OK); //used for testing, should be removed - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = loc; - t->rot = rot; - t->rot_blend_accum = 0; - t->scale = scale; - } - if (err != OK) { continue; } - t->loc = t->loc.lerp(loc, blend); if (t->rot_blend_accum == 0) { t->rot = rot; t->rot_blend_accum = blend; @@ -934,6 +1024,67 @@ void AnimationTree::_process_graph(real_t p_delta) { t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized(); t->rot_blend_accum = rot_total; } + } +#endif // _3D_DISABLED + } break; + case Animation::TYPE_SCALE_3D: { +#ifndef _3D_DISABLED + TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); + + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(); + t->rot = Quaternion(); + t->rot_blend_accum = 0; + t->scale = Vector3(1, 1, 1); + } + + if (track->root_motion) { + real_t prev_time = time - delta; + if (prev_time < 0) { + if (!a->has_loop()) { + prev_time = 0; + } else { + prev_time = a->get_length() + prev_time; + } + } + + Vector3 scale[2]; + + if (prev_time > time) { + Error err = a->scale_track_interpolate(i, prev_time, &scale[0]); + if (err != OK) { + continue; + } + + a->scale_track_interpolate(i, a->get_length(), &scale[1]); + + t->scale += (scale[1] - scale[0]) * blend; + + prev_time = 0; + } + + Error err = a->scale_track_interpolate(i, prev_time, &scale[0]); + if (err != OK) { + continue; + } + + a->scale_track_interpolate(i, time, &scale[1]); + + t->scale += (scale[1] - scale[0]) * blend; + + prev_time = 0; + + } else { + Vector3 scale; + + Error err = a->scale_track_interpolate(i, time, &scale); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + t->scale = t->scale.lerp(scale, blend); } #endif // _3D_DISABLED @@ -1198,26 +1349,38 @@ void AnimationTree::_process_graph(real_t p_delta) { } switch (track->type) { - case Animation::TYPE_TRANSFORM3D: { + case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - Transform3D xform; - xform.origin = t->loc; - - xform.basis.set_quaternion_scale(t->rot, t->scale); - if (t->root_motion) { + Transform3D xform; + xform.origin = t->loc; + xform.basis.set_quaternion_scale(t->rot, t->scale); + root_motion_transform = xform; - if (t->skeleton && t->bone_idx >= 0) { - root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse(); - } } else if (t->skeleton && t->bone_idx >= 0) { - t->skeleton->set_bone_pose(t->bone_idx, xform); + if (t->loc_used) { + t->skeleton->set_bone_pose_position(t->bone_idx, t->loc); + } + if (t->rot_used) { + t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot); + } + if (t->scale_used) { + t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale); + } } else if (!t->skeleton) { - t->node_3d->set_transform(xform); + if (t->loc_used) { + t->node_3d->set_position(t->loc); + } + if (t->rot_used) { + t->node_3d->set_rotation(t->rot.get_euler()); + } + if (t->scale_used) { + t->node_3d->set_scale(t->scale); + } } #endif // _3D_DISABLED } break; diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 1e0267682e..ed207dfe0c 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -197,13 +197,16 @@ private: Skeleton3D *skeleton = nullptr; #endif // _3D_DISABLED int bone_idx = -1; + bool loc_used = false; + bool rot_used = false; + bool scale_used = false; Vector3 loc; Quaternion rot; real_t rot_blend_accum = 0.0; Vector3 scale; TrackCacheTransform() { - type = Animation::TYPE_TRANSFORM3D; + type = Animation::TYPE_POSITION_3D; } }; diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 0334ba667b..d2fd65d3d9 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -135,7 +135,7 @@ void AudioStreamPlayer::play(float p_from_pos) { Ref<AudioStreamPlayback> stream_playback = stream->instance_playback(); ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback."); - AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos); + AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos, pitch_scale); stream_playbacks.push_back(stream_playback); active.set(); set_process_internal(true); 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/button.cpp b/scene/gui/button.cpp index 9cdf3bf210..e253a27e66 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -427,9 +427,9 @@ Ref<Texture2D> Button::get_icon() const { return icon; } -void Button::set_expand_icon(bool p_expand_icon) { - if (expand_icon != p_expand_icon) { - expand_icon = p_expand_icon; +void Button::set_expand_icon(bool p_enabled) { + if (expand_icon != p_enabled) { + expand_icon = p_enabled; update(); minimum_size_changed(); } @@ -439,9 +439,9 @@ bool Button::is_expand_icon() const { return expand_icon; } -void Button::set_flat(bool p_flat) { - if (flat != p_flat) { - flat = p_flat; +void Button::set_flat(bool p_enabled) { + if (flat != p_enabled) { + flat = p_enabled; update(); } } @@ -450,9 +450,9 @@ bool Button::is_flat() const { return flat; } -void Button::set_clip_text(bool p_clip_text) { - if (clip_text != p_clip_text) { - clip_text = p_clip_text; +void Button::set_clip_text(bool p_enabled) { + if (clip_text != p_enabled) { + clip_text = p_enabled; update(); minimum_size_changed(); } @@ -553,7 +553,7 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text_align"), &Button::get_text_align); ClassDB::bind_method(D_METHOD("set_icon_align", "icon_align"), &Button::set_icon_align); ClassDB::bind_method(D_METHOD("get_icon_align"), &Button::get_icon_align); - ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); + ClassDB::bind_method(D_METHOD("set_expand_icon", "enabled"), &Button::set_expand_icon); ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); BIND_ENUM_CONSTANT(ALIGN_LEFT); diff --git a/scene/gui/button.h b/scene/gui/button.h index 253d079680..fd36cb77af 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -91,13 +91,13 @@ public: void set_icon(const Ref<Texture2D> &p_icon); Ref<Texture2D> get_icon() const; - void set_expand_icon(bool p_expand_icon); + void set_expand_icon(bool p_enabled); bool is_expand_icon() const; - void set_flat(bool p_flat); + void set_flat(bool p_enabled); bool is_flat() const; - void set_clip_text(bool p_clip_text); + void set_clip_text(bool p_enabled); bool get_clip_text() const; void set_text_align(TextAlign p_align); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index c7d1d7ef82..da54903871 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -433,7 +433,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { return; } if (k->is_action("ui_end", true)) { - code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + code_completion_max_lines); + code_completion_current_selected = code_completion_options.size() - 1; update(); accept_event(); return; @@ -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. */ @@ -2930,6 +2937,7 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) { return; } + lines_edited_changed += p_to_line - p_from_line; lines_edited_from = (lines_edited_from == -1) ? MIN(p_from_line, p_to_line) : MIN(lines_edited_from, MIN(p_from_line, p_to_line)); lines_edited_to = (lines_edited_to == -1) ? MAX(p_from_line, p_to_line) : MAX(lines_edited_from, MAX(p_from_line, p_to_line)); } @@ -2956,7 +2964,6 @@ void CodeEdit::_text_changed() { } lc = get_line_count(); - int line_change_size = (lines_edited_to - lines_edited_from); List<int> breakpoints; breakpointed_lines.get_key_list(&breakpoints); for (const int &line : breakpoints) { @@ -2967,8 +2974,8 @@ void CodeEdit::_text_changed() { breakpointed_lines.erase(line); emit_signal(SNAME("breakpoint_toggled"), line); - int next_line = line + line_change_size; - if (next_line < lc && is_line_breakpointed(next_line)) { + int next_line = line + lines_edited_changed; + if (next_line > -1 && next_line < lc && is_line_breakpointed(next_line)) { emit_signal(SNAME("breakpoint_toggled"), next_line); breakpointed_lines[next_line] = true; continue; @@ -2977,6 +2984,7 @@ void CodeEdit::_text_changed() { lines_edited_from = -1; lines_edited_to = -1; + lines_edited_changed = 0; } CodeEdit::CodeEdit() { diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index d8eccb7d32..f0d971dd35 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -240,6 +240,7 @@ private: int line_spacing = 1; /* Callbacks */ + int lines_edited_changed = 0; int lines_edited_from = -1; int lines_edited_to = -1; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 611035fff9..894175d559 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); @@ -1100,9 +1106,9 @@ bool ColorPicker::are_presets_visible() const { void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color); ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color); - ClassDB::bind_method(D_METHOD("set_hsv_mode"), &ColorPicker::set_hsv_mode); + ClassDB::bind_method(D_METHOD("set_hsv_mode", "enabled"), &ColorPicker::set_hsv_mode); ClassDB::bind_method(D_METHOD("is_hsv_mode"), &ColorPicker::is_hsv_mode); - ClassDB::bind_method(D_METHOD("set_raw_mode"), &ColorPicker::set_raw_mode); + ClassDB::bind_method(D_METHOD("set_raw_mode", "enabled"), &ColorPicker::set_raw_mode); ClassDB::bind_method(D_METHOD("is_raw_mode"), &ColorPicker::is_raw_mode); ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode); ClassDB::bind_method(D_METHOD("is_deferred_mode"), &ColorPicker::is_deferred_mode); @@ -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/container.cpp b/scene/gui/container.cpp index 11941529cd..a1bd82f6f7 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -87,6 +87,9 @@ void Container::_sort_children() { return; } + notification(NOTIFICATION_PRE_SORT_CHILDREN); + emit_signal(SceneStringNames::get_singleton()->pre_sort_children); + notification(NOTIFICATION_SORT_CHILDREN); emit_signal(SceneStringNames::get_singleton()->sort_children); pending_sort = false; @@ -174,7 +177,10 @@ void Container::_bind_methods() { ClassDB::bind_method(D_METHOD("queue_sort"), &Container::queue_sort); ClassDB::bind_method(D_METHOD("fit_child_in_rect", "child", "rect"), &Container::fit_child_in_rect); + BIND_CONSTANT(NOTIFICATION_PRE_SORT_CHILDREN); BIND_CONSTANT(NOTIFICATION_SORT_CHILDREN); + + ADD_SIGNAL(MethodInfo("pre_sort_children")); ADD_SIGNAL(MethodInfo("sort_children")); } diff --git a/scene/gui/container.h b/scene/gui/container.h index bce3085f0c..f3ae948556 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -51,7 +51,8 @@ protected: public: enum { - NOTIFICATION_SORT_CHILDREN = 50 + NOTIFICATION_PRE_SORT_CHILDREN = 50, + NOTIFICATION_SORT_CHILDREN = 51, }; void fit_child_in_rect(Control *p_child, const Rect2 &p_rect); 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..be692b6a0c 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); @@ -408,7 +408,7 @@ public: Rect2 get_rect() const; Rect2 get_global_rect() const; Rect2 get_screen_rect() const; - Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server + Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the rendering server Rect2 get_anchorable_rect() const override; void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting. @@ -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 bca3b2059d..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() { @@ -1071,10 +1065,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { if (mm.is_valid() && box_selecting) { box_selecting_to = mm->get_position(); - box_selecting_rect = Rect2(MIN(box_selecting_from.x, box_selecting_to.x), - MIN(box_selecting_from.y, box_selecting_to.y), - ABS(box_selecting_from.x - box_selecting_to.x), - ABS(box_selecting_from.y - box_selecting_to.y)); + box_selecting_rect = Rect2(box_selecting_from.min(box_selecting_to), (box_selecting_from - box_selecting_to).abs()); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -2275,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 d9acbeb828..54e7d8f960 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; } } @@ -242,7 +241,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { _reset_caret_blink_timer(); if (b->is_pressed()) { - accept_event(); //don't pass event further when clicked on text field + accept_event(); // don't pass event further when clicked on text field if (!text.is_empty() && is_editable() && _is_over_clear_button(b->get_position())) { clear_button_status.press_attempt = true; clear_button_status.pressing_inside = true; @@ -260,24 +259,29 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { } else { if (selecting_enabled) { - if (!b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) { + const int triple_click_timeout = 600; + const int triple_click_tolerance = 5; + const bool is_triple_click = !b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && b->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance; + + if (is_triple_click && text.length()) { // Triple-click select all. selection.enabled = true; selection.begin = 0; selection.end = text.length(); selection.double_click = true; - selection.last_dblclk = 0; + last_dblclk = 0; caret_column = selection.begin; } else if (b->is_double_click()) { // Double-click select word. - 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) { + last_dblclk = OS::get_singleton()->get_ticks_msec(); + last_dblclk_pos = b->get_position(); + 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; - selection.last_dblclk = OS::get_singleton()->get_ticks_msec(); caret_column = selection.end; break; } @@ -708,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); @@ -732,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); @@ -778,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 { { @@ -1109,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; @@ -1497,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; } @@ -1555,6 +1551,20 @@ void LineEdit::deselect() { update(); } +bool LineEdit::has_selection() const { + return selection.enabled; +} + +int LineEdit::get_selection_from_column() const { + ERR_FAIL_COND_V(!selection.enabled, -1); + return selection.begin; +} + +int LineEdit::get_selection_to_column() const { + ERR_FAIL_COND_V(!selection.enabled, -1); + return selection.end; +} + void LineEdit::selection_delete() { if (selection.enabled) { delete_text(selection.begin, selection.end); @@ -1921,7 +1931,7 @@ void LineEdit::_shape() { TS->shaped_text_clear(text_rid); String t; - if (text.length() == 0) { + if (text.length() == 0 && ime_text.length() == 0) { t = placeholder_translated; } else if (pass) { t = secret_character.repeat(text.length() + ime_text.length()); @@ -2089,6 +2099,9 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect); + ClassDB::bind_method(D_METHOD("has_selection"), &LineEdit::has_selection); + ClassDB::bind_method(D_METHOD("get_selection_from_column"), &LineEdit::get_selection_from_column); + ClassDB::bind_method(D_METHOD("get_selection_to_column"), &LineEdit::get_selection_to_column); ClassDB::bind_method(D_METHOD("set_text", "text"), &LineEdit::set_text); ClassDB::bind_method(D_METHOD("get_text"), &LineEdit::get_text); ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &LineEdit::get_draw_control_chars); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index e364a79c83..923024dd56 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -136,7 +136,6 @@ private: bool creating = false; bool double_click = false; bool drag_attempt = false; - uint64_t last_dblclk = 0; } selection; struct TextOperation { @@ -153,6 +152,9 @@ private: bool pressing_inside = false; } clear_button_status; + uint64_t last_dblclk = 0; + Vector2 last_dblclk_pos; + bool caret_blink_enabled = false; bool caret_force_displayed = false; bool draw_caret = true; @@ -229,6 +231,9 @@ public: void select_all(); void selection_delete(); void deselect(); + bool has_selection() const; + int get_selection_from_column() const; + int get_selection_to_column() const; void delete_char(); void delete_text(int p_from_column, int p_to_column); diff --git a/scene/gui/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/spin_box.cpp b/scene/gui/spin_box.cpp index 0aec017649..0446e1b402 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -253,14 +253,14 @@ String SpinBox::get_prefix() const { return prefix; } -void SpinBox::set_update_on_text_changed(bool p_update) { - if (update_on_text_changed == p_update) { +void SpinBox::set_update_on_text_changed(bool p_enabled) { + if (update_on_text_changed == p_enabled) { return; } - update_on_text_changed = p_update; + update_on_text_changed = p_enabled; - if (p_update) { + if (p_enabled) { line_edit->connect("text_changed", callable_mp(this, &SpinBox::_text_changed), Vector<Variant>(), CONNECT_DEFERRED); } else { line_edit->disconnect("text_changed", callable_mp(this, &SpinBox::_text_changed)); @@ -271,8 +271,8 @@ bool SpinBox::get_update_on_text_changed() const { return update_on_text_changed; } -void SpinBox::set_editable(bool p_editable) { - line_edit->set_editable(p_editable); +void SpinBox::set_editable(bool p_enabled) { + line_edit->set_editable(p_enabled); } bool SpinBox::is_editable() const { @@ -284,17 +284,15 @@ void SpinBox::apply() { } void SpinBox::_bind_methods() { - //ClassDB::bind_method(D_METHOD("_value_changed"),&SpinBox::_value_changed); - ClassDB::bind_method(D_METHOD("set_align", "align"), &SpinBox::set_align); ClassDB::bind_method(D_METHOD("get_align"), &SpinBox::get_align); ClassDB::bind_method(D_METHOD("set_suffix", "suffix"), &SpinBox::set_suffix); ClassDB::bind_method(D_METHOD("get_suffix"), &SpinBox::get_suffix); ClassDB::bind_method(D_METHOD("set_prefix", "prefix"), &SpinBox::set_prefix); ClassDB::bind_method(D_METHOD("get_prefix"), &SpinBox::get_prefix); - ClassDB::bind_method(D_METHOD("set_editable", "editable"), &SpinBox::set_editable); + ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &SpinBox::set_editable); ClassDB::bind_method(D_METHOD("is_editable"), &SpinBox::is_editable); - ClassDB::bind_method(D_METHOD("set_update_on_text_changed"), &SpinBox::set_update_on_text_changed); + ClassDB::bind_method(D_METHOD("set_update_on_text_changed", "enabled"), &SpinBox::set_update_on_text_changed); ClassDB::bind_method(D_METHOD("get_update_on_text_changed"), &SpinBox::get_update_on_text_changed); ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply); ClassDB::bind_method(D_METHOD("get_line_edit"), &SpinBox::get_line_edit); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 9828b894da..f2299ce1c2 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -82,7 +82,7 @@ public: void set_align(LineEdit::Align p_align); LineEdit::Align get_align() const; - void set_editable(bool p_editable); + void set_editable(bool p_enabled); bool is_editable() const; void set_suffix(const String &p_suffix); @@ -91,7 +91,7 @@ public: void set_prefix(const String &p_prefix); String get_prefix() const; - void set_update_on_text_changed(bool p_update); + void set_update_on_text_changed(bool p_enabled); bool get_update_on_text_changed() const; void apply(); diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 7faa9aca2c..0755a79eee 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -98,29 +98,45 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid()) { Point2 pos = mm->get_position(); - highlight_arrow = -1; if (buttons_visible) { Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); if (is_layout_rtl()) { if (pos.x < decr->get_width()) { - highlight_arrow = 1; + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } } else if (pos.x < incr->get_width() + decr->get_width()) { - highlight_arrow = 0; + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow != -1) { + highlight_arrow = -1; + update(); } } else { int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); if (pos.x > limit_minus_buttons + decr->get_width()) { - highlight_arrow = 1; + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } } else if (pos.x > limit_minus_buttons) { - highlight_arrow = 0; + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow != -1) { + highlight_arrow = -1; + update(); } } } _update_hover(); - update(); return; } @@ -140,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(); } } @@ -148,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; @@ -206,7 +223,7 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) { } int found = -1; - for (int i = offset; i < tabs.size(); i++) { + for (int i = offset; i <= max_drawn_tab; i++) { if (tabs[i].rb_rect.has_point(pos)) { rb_pressing = true; update(); @@ -385,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")); @@ -417,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")); @@ -433,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); } @@ -870,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 @@ -897,7 +914,7 @@ void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) { int Tabs::get_tab_idx_at_point(const Point2 &p_point) const { int hover_now = -1; - for (int i = offset; i < tabs.size(); i++) { + for (int i = offset; i <= max_drawn_tab; i++) { Rect2 rect = get_tab_rect(i); if (rect.has_point(p_point)) { hover_now = i; @@ -1149,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 09899413f2..dc9a3f337e 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); } @@ -107,11 +103,11 @@ void TextEdit::Text::set_direction_and_language(TextServer::Direction p_directio is_dirty = true; } -void TextEdit::Text::set_draw_control_chars(bool p_draw_control_chars) { - if (draw_control_chars == p_draw_control_chars) { +void TextEdit::Text::set_draw_control_chars(bool p_enabled) { + if (draw_control_chars == p_enabled) { return; } - draw_control_chars = p_draw_control_chars; + draw_control_chars = p_enabled; is_dirty = true; } @@ -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; } } @@ -2383,6 +2399,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { } String TextEdit::get_tooltip(const Point2 &p_pos) const { + Object *tooltip_obj = ObjectDB::get_instance(tooltip_obj_id); if (!tooltip_obj) { return Control::get_tooltip(p_pos); } @@ -2405,7 +2422,8 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const { } void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) { - tooltip_obj = p_obj; + ERR_FAIL_NULL(p_obj); + tooltip_obj_id = p_obj->get_instance_id(); tooltip_func = p_function; tooltip_ud = p_udata; } @@ -2554,8 +2572,8 @@ bool TextEdit::is_overtype_mode_enabled() const { return overtype_mode; } -void TextEdit::set_context_menu_enabled(bool p_enable) { - context_menu_enabled = p_enable; +void TextEdit::set_context_menu_enabled(bool p_enabled) { + context_menu_enabled = p_enabled; } bool TextEdit::is_context_menu_enabled() const { @@ -2570,8 +2588,8 @@ bool TextEdit::is_shortcut_keys_enabled() const { return shortcut_keys_enabled; } -void TextEdit::set_virtual_keyboard_enabled(bool p_enable) { - virtual_keyboard_enabled = p_enable; +void TextEdit::set_virtual_keyboard_enabled(bool p_enabled) { + virtual_keyboard_enabled = p_enabled; } bool TextEdit::is_virtual_keyboard_enabled() const { @@ -2731,7 +2749,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 +2764,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) { @@ -3481,8 +3504,8 @@ void TextEdit::set_caret_blink_speed(const float p_speed) { caret_blink_timer->set_wait_time(p_speed); } -void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enable) { - move_caret_on_right_click = p_enable; +void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enabled) { + move_caret_on_right_click = p_enabled; } bool TextEdit::is_move_caret_on_right_click_enabled() const { @@ -3600,10 +3623,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 +3710,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) { - 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; } } @@ -3890,9 +3913,9 @@ Vector<String> TextEdit::get_line_wrapped_text(int p_line) const { /* Viewport */ // Scrolling. -void TextEdit::set_smooth_scroll_enabled(const bool p_enable) { - v_scroll->set_smooth_scroll_enabled(p_enable); - smooth_scroll_enabled = p_enable; +void TextEdit::set_smooth_scroll_enabled(const bool p_enabled) { + v_scroll->set_smooth_scroll_enabled(p_enabled); + smooth_scroll_enabled = p_enabled; } bool TextEdit::is_smooth_scroll_enabled() const { @@ -4151,9 +4174,9 @@ void TextEdit::center_viewport_to_caret() { } /* Minimap */ -void TextEdit::set_draw_minimap(bool p_draw) { - if (draw_minimap != p_draw) { - draw_minimap = p_draw; +void TextEdit::set_draw_minimap(bool p_enabled) { + if (draw_minimap != p_enabled) { + draw_minimap = p_enabled; _update_wrap_at_column(); } update(); @@ -4434,9 +4457,9 @@ bool TextEdit::is_highlight_all_occurrences_enabled() const { return highlight_all_occurrences; } -void TextEdit::set_draw_control_chars(bool p_draw_control_chars) { - if (draw_control_chars != p_draw_control_chars) { - draw_control_chars = p_draw_control_chars; +void TextEdit::set_draw_control_chars(bool p_enabled) { + if (draw_control_chars != p_enabled) { + draw_control_chars = p_enabled; if (menu) { menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); } @@ -4450,8 +4473,8 @@ bool TextEdit::get_draw_control_chars() const { return draw_control_chars; } -void TextEdit::set_draw_tabs(bool p_draw) { - draw_tabs = p_draw; +void TextEdit::set_draw_tabs(bool p_enabled) { + draw_tabs = p_enabled; update(); } @@ -4459,8 +4482,8 @@ bool TextEdit::is_drawing_tabs() const { return draw_tabs; } -void TextEdit::set_draw_spaces(bool p_draw) { - draw_spaces = p_draw; +void TextEdit::set_draw_spaces(bool p_enabled) { + draw_spaces = p_enabled; update(); } @@ -4477,7 +4500,7 @@ void TextEdit::_bind_methods() { // Text properties ClassDB::bind_method(D_METHOD("has_ime_text"), &TextEdit::has_ime_text); - ClassDB::bind_method(D_METHOD("set_editable", "enable"), &TextEdit::set_editable); + ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &TextEdit::set_editable); ClassDB::bind_method(D_METHOD("is_editable"), &TextEdit::is_editable); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextEdit::set_text_direction); @@ -4502,13 +4525,13 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_overtype_mode_enabled", "enabled"), &TextEdit::set_overtype_mode_enabled); ClassDB::bind_method(D_METHOD("is_overtype_mode_enabled"), &TextEdit::is_overtype_mode_enabled); - ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled); + ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enabled"), &TextEdit::set_context_menu_enabled); ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled); - ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &TextEdit::set_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enabled"), &TextEdit::set_shortcut_keys_enabled); ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &TextEdit::is_shortcut_keys_enabled); - ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &TextEdit::set_virtual_keyboard_enabled); + ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enabled"), &TextEdit::set_virtual_keyboard_enabled); ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &TextEdit::is_virtual_keyboard_enabled); // Text manipulation @@ -4743,7 +4766,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("center_viewport_to_caret"), &TextEdit::center_viewport_to_caret); // Minimap - ClassDB::bind_method(D_METHOD("draw_minimap", "draw"), &TextEdit::set_draw_minimap); + ClassDB::bind_method(D_METHOD("set_draw_minimap", "enabled"), &TextEdit::set_draw_minimap); ClassDB::bind_method(D_METHOD("is_drawing_minimap"), &TextEdit::is_drawing_minimap); ClassDB::bind_method(D_METHOD("set_minimap_width", "width"), &TextEdit::set_minimap_width); @@ -4799,16 +4822,16 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_highlight_current_line", "enabled"), &TextEdit::set_highlight_current_line); ClassDB::bind_method(D_METHOD("is_highlight_current_line_enabled"), &TextEdit::is_highlight_current_line_enabled); - ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences); + ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enabled"), &TextEdit::set_highlight_all_occurrences); ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled); ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &TextEdit::get_draw_control_chars); - ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &TextEdit::set_draw_control_chars); + ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enabled"), &TextEdit::set_draw_control_chars); - ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs); + ClassDB::bind_method(D_METHOD("set_draw_tabs", "enabled"), &TextEdit::set_draw_tabs); ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs); - ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces); + ClassDB::bind_method(D_METHOD("set_draw_spaces", "enabled"), &TextEdit::set_draw_spaces); ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces); ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu); @@ -4845,7 +4868,7 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll"); ADD_GROUP("Minimap", "minimap_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "draw_minimap", "is_drawing_minimap"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "set_draw_minimap", "is_drawing_minimap"); ADD_PROPERTY(PropertyInfo(Variant::INT, "minimap_width"), "set_minimap_width", "get_minimap_width"); ADD_GROUP("Caret", "caret_"); @@ -5051,10 +5074,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 +5090,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 +5381,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 +5443,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) { - 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 +6035,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..16c1ee9ff9 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); @@ -182,7 +182,7 @@ private: void set_font_size(int p_font_size); void set_font_features(const Dictionary &p_features); void set_direction_and_language(TextServer::Direction p_direction, const String &p_language); - void set_draw_control_chars(bool p_draw_control_chars); + void set_draw_control_chars(bool p_enabled); int get_line_height() const; int get_line_width(int p_line, int p_wrap_index = -1) const; @@ -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(); @@ -330,7 +330,7 @@ private: int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) const; /* Tooltip. */ - Object *tooltip_obj = nullptr; + ObjectID tooltip_obj_id; StringName tooltip_func; Variant tooltip_ud; @@ -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(); @@ -630,13 +631,13 @@ public: void set_overtype_mode_enabled(const bool p_enabled); bool is_overtype_mode_enabled() const; - void set_context_menu_enabled(bool p_enable); + void set_context_menu_enabled(bool p_enabled); bool is_context_menu_enabled() const; void set_shortcut_keys_enabled(bool p_enabled); bool is_shortcut_keys_enabled() const; - void set_virtual_keyboard_enabled(bool p_enable); + void set_virtual_keyboard_enabled(bool p_enabled); bool is_virtual_keyboard_enabled() const; // Text manipulation @@ -722,7 +723,7 @@ public: void set_caret_blink_speed(const float p_speed); float get_caret_blink_speed() const; - void set_move_caret_on_right_click_enabled(const bool p_enable); + void set_move_caret_on_right_click_enabled(const bool p_enabled); bool is_move_caret_on_right_click_enabled() const; void set_caret_mid_grapheme_enabled(const bool p_enabled); @@ -782,7 +783,7 @@ public: /* Viewport. */ // Scrolling. - void set_smooth_scroll_enabled(const bool p_enable); + void set_smooth_scroll_enabled(const bool p_enabled); bool is_smooth_scroll_enabled() const; void set_scroll_past_end_of_file_enabled(const bool p_enabled); @@ -817,7 +818,7 @@ public: void center_viewport_to_caret(); // Minimap - void set_draw_minimap(bool p_draw); + void set_draw_minimap(bool p_enabled); bool is_drawing_minimap() const; void set_minimap_width(int p_minimap_width); @@ -884,13 +885,13 @@ public: void set_highlight_all_occurrences(const bool p_enabled); bool is_highlight_all_occurrences_enabled() const; - void set_draw_control_chars(bool p_draw_control_chars); + void set_draw_control_chars(bool p_enabled); bool get_draw_control_chars() const; - void set_draw_tabs(bool p_draw); + void set_draw_tabs(bool p_enabled); bool is_drawing_tabs() const; - void set_draw_spaces(bool p_draw); + void set_draw_spaces(bool p_enabled); bool is_drawing_spaces() const; TextEdit(); diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 35a098c6fd..fe11de128a 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -341,7 +341,11 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu } break; case FILL_BILINEAR_LEFT_AND_RIGHT: { double center_mapped_from_real_width = (width_total * 0.5 - topleft.x) / max_middle_real_size * max_middle_texture_size + topleft.x; - double drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x); + double drift_from_unscaled_center = 0; + if (bottomright.y != topleft.y) { // To avoid division by zero. + drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x); + } + src_rect.position.x += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5; src_rect.size.x = width_texture; dst_rect.position.x += (width_total - width_filled) * 0.5; @@ -351,7 +355,11 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu } break; case FILL_BILINEAR_TOP_AND_BOTTOM: { double center_mapped_from_real_width = (width_total * 0.5 - topleft.y) / max_middle_real_size * max_middle_texture_size + topleft.y; - double drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y); + double drift_from_unscaled_center = 0; + if (bottomright.y != topleft.y) { // To avoid division by zero. + drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y); + } + src_rect.position.y += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5; src_rect.size.y = width_texture; dst_rect.position.y += (width_total - width_filled) * 0.5; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 2be59ca04d..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 { @@ -144,6 +140,7 @@ void TreeItem::_change_tree(Tree *p_tree) { /* cell mode */ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); + Cell &c = cells.write[p_column]; c.mode = p_mode; c.min = 0; @@ -155,8 +152,9 @@ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { c.text = ""; c.dirty = true; c.icon_max_w = 0; + c.cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { @@ -167,22 +165,27 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { /* check mode */ void TreeItem::set_checked(int p_column, bool p_checked) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].checked = p_checked; cells.write[p_column].indeterminate = false; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } void TreeItem::set_indeterminate(int p_column, bool p_indeterminate) { ERR_FAIL_INDEX(p_column, cells.size()); + // Prevent uncheck if indeterminate set to false twice if (p_indeterminate == cells[p_column].indeterminate) { return; } + cells.write[p_column].indeterminate = p_indeterminate; cells.write[p_column].checked = false; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } bool TreeItem::is_checked(int p_column) const { @@ -214,8 +217,10 @@ void TreeItem::set_text(int p_column, String p_text) { } cells.write[p_column].step = 0; } + + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } String TreeItem::get_text(int p_column) const { @@ -231,7 +236,7 @@ void TreeItem::set_text_direction(int p_column, Control::TextDirection p_text_di cells.write[p_column].dirty = true; _changed_notify(p_column); } - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } Control::TextDirection TreeItem::get_text_direction(int p_column) const { @@ -241,10 +246,12 @@ Control::TextDirection TreeItem::get_text_direction(int p_column) const { void TreeItem::clear_opentype_features(int p_column) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].opentype_features.clear(); cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_value) { @@ -253,8 +260,9 @@ void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_va if (!cells[p_column].opentype_features.has(tag) || (int)cells[p_column].opentype_features[tag] != p_value) { cells.write[p_column].opentype_features[tag] = p_value; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } } @@ -269,11 +277,13 @@ int TreeItem::get_opentype_feature(int p_column, const String &p_name) const { void TreeItem::set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].st_parser != p_parser) { cells.write[p_column].st_parser = p_parser; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } } @@ -284,10 +294,12 @@ Control::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_ void TreeItem::set_structured_text_bidi_override_options(int p_column, Array p_args) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].st_args = p_args; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } Array TreeItem::get_structured_text_bidi_override_options(int p_column) const { @@ -297,11 +309,13 @@ Array TreeItem::get_structured_text_bidi_override_options(int p_column) const { void TreeItem::set_language(int p_column, const String &p_language) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].language != p_language) { cells.write[p_column].language = p_language; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } } @@ -312,10 +326,11 @@ String TreeItem::get_language(int p_column) const { void TreeItem::set_suffix(int p_column, String p_suffix) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].suffix = p_suffix; + cells.write[p_column].cached_minimum_size_dirty = true; _changed_notify(p_column); - cached_minimum_size_dirty = true; } String TreeItem::get_suffix(int p_column) const { @@ -325,9 +340,11 @@ String TreeItem::get_suffix(int p_column) const { void TreeItem::set_icon(int p_column, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon = p_icon; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } Ref<Texture2D> TreeItem::get_icon(int p_column) const { @@ -337,9 +354,11 @@ Ref<Texture2D> TreeItem::get_icon(int p_column) const { void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon_region = p_icon_region; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } Rect2 TreeItem::get_icon_region(int p_column) const { @@ -360,9 +379,11 @@ Color TreeItem::get_icon_modulate(int p_column) const { void TreeItem::set_icon_max_width(int p_column, int p_max) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon_max_w = p_max; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } int TreeItem::get_icon_max_width(int p_column) const { @@ -474,8 +495,11 @@ void TreeItem::uncollapse_tree() { void TreeItem::set_custom_minimum_height(int p_height) { custom_min_height = p_height; + + for (Cell &c : cells) + c.cached_minimum_size_dirty = true; + _changed_notify(); - cached_minimum_size_dirty = true; } int TreeItem::get_custom_minimum_height() const { @@ -799,8 +823,9 @@ void TreeItem::add_button(int p_column, const Ref<Texture2D> &p_button, int p_id button.disabled = p_disabled; button.tooltip = p_tooltip; cells.write[p_column].buttons.push_back(button); + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } int TreeItem::get_button_count(int p_column) const { @@ -843,8 +868,9 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_butto ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); cells.write[p_column].buttons.write[p_idx].texture = p_button; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) { @@ -859,8 +885,9 @@ void TreeItem::set_button_disabled(int p_column, int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); cells.write[p_column].buttons.write[p_idx].disabled = p_disabled; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } bool TreeItem::is_button_disabled(int p_column, int p_idx) const { @@ -872,9 +899,11 @@ bool TreeItem::is_button_disabled(int p_column, int p_idx) const { void TreeItem::set_editable(int p_column, bool p_editable) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].editable = p_editable; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } bool TreeItem::is_editable(int p_column) { @@ -906,8 +935,9 @@ void TreeItem::clear_custom_color(int p_column) { void TreeItem::set_custom_font(int p_column, const Ref<Font> &p_font) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_font = p_font; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } Ref<Font> TreeItem::get_custom_font(int p_column) const { @@ -917,8 +947,9 @@ Ref<Font> TreeItem::get_custom_font(int p_column) const { void TreeItem::set_custom_font_size(int p_column, int p_font_size) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_font_size = p_font_size; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } int TreeItem::get_custom_font_size(int p_column) const { @@ -961,8 +992,9 @@ Color TreeItem::get_custom_bg_color(int p_column) const { void TreeItem::set_custom_as_button(int p_column, bool p_button) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_button = p_button; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } bool TreeItem::is_custom_set_as_button(int p_column) const { @@ -972,9 +1004,11 @@ bool TreeItem::is_custom_set_as_button(int p_column) const { void TreeItem::set_text_align(int p_column, TextAlign p_align) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].text_align = p_align; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } TreeItem::TextAlign TreeItem::get_text_align(int p_column) const { @@ -984,9 +1018,11 @@ TreeItem::TextAlign TreeItem::get_text_align(int p_column) const { void TreeItem::set_expand_right(int p_column, bool p_enable) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].expand_right = p_enable; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } bool TreeItem::get_expand_right(int p_column) const { @@ -996,8 +1032,11 @@ bool TreeItem::get_expand_right(int p_column) const { void TreeItem::set_disable_folding(bool p_disable) { disable_folding = p_disable; + + for (Cell &c : cells) + c.cached_minimum_size_dirty = true; + _changed_notify(0); - cached_minimum_size_dirty = true; } bool TreeItem::is_folding_disabled() const { @@ -1009,14 +1048,12 @@ Size2 TreeItem::get_minimum_size(int p_column) { Tree *tree = get_tree(); ERR_FAIL_COND_V(!tree, Size2()); - if (cached_minimum_size_dirty) { - Size2 size; + const TreeItem::Cell &cell = cells[p_column]; - // Default offset? - //size.width += (disable_folding || tree->hide_folding) ? tree->cache.hseparation : tree->cache.item_margin; + if (cell.cached_minimum_size_dirty) { + Size2 size; // Text. - const TreeItem::Cell &cell = cells[p_column]; if (!cell.text.is_empty()) { if (cell.dirty) { tree->update_item_cell(this, p_column); @@ -1052,11 +1089,11 @@ Size2 TreeItem::get_minimum_size(int p_column) { size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; } - cached_minimum_size = size; - cached_minimum_size_dirty = false; + cells.write[p_column].cached_minimum_size = size; + cells.write[p_column].cached_minimum_size_dirty = false; } - return cached_minimum_size; + return cell.cached_minimum_size; } Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -1336,11 +1373,9 @@ 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")); - v_scroll->set_custom_step(cache.font->get_height(cache.font_size)); + cache.base_scale = get_theme_default_base_scale(); - for (TreeItem *item = get_root(); item; item = item->get_next()) { - item->cached_minimum_size_dirty = true; - } + v_scroll->set_custom_step(cache.font->get_height(cache.font_size)); } int Tree::compute_item_height(TreeItem *p_item) const { @@ -2009,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; @@ -4001,10 +4030,12 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { int Tree::get_column_minimum_width(int p_column) const { ERR_FAIL_INDEX_V(p_column, columns.size(), -1); + // Use the custom minimum width. int min_width = columns[p_column].custom_min_width; + // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(cache.font->get_string_size(columns[p_column].title).width, min_width); + min_width = MAX(cache.font->get_string_size(columns[p_column].title).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); } if (!columns[p_column].clip_content) { @@ -4029,7 +4060,11 @@ int Tree::get_column_minimum_width(int p_column) const { Size2 item_size = item->get_minimum_size(p_column); if (p_column == 0) { item_size.width += cache.item_margin * depth; + } else { + item_size.width += cache.hseparation; } + + // Check if the item is wider. min_width = MAX(min_width, item_size.width); } } @@ -4069,9 +4104,6 @@ int Tree::get_column_width(int p_column) const { } } - if (p_column < columns.size() - 1) { - column_width += cache.hseparation; - } return column_width; } diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 85fed941dc..6ca9458e9b 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -95,6 +95,9 @@ private: bool expand_right = false; Color icon_color = Color(1, 1, 1); + Size2i cached_minimum_size; + bool cached_minimum_size_dirty = true; + TextAlign text_align = ALIGN_LEFT; Variant meta; @@ -130,9 +133,6 @@ private: bool disable_folding = false; int custom_min_height = 0; - Size2i cached_minimum_size; - bool cached_minimum_size_dirty = true; - TreeItem *parent = nullptr; // parent item TreeItem *prev = nullptr; // previous in list TreeItem *next = nullptr; // next in list @@ -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/http_request.cpp b/scene/main/http_request.cpp index f24d880045..a4fcc04e20 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -104,9 +104,11 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h CharString charstr = p_request_data.utf8(); size_t len = charstr.length(); - raw_data.resize(len); - uint8_t *w = raw_data.ptrw(); - memcpy(w, charstr.ptr(), len); + if (len > 0) { + raw_data.resize(len); + uint8_t *w = raw_data.ptrw(); + memcpy(w, charstr.ptr(), len); + } return request_raw(p_url, p_custom_headers, p_ssl_validate_domain, p_method, raw_data); } @@ -597,7 +599,7 @@ void HTTPRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &HTTPRequest::set_timeout); ClassDB::bind_method(D_METHOD("get_timeout"), &HTTPRequest::get_timeout); - ClassDB::bind_method(D_METHOD("set_download_chunk_size"), &HTTPRequest::set_download_chunk_size); + ClassDB::bind_method(D_METHOD("set_download_chunk_size", "chunk_size"), &HTTPRequest::set_download_chunk_size); ClassDB::bind_method(D_METHOD("get_download_chunk_size"), &HTTPRequest::get_download_chunk_size); ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file"); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index f5d2d2f2a2..189aebb47d 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; } } @@ -540,12 +536,13 @@ bool Node::is_multiplayer_authority() const { /***** RPC CONFIG ********/ -uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel) { +uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local, Multiplayer::TransferMode p_transfer_mode, int p_channel) { for (int i = 0; i < data.rpc_methods.size(); i++) { if (data.rpc_methods[i].name == p_method) { Multiplayer::RPCConfig &nd = data.rpc_methods.write[i]; nd.rpc_mode = p_rpc_mode; nd.transfer_mode = p_transfer_mode; + nd.call_local = p_call_local; nd.channel = p_channel; return i | (1 << 15); } @@ -556,6 +553,7 @@ uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc nd.rpc_mode = p_rpc_mode; nd.transfer_mode = p_transfer_mode; nd.channel = p_channel; + nd.call_local = p_call_local; data.rpc_methods.push_back(nd); return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15); } @@ -1678,10 +1676,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 +1687,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 +1844,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 +1946,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 +1970,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 +2002,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 +2311,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 +2534,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 +2685,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); @@ -2750,7 +2742,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer); ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer); ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer); - ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description); ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description); @@ -2839,7 +2831,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..e59a7a390a 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; @@ -466,7 +466,7 @@ public: int get_multiplayer_authority() const; bool is_multiplayer_authority() const; - uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC + uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local = false, Multiplayer::TransferMode p_transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0); // config a local method for RPC Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const; void rpc(const StringName &p_method, VARIANT_ARG_LIST); // RPC, honors RPCMode, TransferMode, channel 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..a122241cd0 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -570,7 +570,11 @@ void SceneTree::finalize() { root = nullptr; } - // cleanup timers + // In case deletion of some objects was queued when destructing the `root`. + // E.g. if `queue_free()` was called for some node outside the tree when handling NOTIFICATION_PREDELETE for some node in the tree. + _flush_delete_queue(); + + // Cleanup timers. for (Ref<SceneTreeTimer> &timer : timers) { timer->release_connections(); } @@ -1105,7 +1109,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/timer.cpp b/scene/main/timer.cpp index 9e462eb1c8..154e4cf683 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -82,6 +82,7 @@ void Timer::_notification(int p_what) { void Timer::set_wait_time(double p_time) { ERR_FAIL_COND_MSG(p_time <= 0, "Time should be greater than zero."); wait_time = p_time; + update_configuration_warnings(); } double Timer::get_wait_time() const { @@ -179,6 +180,16 @@ void Timer::_set_process(bool p_process, bool p_force) { processing = p_process; } +TypedArray<String> Timer::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (wait_time < 0.05 - CMP_EPSILON) { + warnings.push_back(TTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times.")); + } + + return warnings; +} + void Timer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_wait_time", "time_sec"), &Timer::set_wait_time); ClassDB::bind_method(D_METHOD("get_wait_time"), &Timer::get_wait_time); diff --git a/scene/main/timer.h b/scene/main/timer.h index 2b9faddcb9..e2f34042dd 100644 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -73,6 +73,8 @@ public: double get_time_left() const; + TypedArray<String> get_configuration_warnings() const override; + void set_timer_process_callback(TimerProcessCallback p_callback); TimerProcessCallback get_timer_process_callback() const; Timer(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index b3b743370b..0e62e6e30a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -57,17 +57,17 @@ #include "servers/audio_server.h" void ViewportTexture::setup_local_to_scene() { + Node *local_scene = get_local_scene(); + if (!local_scene) { + return; + } + if (vp) { vp->viewport_textures.erase(this); } vp = nullptr; - Node *local_scene = get_local_scene(); - if (!local_scene) { - return; - } - Node *vpn = local_scene->get_node(path); ERR_FAIL_COND_MSG(!vpn, "ViewportTexture: Path to node is invalid."); @@ -1079,8 +1079,11 @@ Transform2D Viewport::_get_input_pre_xform() const { } Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { - Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); + if (ev.is_null()) { + return ev; // No transformation defined for null event + } + Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); return ev->xformed_by(ai); } @@ -2878,9 +2881,8 @@ bool Viewport::gui_is_dragging() const { void Viewport::set_input_as_handled() { _drop_physics_mouseover(); - if (handle_input_locally) { - local_input_handled = true; - } else { + + if (!handle_input_locally) { ERR_FAIL_COND(!is_inside_tree()); Viewport *vp = this; while (true) { @@ -2892,16 +2894,19 @@ void Viewport::set_input_as_handled() { } vp = vp->get_parent()->get_viewport(); } - vp->set_input_as_handled(); + if (vp != this) { + vp->set_input_as_handled(); + return; + } } + + local_input_handled = true; } bool Viewport::is_input_handled() const { - if (handle_input_locally) { - return local_input_handled; - } else { - const Viewport *vp = this; + if (!handle_input_locally) { ERR_FAIL_COND_V(!is_inside_tree(), false); + const Viewport *vp = this; while (true) { if (Object::cast_to<Window>(vp)) { break; @@ -2911,8 +2916,11 @@ bool Viewport::is_input_handled() const { } vp = vp->get_parent()->get_viewport(); } - return vp->is_input_handled(); + if (vp != this) { + return vp->is_input_handled(); + } } + return local_input_handled; } void Viewport::set_handle_input_locally(bool p_enable) { @@ -3443,13 +3451,16 @@ bool Viewport::is_using_xr() { return use_xr; } -void Viewport::set_scale_3d(const Scale3D p_scale_3d) { - scale_3d = p_scale_3d; +void Viewport::set_scale_3d(float p_scale_3d) { + // Clamp to reasonable values that are actually useful. + // Values above 2.0 don't serve a practical purpose since the viewport + // isn't displayed with mipmaps. + scale_3d = CLAMP(p_scale_3d, 0.1, 2.0); - RS::get_singleton()->viewport_set_scale_3d(viewport, RS::ViewportScale3D(scale_3d)); + RS::get_singleton()->viewport_set_scale_3d(viewport, scale_3d); } -Viewport::Scale3D Viewport::get_scale_3d() const { +float Viewport::get_scale_3d() const { return scale_3d; } @@ -3582,7 +3593,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "scale_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled,75%,50%,33%,25%")), "set_scale_3d", "get_scale_3d"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scale_3d", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scale_3d", "get_scale_3d"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d"); @@ -3626,12 +3637,6 @@ void Viewport::_bind_methods() { ADD_SIGNAL(MethodInfo("size_changed")); ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); - BIND_ENUM_CONSTANT(SCALE_3D_DISABLED); - BIND_ENUM_CONSTANT(SCALE_3D_75_PERCENT); - BIND_ENUM_CONSTANT(SCALE_3D_50_PERCENT); - BIND_ENUM_CONSTANT(SCALE_3D_33_PERCENT); - BIND_ENUM_CONSTANT(SCALE_3D_25_PERCENT); - BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED); BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1); BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_4); @@ -3744,8 +3749,7 @@ Viewport::Viewport() { ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers #ifndef _3D_DISABLED - int scale = GLOBAL_GET("rendering/3d/viewport/scale"); - set_scale_3d((Scale3D)scale); + set_scale_3d(GLOBAL_GET("rendering/3d/viewport/scale")); #endif // _3D_DISABLED set_sdf_oversize(sdf_oversize); // Set to server. diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 3732f9cfd1..1f19ff04c9 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -89,14 +89,6 @@ class Viewport : public Node { GDCLASS(Viewport, Node); public: - enum Scale3D { - SCALE_3D_DISABLED, - SCALE_3D_75_PERCENT, - SCALE_3D_50_PERCENT, - SCALE_3D_33_PERCENT, - SCALE_3D_25_PERCENT - }; - enum ShadowAtlasQuadrantSubdiv { SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED, SHADOW_ATLAS_QUADRANT_SUBDIV_1, @@ -592,7 +584,7 @@ public: #ifndef _3D_DISABLED bool use_xr = false; - Scale3D scale_3d = SCALE_3D_DISABLED; + float scale_3d = 1.0; friend class AudioListener3D; AudioListener3D *audio_listener_3d = nullptr; Set<AudioListener3D *> audio_listener_3d_set; @@ -664,8 +656,8 @@ public: void set_use_xr(bool p_use_xr); bool is_using_xr(); - void set_scale_3d(const Scale3D p_scale_3d); - Scale3D get_scale_3d() const; + void set_scale_3d(float p_scale_3d); + float get_scale_3d() const; #endif // _3D_DISABLED Viewport(); @@ -724,7 +716,6 @@ VARIANT_ENUM_CAST(SubViewport::UpdateMode); VARIANT_ENUM_CAST(Viewport::ShadowAtlasQuadrantSubdiv); VARIANT_ENUM_CAST(Viewport::MSAA); VARIANT_ENUM_CAST(Viewport::ScreenSpaceAA); -VARIANT_ENUM_CAST(Viewport::Scale3D); VARIANT_ENUM_CAST(Viewport::DebugDraw); VARIANT_ENUM_CAST(Viewport::SDFScale); VARIANT_ENUM_CAST(Viewport::SDFOversize); 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/animation.cpp b/scene/resources/animation.cpp index b4eec2530b..6612c5ff3c 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -43,8 +43,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { if (tracks.size() == track && what == "type") { String type = p_value; - if (type == "transform" || type == "transform3d") { - add_track(TYPE_TRANSFORM3D); + if (type == "position_3d") { + add_track(TYPE_POSITION_3D); + } else if (type == "rotation_3d") { + add_track(TYPE_ROTATION_3D); + } else if (type == "scale_3d") { + add_track(TYPE_SCALE_3D); } else if (type == "value") { add_track(TYPE_VALUE); } else if (type == "method") { @@ -75,35 +79,72 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { } else if (what == "enabled") { track_set_enabled(track, p_value); } else if (what == "keys" || what == "key_values") { - if (track_get_type(track) == TYPE_TRANSFORM3D) { - TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]); + if (track_get_type(track) == TYPE_POSITION_3D) { + PositionTrack *tt = static_cast<PositionTrack *>(tracks[track]); Vector<real_t> values = p_value; int vcount = values.size(); - ERR_FAIL_COND_V(vcount % TRANSFORM_TRACK_SIZE, false); + ERR_FAIL_COND_V(vcount % POSITION_TRACK_SIZE, false); const real_t *r = values.ptr(); - int64_t count = vcount / TRANSFORM_TRACK_SIZE; - tt->transforms.resize(count); + int64_t count = vcount / POSITION_TRACK_SIZE; + tt->positions.resize(count); + TKey<Vector3> *tw = tt->positions.ptrw(); for (int i = 0; i < count; i++) { - TKey<TransformKey> &tk = tt->transforms.write[i]; - const real_t *ofs = &r[i * TRANSFORM_TRACK_SIZE]; + TKey<Vector3> &tk = tw[i]; + const real_t *ofs = &r[i * POSITION_TRACK_SIZE]; tk.time = ofs[0]; tk.transition = ofs[1]; - tk.value.loc.x = ofs[2]; - tk.value.loc.y = ofs[3]; - tk.value.loc.z = ofs[4]; + tk.value.x = ofs[2]; + tk.value.y = ofs[3]; + tk.value.z = ofs[4]; + } + } else if (track_get_type(track) == TYPE_ROTATION_3D) { + RotationTrack *rt = static_cast<RotationTrack *>(tracks[track]); + Vector<real_t> values = p_value; + int vcount = values.size(); + ERR_FAIL_COND_V(vcount % ROTATION_TRACK_SIZE, false); + + const real_t *r = values.ptr(); + + int64_t count = vcount / ROTATION_TRACK_SIZE; + rt->rotations.resize(count); + + TKey<Quaternion> *rw = rt->rotations.ptrw(); + for (int i = 0; i < count; i++) { + TKey<Quaternion> &rk = rw[i]; + const real_t *ofs = &r[i * ROTATION_TRACK_SIZE]; + rk.time = ofs[0]; + rk.transition = ofs[1]; + + rk.value.x = ofs[2]; + rk.value.y = ofs[3]; + rk.value.z = ofs[4]; + rk.value.w = ofs[5]; + } + } else if (track_get_type(track) == TYPE_SCALE_3D) { + ScaleTrack *st = static_cast<ScaleTrack *>(tracks[track]); + Vector<real_t> values = p_value; + int vcount = values.size(); + ERR_FAIL_COND_V(vcount % SCALE_TRACK_SIZE, false); - tk.value.rot.x = ofs[5]; - tk.value.rot.y = ofs[6]; - tk.value.rot.z = ofs[7]; - tk.value.rot.w = ofs[8]; + const real_t *r = values.ptr(); + + int64_t count = vcount / SCALE_TRACK_SIZE; + st->scales.resize(count); - tk.value.scale.x = ofs[9]; - tk.value.scale.y = ofs[10]; - tk.value.scale.z = ofs[11]; + TKey<Vector3> *sw = st->scales.ptrw(); + for (int i = 0; i < count; i++) { + TKey<Vector3> &sk = sw[i]; + const real_t *ofs = &r[i * SCALE_TRACK_SIZE]; + sk.time = ofs[0]; + sk.transition = ofs[1]; + + sk.value.x = ofs[2]; + sk.value.y = ofs[3]; + sk.value.z = ofs[4]; } } else if (track_get_type(track) == TYPE_VALUE) { @@ -319,8 +360,14 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { ERR_FAIL_INDEX_V(track, tracks.size(), false); if (what == "type") { switch (track_get_type(track)) { - case TYPE_TRANSFORM3D: - r_ret = "transform"; + case TYPE_POSITION_3D: + r_ret = "position_3d"; + break; + case TYPE_ROTATION_3D: + r_ret = "rotation_3d"; + break; + case TYPE_SCALE_3D: + r_ret = "scale_3d"; break; case TYPE_VALUE: r_ret = "value"; @@ -352,31 +399,64 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { } else if (what == "enabled") { r_ret = track_is_enabled(track); } else if (what == "keys") { - if (track_get_type(track) == TYPE_TRANSFORM3D) { + if (track_get_type(track) == TYPE_POSITION_3D) { Vector<real_t> keys; int kk = track_get_key_count(track); - keys.resize(kk * TRANSFORM_TRACK_SIZE); + keys.resize(kk * POSITION_TRACK_SIZE); real_t *w = keys.ptrw(); int idx = 0; for (int i = 0; i < track_get_key_count(track); i++) { Vector3 loc; - Quaternion rot; - Vector3 scale; - transform_track_get_key(track, i, &loc, &rot, &scale); + position_track_get_key(track, i, &loc); w[idx++] = track_get_key_time(track, i); w[idx++] = track_get_key_transition(track, i); w[idx++] = loc.x; w[idx++] = loc.y; w[idx++] = loc.z; + } + r_ret = keys; + return true; + } else if (track_get_type(track) == TYPE_ROTATION_3D) { + Vector<real_t> keys; + int kk = track_get_key_count(track); + keys.resize(kk * ROTATION_TRACK_SIZE); + + real_t *w = keys.ptrw(); + + int idx = 0; + for (int i = 0; i < track_get_key_count(track); i++) { + Quaternion rot; + rotation_track_get_key(track, i, &rot); + + w[idx++] = track_get_key_time(track, i); + w[idx++] = track_get_key_transition(track, i); w[idx++] = rot.x; w[idx++] = rot.y; w[idx++] = rot.z; w[idx++] = rot.w; + } + r_ret = keys; + return true; + + } else if (track_get_type(track) == TYPE_SCALE_3D) { + Vector<real_t> keys; + int kk = track_get_key_count(track); + keys.resize(kk * SCALE_TRACK_SIZE); + + real_t *w = keys.ptrw(); + + int idx = 0; + for (int i = 0; i < track_get_key_count(track); i++) { + Vector3 scale; + scale_track_get_key(track, i, &scale); + + w[idx++] = track_get_key_time(track, i); + w[idx++] = track_get_key_transition(track, i); w[idx++] = scale.x; w[idx++] = scale.y; w[idx++] = scale.z; @@ -384,7 +464,6 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { r_ret = keys; return true; - } else if (track_get_type(track) == TYPE_VALUE) { const ValueTrack *vt = static_cast<const ValueTrack *>(tracks[track]); @@ -591,10 +670,18 @@ int Animation::add_track(TrackType p_type, int p_at_pos) { } switch (p_type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = memnew(TransformTrack); + case TYPE_POSITION_3D: { + PositionTrack *tt = memnew(PositionTrack); tracks.insert(p_at_pos, tt); } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = memnew(RotationTrack); + tracks.insert(p_at_pos, rt); + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = memnew(ScaleTrack); + tracks.insert(p_at_pos, st); + } break; case TYPE_VALUE: { tracks.insert(p_at_pos, memnew(ValueTrack)); @@ -629,9 +716,19 @@ void Animation::remove_track(int p_track) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - _clear(tt->transforms); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + _clear(tt->positions); + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + _clear(rt->rotations); + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + _clear(st->scales); } break; case TYPE_VALUE: { @@ -672,7 +769,7 @@ int Animation::get_track_count() const { } Animation::TrackType Animation::track_get_type(int p_track) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM3D); + ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_VALUE); return tracks[p_track]->type; } @@ -720,31 +817,6 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const { return tracks[p_track]->loop_wrap; } -// transform -/* -template<class T> -int Animation::_insert_pos(double p_time, T& p_keys) { - // simple, linear time inset that should be fast enough in reality. - - int idx=p_keys.size(); - - while(true) { - - - if (idx==0 || p_keys[idx-1].time < p_time) { - //condition for insertion. - p_keys.insert(idx,T()); - return idx; - } else if (p_keys[idx-1].time == p_time) { - // condition for replacing. - return idx-1; - } - - idx--; - } -} - -*/ template <class T, class V> int Animation::_insert(double p_time, T &p_keys, const V &p_value) { int idx = p_keys.size(); @@ -774,45 +846,153 @@ void Animation::_clear(T &p_keys) { p_keys.clear(); } -Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const { +//// + +int Animation::position_track_insert_key(int p_track, double p_time, const Vector3 &p_position) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, -1); + + PositionTrack *tt = static_cast<PositionTrack *>(t); + + TKey<Vector3> tkey; + tkey.time = p_time; + tkey.value = p_position; + + int ret = _insert(p_time, tt->positions, tkey); + emit_changed(); + return ret; +} + +Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_position) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); Track *t = tracks[p_track]; - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER); - ERR_FAIL_INDEX_V(p_key, tt->transforms.size(), ERR_INVALID_PARAMETER); + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER); - if (r_loc) { - *r_loc = tt->transforms[p_key].value.loc; - } - if (r_rot) { - *r_rot = tt->transforms[p_key].value.rot; - } - if (r_scale) { - *r_scale = tt->transforms[p_key].value.scale; + *r_position = tt->positions[p_key].value; + + return OK; +} + +Error Animation::position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER); + + PositionTrack *tt = static_cast<PositionTrack *>(t); + + bool ok = false; + + Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; } + *r_interpolation = tk; + return OK; +} + +//// + +int Animation::rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, -1); + + RotationTrack *rt = static_cast<RotationTrack *>(t); + + TKey<Quaternion> tkey; + tkey.time = p_time; + tkey.value = p_rotation; + + int ret = _insert(p_time, rt->rotations, tkey); + emit_changed(); + return ret; +} + +Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, rt->rotations.size(), ERR_INVALID_PARAMETER); + + *r_rotation = rt->rotations[p_key].value; return OK; } -int Animation::transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) { +Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER); + + RotationTrack *rt = static_cast<RotationTrack *>(t); + + bool ok = false; + + Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; + } + *r_interpolation = tk; + return OK; +} + +//// + +int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale) { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1); + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, -1); - TransformTrack *tt = static_cast<TransformTrack *>(t); + ScaleTrack *st = static_cast<ScaleTrack *>(t); - TKey<TransformKey> tkey; + TKey<Vector3> tkey; tkey.time = p_time; - tkey.value.loc = p_loc; - tkey.value.rot = p_rot; - tkey.value.scale = p_scale; + tkey.value = p_scale; - int ret = _insert(p_time, tt->transforms, tkey); + int ret = _insert(p_time, st->scales, tkey); emit_changed(); return ret; } +Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, st->scales.size(), ERR_INVALID_PARAMETER); + + *r_scale = st->scales[p_key].value; + + return OK; +} + +Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER); + + ScaleTrack *st = static_cast<ScaleTrack *>(t); + + bool ok = false; + + Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; + } + *r_interpolation = tk; + return OK; +} + void Animation::track_remove_key_at_time(int p_track, double p_time) { int idx = track_find_key(p_track, p_time, true); ERR_FAIL_COND(idx < 0); @@ -824,10 +1004,22 @@ void Animation::track_remove_key(int p_track, int p_idx) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX(p_idx, tt->transforms.size()); - tt->transforms.remove(p_idx); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX(p_idx, tt->positions.size()); + tt->positions.remove(p_idx); + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX(p_idx, rt->rotations.size()); + rt->rotations.remove(p_idx); + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX(p_idx, st->scales.size()); + st->scales.remove(p_idx); } break; case TYPE_VALUE: { @@ -870,13 +1062,37 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - int k = _find(tt->transforms, p_time); - if (k < 0 || k >= tt->transforms.size()) { + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + int k = _find(tt->positions, p_time); + if (k < 0 || k >= tt->positions.size()) { return -1; } - if (tt->transforms[k].time != p_time && p_exact) { + if (tt->positions[k].time != p_time && p_exact) { + return -1; + } + return k; + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + int k = _find(rt->rotations, p_time); + if (k < 0 || k >= rt->rotations.size()) { + return -1; + } + if (rt->rotations[k].time != p_time && p_exact) { + return -1; + } + return k; + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *rt = static_cast<ScaleTrack *>(t); + int k = _find(rt->scales, p_time); + if (k < 0 || k >= rt->scales.size()) { + return -1; + } + if (rt->scales[k].time != p_time && p_exact) { return -1; } return k; @@ -952,24 +1168,21 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - Dictionary d = p_key; - Vector3 loc; - if (d.has("location")) { - loc = d["location"]; - } - - Quaternion rot; - if (d.has("rotation")) { - rot = d["rotation"]; - } + case TYPE_POSITION_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); + int idx = position_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, idx, p_transition); - Vector3 scale; - if (d.has("scale")) { - scale = d["scale"]; - } + } break; + case TYPE_ROTATION_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS)); + int idx = rotation_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, idx, p_transition); - int idx = transform_track_insert_key(p_track, p_time, loc, rot, scale); + } break; + case TYPE_SCALE_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); + int idx = scale_track_insert_key(p_track, p_time, p_key); track_set_key_transition(p_track, idx, p_transition); } break; @@ -1054,9 +1267,17 @@ int Animation::track_get_key_count(int p_track) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - return tt->transforms.size(); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + return tt->positions.size(); + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + return rt->rotations.size(); + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + return st->scales.size(); } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1089,16 +1310,23 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), Variant()); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), Variant()); - Dictionary d; - d["location"] = tt->transforms[p_key_idx].value.loc; - d["rotation"] = tt->transforms[p_key_idx].value.rot; - d["scale"] = tt->transforms[p_key_idx].value.scale; + return tt->positions[p_key_idx].value; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), Variant()); - return d; + return rt->rotations[p_key_idx].value; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), Variant()); + + return st->scales[p_key_idx].value; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1157,10 +1385,20 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1); - return tt->transforms[p_key_idx].time; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1); + return tt->positions[p_key_idx].time; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1); + return rt->rotations[p_key_idx].time; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); + return st->scales[p_key_idx].time; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1202,13 +1440,31 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); - TKey<TransformKey> key = tt->transforms[p_key_idx]; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); + TKey<Vector3> key = tt->positions[p_key_idx]; key.time = p_time; - tt->transforms.remove(p_key_idx); - _insert(p_time, tt->transforms, key); + tt->positions.remove(p_key_idx); + _insert(p_time, tt->positions, key); + return; + } + case TYPE_ROTATION_3D: { + RotationTrack *tt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->rotations.size()); + TKey<Quaternion> key = tt->rotations[p_key_idx]; + key.time = p_time; + tt->rotations.remove(p_key_idx); + _insert(p_time, tt->rotations, key); + return; + } + case TYPE_SCALE_3D: { + ScaleTrack *tt = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->scales.size()); + TKey<Vector3> key = tt->scales[p_key_idx]; + key.time = p_time; + tt->scales.remove(p_key_idx); + _insert(p_time, tt->scales, key); return; } case TYPE_VALUE: { @@ -1266,10 +1522,20 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1); - return tt->transforms[p_key_idx].transition; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1); + return tt->positions[p_key_idx].transition; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1); + return rt->rotations[p_key_idx].transition; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); + return st->scales[p_key_idx].transition; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1302,21 +1568,28 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); + case TYPE_POSITION_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I)); + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); - Dictionary d = p_value; + tt->positions.write[p_key_idx].value = p_value; - if (d.has("location")) { - tt->transforms.write[p_key_idx].value.loc = d["location"]; - } - if (d.has("rotation")) { - tt->transforms.write[p_key_idx].value.rot = d["rotation"]; - } - if (d.has("scale")) { - tt->transforms.write[p_key_idx].value.scale = d["scale"]; - } + } break; + case TYPE_ROTATION_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::QUATERNION) && (p_value.get_type() != Variant::BASIS)); + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, rt->rotations.size()); + + rt->rotations.write[p_key_idx].value = p_value; + + } break; + case TYPE_SCALE_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I)); + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, st->scales.size()); + + st->scales.write[p_key_idx].value = p_value; } break; case TYPE_VALUE: { @@ -1385,10 +1658,20 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); - tt->transforms.write[p_key_idx].transition = p_transition; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); + tt->positions.write[p_key_idx].transition = p_transition; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, rt->rotations.size()); + rt->rotations.write[p_key_idx].transition = p_transition; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, st->scales.size()); + st->scales.write[p_key_idx].transition = p_transition; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1450,15 +1733,6 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const { return middle; } -Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const { - TransformKey ret; - ret.loc = _interpolate(p_a.loc, p_b.loc, p_c); - ret.rot = _interpolate(p_a.rot, p_b.rot, p_c); - ret.scale = _interpolate(p_a.scale, p_b.scale, p_c); - - return ret; -} - Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const { return p_a.lerp(p_b, p_c); } @@ -1477,16 +1751,6 @@ real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) return p_a * (1.0 - p_c) + p_b * p_c; } -Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const { - Animation::TransformKey tk; - - tk.loc = p_a.loc.cubic_interpolate(p_b.loc, p_pre_a.loc, p_post_b.loc, p_c); - tk.scale = p_a.scale.cubic_interpolate(p_b.scale, p_pre_a.scale, p_post_b.scale, p_c); - tk.rot = p_a.rot.cubic_slerp(p_b.rot, p_pre_a.rot, p_post_b.rot, p_c); - - return tk; -} - Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const { return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c); } @@ -1729,36 +1993,6 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol // do a barrel roll } -Error Animation::transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); - Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER); - - TransformTrack *tt = static_cast<TransformTrack *>(t); - - bool ok = false; - - TransformKey tk = _interpolate(tt->transforms, p_time, tt->interpolation, tt->loop_wrap, &ok); - - if (!ok) { - return ERR_UNAVAILABLE; - } - - if (r_loc) { - *r_loc = tk.loc; - } - - if (r_rot) { - *r_rot = tk.rot; - } - - if (r_scale) { - *r_scale = tk.scale; - } - - return OK; -} - Variant Animation::value_track_interpolate(int p_track, double p_time) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), 0); Track *t = tracks[p_track]; @@ -1933,10 +2167,22 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl // handle loop by splitting switch (t->type) { - case TYPE_TRANSFORM3D: { - const TransformTrack *tt = static_cast<const TransformTrack *>(t); - _track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices); - _track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices); + case TYPE_POSITION_3D: { + const PositionTrack *tt = static_cast<const PositionTrack *>(t); + _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); + _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices); + + } break; + case TYPE_ROTATION_3D: { + const RotationTrack *rt = static_cast<const RotationTrack *>(t); + _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); + _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices); + + } break; + case TYPE_SCALE_3D: { + const ScaleTrack *st = static_cast<const ScaleTrack *>(t); + _track_get_key_indices_in_range(st->scales, from_time, length, p_indices); + _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices); } break; case TYPE_VALUE: { @@ -1989,9 +2235,19 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl } switch (t->type) { - case TYPE_TRANSFORM3D: { - const TransformTrack *tt = static_cast<const TransformTrack *>(t); - _track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices); + case TYPE_POSITION_3D: { + const PositionTrack *tt = static_cast<const PositionTrack *>(t); + _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices); + + } break; + case TYPE_ROTATION_3D: { + const RotationTrack *rt = static_cast<const RotationTrack *>(t); + _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices); + + } break; + case TYPE_SCALE_3D: { + const ScaleTrack *st = static_cast<const ScaleTrack *>(t); + _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices); } break; case TYPE_VALUE: { @@ -2608,7 +2864,10 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_set_enabled", "track_idx", "enabled"), &Animation::track_set_enabled); ClassDB::bind_method(D_METHOD("track_is_enabled", "track_idx"), &Animation::track_is_enabled); - ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key); + ClassDB::bind_method(D_METHOD("position_track_insert_key", "track_idx", "time", "position"), &Animation::position_track_insert_key); + ClassDB::bind_method(D_METHOD("rotation_track_insert_key", "track_idx", "time", "rotation"), &Animation::rotation_track_insert_key); + ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key); + ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1)); ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key); ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time); @@ -2628,7 +2887,6 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap); ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap); - ClassDB::bind_method(D_METHOD("transform_track_interpolate", "track_idx", "time_sec"), &Animation::_transform_track_interpolate); ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode); ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode); @@ -2682,7 +2940,9 @@ void Animation::_bind_methods() { ADD_SIGNAL(MethodInfo("tracks_changed")); BIND_ENUM_CONSTANT(TYPE_VALUE); - BIND_ENUM_CONSTANT(TYPE_TRANSFORM3D); + BIND_ENUM_CONSTANT(TYPE_POSITION_3D); + BIND_ENUM_CONSTANT(TYPE_ROTATION_3D); + BIND_ENUM_CONSTANT(TYPE_SCALE_3D); BIND_ENUM_CONSTANT(TYPE_METHOD); BIND_ENUM_CONSTANT(TYPE_BEZIER); BIND_ENUM_CONSTANT(TYPE_AUDIO); @@ -2709,193 +2969,215 @@ void Animation::clear() { emit_signal(SceneStringNames::get_singleton()->tracks_changed); } -bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm) { - real_t c = (t1.time - t0.time) / (t2.time - t0.time); - real_t t[3] = { -1, -1, -1 }; +bool Animation::_position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm) { + const Vector3 &v0 = t0.value; + const Vector3 &v1 = t1.value; + const Vector3 &v2 = t2.value; - { //translation + if (v0.is_equal_approx(v2)) { + //0 and 2 are close, let's see if 1 is close + if (!v0.is_equal_approx(v1)) { + //not close, not optimizable + return false; + } - const Vector3 &v0 = t0.value.loc; - const Vector3 &v1 = t1.value.loc; - const Vector3 &v2 = t2.value.loc; + } else { + Vector3 pd = (v2 - v0); + real_t d0 = pd.dot(v0); + real_t d1 = pd.dot(v1); + real_t d2 = pd.dot(v2); + if (d1 < d0 || d1 > d2) { + return false; + } - if (v0.is_equal_approx(v2)) { - //0 and 2 are close, let's see if 1 is close - if (!v0.is_equal_approx(v1)) { - //not close, not optimizable - return false; - } + Vector3 s[2] = { v0, v2 }; + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); - } else { - Vector3 pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; - } + if (d > pd.length() * p_allowed_linear_err) { + return false; //beyond allowed error for collinearity + } - Vector3 s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); + if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_allowed_angular_error) { + return false; + } + } - if (d > pd.length() * p_alowed_linear_err) { - return false; //beyond allowed error for collinearity - } + return true; +} - if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) { - return false; - } +bool Animation::_rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle) { + const Quaternion &q0 = t0.value; + const Quaternion &q1 = t1.value; + const Quaternion &q2 = t2.value; + + //localize both to rotation from q0 - t[0] = (d1 - d0) / (d2 - d0); + if (q0.is_equal_approx(q2)) { + if (!q0.is_equal_approx(q1)) { + return false; } - } - { //rotation + } else { + Quaternion r02 = (q0.inverse() * q2).normalized(); + Quaternion r01 = (q0.inverse() * q1).normalized(); - const Quaternion &q0 = t0.value.rot; - const Quaternion &q1 = t1.value.rot; - const Quaternion &q2 = t2.value.rot; + Vector3 v02, v01; + real_t a02, a01; - //localize both to rotation from q0 + r02.get_axis_angle(v02, a02); + r01.get_axis_angle(v01, a01); - if (q0.is_equal_approx(q2)) { - if (!q0.is_equal_approx(q1)) { - return false; - } + if (Math::abs(a02) > p_max_optimizable_angle) { + return false; + } - } else { - Quaternion r02 = (q0.inverse() * q2).normalized(); - Quaternion r01 = (q0.inverse() * q1).normalized(); + if (v01.dot(v02) < 0) { + //make sure both rotations go the same way to compare + v02 = -v02; + a02 = -a02; + } - Vector3 v02, v01; - real_t a02, a01; + real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI; + if (err_01 > p_allowed_angular_error) { + //not rotating in the same axis + return false; + } - r02.get_axis_angle(v02, a02); - r01.get_axis_angle(v01, a01); + if (a01 * a02 < 0) { + //not rotating in the same direction + return false; + } - if (Math::abs(a02) > p_max_optimizable_angle) { - return false; - } + real_t tr = a01 / a02; + if (tr < 0 || tr > 1) { + return false; //rotating too much or too less + } + } - if (v01.dot(v02) < 0) { - //make sure both rotations go the same way to compare - v02 = -v02; - a02 = -a02; - } + return true; +} - real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI; - if (err_01 > p_alowed_angular_err) { - //not rotating in the same axis - return false; - } +bool Animation::_scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error) { + const Vector3 &v0 = t0.value; + const Vector3 &v1 = t1.value; + const Vector3 &v2 = t2.value; - if (a01 * a02 < 0) { - //not rotating in the same direction - return false; - } + if (v0.is_equal_approx(v2)) { + //0 and 2 are close, let's see if 1 is close + if (!v0.is_equal_approx(v1)) { + //not close, not optimizable + return false; + } - real_t tr = a01 / a02; - if (tr < 0 || tr > 1) { - return false; //rotating too much or too less - } + } else { + Vector3 pd = (v2 - v0); + real_t d0 = pd.dot(v0); + real_t d1 = pd.dot(v1); + real_t d2 = pd.dot(v2); + if (d1 < d0 || d1 > d2) { + return false; //beyond segment range + } - t[1] = tr; + Vector3 s[2] = { v0, v2 }; + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); + + if (d > pd.length() * p_allowed_linear_error) { + return false; //beyond allowed error for colinearity } } - { //scale + return true; +} - const Vector3 &v0 = t0.value.scale; - const Vector3 &v1 = t1.value.scale; - const Vector3 &v2 = t2.value.scale; +void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) { + ERR_FAIL_INDEX(p_idx, tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D); + PositionTrack *tt = static_cast<PositionTrack *>(tracks[p_idx]); + bool prev_erased = false; + TKey<Vector3> first_erased; - if (v0.is_equal_approx(v2)) { - //0 and 2 are close, let's see if 1 is close - if (!v0.is_equal_approx(v1)) { - //not close, not optimizable - return false; - } + Vector3 norm; - } else { - Vector3 pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; //beyond segment range - } + for (int i = 1; i < tt->positions.size() - 1; i++) { + TKey<Vector3> &t0 = tt->positions.write[i - 1]; + TKey<Vector3> &t1 = tt->positions.write[i]; + TKey<Vector3> &t2 = tt->positions.write[i + 1]; - Vector3 s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); + bool erase = _position_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, norm); + if (erase && !prev_erased) { + norm = (t2.value - t1.value).normalized(); + } + + if (prev_erased && !_position_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, norm)) { + //avoid error to go beyond first erased key + erase = false; + } - if (d > pd.length() * p_alowed_linear_err) { - return false; //beyond allowed error for collinearity + if (erase) { + if (!prev_erased) { + first_erased = t1; + prev_erased = true; } - t[2] = (d1 - d0) / (d2 - d0); + tt->positions.remove(i); + i--; + + } else { + prev_erased = false; + norm = Vector3(); } } +} - bool erase = false; - if (t[0] == -1 && t[1] == -1 && t[2] == -1) { - erase = true; - } else { - erase = true; - real_t lt = -1.0; - for (int j = 0; j < 3; j++) { - //search for t on first, one must be it - if (t[j] != -1) { - lt = t[j]; //official t - //validate rest - for (int k = j + 1; k < 3; k++) { - if (t[k] == -1) { - continue; - } +void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { + ERR_FAIL_INDEX(p_idx, tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_ROTATION_3D); + RotationTrack *tt = static_cast<RotationTrack *>(tracks[p_idx]); + bool prev_erased = false; + TKey<Quaternion> first_erased; - if (Math::abs(lt - t[k]) > p_alowed_linear_err) { - erase = false; - break; - } - } - break; - } - } + for (int i = 1; i < tt->rotations.size() - 1; i++) { + TKey<Quaternion> &t0 = tt->rotations.write[i - 1]; + TKey<Quaternion> &t1 = tt->rotations.write[i]; + TKey<Quaternion> &t2 = tt->rotations.write[i + 1]; - ERR_FAIL_COND_V(lt == -1, false); + bool erase = _rotation_track_optimize_key(t0, t1, t2, p_allowed_angular_err, p_max_optimizable_angle); + + if (prev_erased && !_rotation_track_optimize_key(t0, first_erased, t2, p_allowed_angular_err, p_max_optimizable_angle)) { + //avoid error to go beyond first erased key + erase = false; + } if (erase) { - if (Math::abs(lt - c) > p_alowed_linear_err) { - //todo, evaluate changing the transition if this fails? - //this could be done as a second pass and would be - //able to optimize more - erase = false; + if (!prev_erased) { + first_erased = t1; + prev_erased = true; } + + tt->rotations.remove(i); + i--; + + } else { + prev_erased = false; } } - - return erase; } -void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { +void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) { ERR_FAIL_INDEX(p_idx, tracks.size()); - ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D); - TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_SCALE_3D); + ScaleTrack *tt = static_cast<ScaleTrack *>(tracks[p_idx]); bool prev_erased = false; - TKey<TransformKey> first_erased; + TKey<Vector3> first_erased; - Vector3 norm; + for (int i = 1; i < tt->scales.size() - 1; i++) { + TKey<Vector3> &t0 = tt->scales.write[i - 1]; + TKey<Vector3> &t1 = tt->scales.write[i]; + TKey<Vector3> &t2 = tt->scales.write[i + 1]; - for (int i = 1; i < tt->transforms.size() - 1; i++) { - TKey<TransformKey> &t0 = tt->transforms.write[i - 1]; - TKey<TransformKey> &t1 = tt->transforms.write[i]; - TKey<TransformKey> &t2 = tt->transforms.write[i + 1]; + bool erase = _scale_track_optimize_key(t0, t1, t2, p_allowed_linear_err); - bool erase = _transform_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm); - if (erase && !prev_erased) { - norm = (t2.value.loc - t1.value.loc).normalized(); - } - - if (prev_erased && !_transform_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm)) { + if (prev_erased && !_scale_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) { //avoid error to go beyond first erased key erase = false; } @@ -2906,20 +3188,23 @@ void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err prev_erased = true; } - tt->transforms.remove(i); + tt->scales.remove(i); i--; } else { prev_erased = false; - norm = Vector3(); } } } void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { for (int i = 0; i < tracks.size(); i++) { - if (tracks[i]->type == TYPE_TRANSFORM3D) { - _transform_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle); + if (tracks[i]->type == TYPE_POSITION_3D) { + _position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err); + } else if (tracks[i]->type == TYPE_ROTATION_3D) { + _rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle); + } else if (tracks[i]->type == TYPE_SCALE_3D) { + _scale_track_optimize(i, p_allowed_linear_err); } } } diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 9a410bd566..614e12a560 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -42,7 +42,9 @@ class Animation : public Resource { public: enum TrackType { TYPE_VALUE, ///< Set a value in a property, can be interpolated. - TYPE_TRANSFORM3D, ///< Transform a node or a bone. + TYPE_POSITION_3D, ///< Position 3D track + TYPE_ROTATION_3D, ///< Rotation 3D track + TYPE_SCALE_3D, ///< Scale 3D track TYPE_METHOD, ///< Call any method on a specific node. TYPE_BEZIER, ///< Bezier curve TYPE_AUDIO, @@ -86,21 +88,31 @@ private: T value; }; - struct TransformKey { - Vector3 loc; - Quaternion rot; - Vector3 scale; + const int32_t POSITION_TRACK_SIZE = 5; + const int32_t ROTATION_TRACK_SIZE = 6; + const int32_t SCALE_TRACK_SIZE = 5; + + /* POSITION TRACK */ + + struct PositionTrack : public Track { + Vector<TKey<Vector3>> positions; + + PositionTrack() { type = TYPE_POSITION_3D; } }; - // Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey. - const int32_t TRANSFORM_TRACK_SIZE = 12; + /* ROTATION TRACK */ - /* TRANSFORM TRACK */ + struct RotationTrack : public Track { + Vector<TKey<Quaternion>> rotations; - struct TransformTrack : public Track { - Vector<TKey<TransformKey>> transforms; + RotationTrack() { type = TYPE_ROTATION_3D; } + }; - TransformTrack() { type = TYPE_TRANSFORM3D; } + /* SCALE TRACK */ + + struct ScaleTrack : public Track { + Vector<TKey<Vector3>> scales; + ScaleTrack() { type = TYPE_SCALE_3D; } }; /* PROPERTY VALUE TRACK */ @@ -186,14 +198,11 @@ private: template <class K> inline int _find(const Vector<K> &p_keys, double p_time) const; - _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const; - _FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const; _FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const; _FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const; _FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const; - _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const; _FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const; _FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const; _FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const; @@ -214,18 +223,6 @@ private: // bind helpers private: - Array _transform_track_interpolate(int p_track, double p_time) const { - Vector3 loc; - Quaternion rot; - Vector3 scale; - transform_track_interpolate(p_track, p_time, &loc, &rot, &scale); - Array ret; - ret.push_back(loc); - ret.push_back(rot); - ret.push_back(scale); - return ret; - } - Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const { List<int> idxs; value_track_get_key_indices(p_track, p_time, p_delta, &idxs); @@ -247,8 +244,13 @@ private: return idxr; } - bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm); - void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125); + bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm); + bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle); + bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error); + + void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err); + void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle); + void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -294,8 +296,18 @@ public: double track_get_key_time(int p_track, int p_key_idx) const; real_t track_get_key_transition(int p_track, int p_key_idx) const; - int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3()); - Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const; + int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position); + Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const; + Error position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const; + + int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation); + Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const; + Error rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const; + + int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale); + Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const; + Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const; + void track_set_interpolation_type(int p_track, InterpolationType p_interp); InterpolationType track_get_interpolation_type(int p_track) const; @@ -324,8 +336,6 @@ public: void track_set_interpolation_loop_wrap(int p_track, bool p_enable); bool track_get_interpolation_loop_wrap(int p_track) const; - Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const; - Variant value_track_interpolate(int p_track, double p_time) const; void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const; void value_track_set_update_mode(int p_track, UpdateMode p_mode); diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index fa95ab0e79..b291724c4c 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -135,7 +135,7 @@ void CanvasItemMaterial::_update_shader() { code += " particle_frame = mod(particle_frame, particle_total_frames);\n"; code += " }"; code += " UV /= vec2(h_frames, v_frames);\n"; - code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n"; code += "}\n"; } 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/environment.cpp b/scene/resources/environment.cpp index cab6c0378a..be0d3a140e 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -173,23 +173,13 @@ Environment::ReflectionSource Environment::get_reflection_source() const { return reflection_source; } -void Environment::set_ao_color(const Color &p_color) { - ao_color = p_color; - _update_ambient_light(); -} - -Color Environment::get_ao_color() const { - return ao_color; -} - void Environment::_update_ambient_light() { RS::get_singleton()->environment_set_ambient_light( environment, ambient_color, RS::EnvironmentAmbientSource(ambient_source), ambient_energy, - ambient_sky_contribution, RS::EnvironmentReflectionSource(reflection_source), - ao_color); + ambient_sky_contribution, RS::EnvironmentReflectionSource(reflection_source)); } // Tonemap @@ -302,7 +292,7 @@ int Environment::get_ssr_max_steps() const { } void Environment::set_ssr_fade_in(float p_fade_in) { - ssr_fade_in = p_fade_in; + ssr_fade_in = MAX(p_fade_in, 0.0f); _update_ssr(); } @@ -311,7 +301,7 @@ float Environment::get_ssr_fade_in() const { } void Environment::set_ssr_fade_out(float p_fade_out) { - ssr_fade_out = p_fade_out; + ssr_fade_out = MAX(p_fade_out, 0.0f); _update_ssr(); } @@ -1092,15 +1082,12 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_ambient_light_sky_contribution"), &Environment::get_ambient_light_sky_contribution); ClassDB::bind_method(D_METHOD("set_reflection_source", "source"), &Environment::set_reflection_source); ClassDB::bind_method(D_METHOD("get_reflection_source"), &Environment::get_reflection_source); - ClassDB::bind_method(D_METHOD("set_ao_color", "color"), &Environment::set_ao_color); - ClassDB::bind_method(D_METHOD("get_ao_color"), &Environment::get_ao_color); ADD_GROUP("Ambient Light", "ambient_light_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Color,Sky"), "set_ambient_source", "get_ambient_source"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_color"), "set_ambient_light_color", "get_ambient_light_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_light_sky_contribution", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ambient_light_sky_contribution", "get_ambient_light_sky_contribution"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_light_energy", "get_ambient_light_energy"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_occlusion_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ao_color", "get_ao_color"); ADD_GROUP("Reflected Light", "reflected_light_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "reflected_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Sky"), "set_reflection_source", "get_reflection_source"); @@ -1304,7 +1291,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater"), "set_fog_height", "get_fog_height"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "0,128,0.001,or_greater"), "set_fog_height_density", "get_fog_height_density"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_lesser,or_greater"), "set_fog_height_density", "get_fog_height_density"); ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled); ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 0df2c3cc27..46842754f4 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -116,7 +116,6 @@ private: float ambient_energy = 1.0; float ambient_sky_contribution = 1.0; ReflectionSource reflection_source = REFLECTION_SOURCE_BG; - Color ao_color; void _update_ambient_light(); // Tonemap @@ -250,8 +249,6 @@ public: float get_ambient_light_sky_contribution() const; void set_reflection_source(ReflectionSource p_source); ReflectionSource get_reflection_source() const; - void set_ao_color(const Color &p_color); - Color get_ao_color() const; // Tonemap void set_tonemapper(ToneMapper p_tone_mapper); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 9b403a18f0..04e2b0dc70 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -116,8 +116,8 @@ void FontData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale); ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale); - ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing"), &FontData::set_spacing); - ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size"), &FontData::get_spacing); + ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing_type", "value"), &FontData::set_spacing); + ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size", "spacing_type"), &FontData::get_spacing); ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count); ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures); @@ -175,7 +175,7 @@ void FontData::_bind_methods() { ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char); ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars); - ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index); + ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontData::get_glyph_index); ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list); ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list); @@ -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..2572c5de33 --- /dev/null +++ b/scene/resources/importer_mesh.cpp @@ -0,0 +1,1247 @@ +/*************************************************************************/ +/* 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) { + _split_normals(arrays, p_indices, p_normals); + + for (BlendShape &blend_shape : blend_shape_data) { + _split_normals(blend_shape.arrays, p_indices, p_normals); + } +} + +void ImporterMesh::Surface::_split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) { + ERR_FAIL_COND(r_arrays.size() != RS::ARRAY_MAX); + + const PackedVector3Array &vertices = r_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 < r_arrays.size(); i++) { + if (i == RS::ARRAY_INDEX) { + continue; + } + + if (r_arrays[i].get_type() == Variant::NIL) { + continue; + } + + switch (r_arrays[i].get_type()) { + case Variant::PACKED_VECTOR3_ARRAY: { + PackedVector3Array data = r_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]]; + } + } + r_arrays[i] = data; + } break; + case Variant::PACKED_VECTOR2_ARRAY: { + PackedVector2Array data = r_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]]; + } + r_arrays[i] = data; + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + PackedFloat32Array data = r_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); + } + r_arrays[i] = data; + } break; + case Variant::PACKED_INT32_ARRAY: { + PackedInt32Array data = r_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); + } + r_arrays[i] = data; + } break; + case Variant::PACKED_BYTE_ARRAY: { + PackedByteArray data = r_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); + } + r_arrays[i] = data; + } break; + case Variant::PACKED_COLOR_ARRAY: { + PackedColorArray data = r_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]]; + } + r_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; + } + + 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..8576312a8a --- /dev/null +++ b/scene/resources/importer_mesh.h @@ -0,0 +1,133 @@ +/*************************************************************************/ +/* 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); + static void _split_normals(Array &r_arrays, 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..abb3381c4e 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(); @@ -815,7 +811,7 @@ void BaseMaterial3D::_update_shader() { code += " particle_frame = mod(particle_frame, particle_total_frames);\n"; code += " }"; code += " UV /= vec2(h_frames, v_frames);\n"; - code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n"; } break; case BILLBOARD_MAX: break; // Internal value, skip. 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..7ffe0b03e1 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 @@ -1577,7 +1577,7 @@ void ArrayMesh::regen_normal_maps() { } //dirty hack -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) = NULL; +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) = nullptr; struct ArrayMeshLightmapSurface { Ref<Material> material; 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..60cda637ca 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,11 +384,11 @@ 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; - } else if (p_node->get_owner() && p_node->get_owner() != p_owner && p_owner->is_editable_instance(p_node->get_owner())) { + } else if (p_node->get_owner() && p_owner->is_ancestor_of(p_node->get_owner()) && p_owner->is_editable_instance(p_node->get_owner())) { // Node is part 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/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index e7da41db9d..f8be00f5fb 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -1390,6 +1390,12 @@ void QuadMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset"); } +uint32_t QuadMesh::surface_get_format(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, 1, 0); + + return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV; +} + QuadMesh::QuadMesh() { primitive_type = PRIMITIVE_TRIANGLES; } @@ -1460,7 +1466,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const { } else { Vector3 p = Vector3(x * radius * w, y, z * radius * w); points.push_back(p); - Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale); + Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale); normals.push_back(normal.normalized()); }; ADD_TANGENT(z, 0.0, -x, 1.0) diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 7915cb0028..d447dad97a 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -285,6 +285,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + virtual uint32_t surface_get_format(int p_idx) const override; + QuadMesh(); void set_size(const Size2 &p_size); diff --git a/scene/resources/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/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index e533fb054a..7ac40b497d 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -96,37 +96,25 @@ float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, floa p_max_bound = Math_TAU + p_max_bound; } if (p_min_bound > p_max_bound) { - float tmp = p_min_bound; - p_min_bound = p_max_bound; - p_max_bound = tmp; + SWAP(p_min_bound, p_max_bound); } + bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound); + bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound); + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (p_invert == false) { - if (p_angle < p_min_bound || p_angle > p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } - } - } else { - if (p_angle > p_min_bound && p_angle < p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); + if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) { + Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); + Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + p_angle = p_min_bound; + } else { + p_angle = p_max_bound; } } + return p_angle; } @@ -152,9 +140,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b arc_angle_max = (Math_PI * 2) + arc_angle_max; } if (arc_angle_min > arc_angle_max) { - float tmp = arc_angle_min; - arc_angle_min = arc_angle_max; - arc_angle_max = tmp; + SWAP(arc_angle_min, arc_angle_max); } arc_angle_min += p_operation_bone->get_bone_angle(); arc_angle_max += p_operation_bone->get_bone_angle(); diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp index 6e9429034f..3b5c555f89 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.cpp +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -247,7 +247,7 @@ void SkeletonModification2DFABRIK::chain_backwards() { } float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); - float length = current_bone2d_node_length / (previous_pose.get_origin() - current_pose.get_origin()).length(); + float length = current_bone2d_node_length / (current_pose.get_origin().distance_to(previous_pose.get_origin())); Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length); current_pose.set_origin(finish_position); @@ -268,7 +268,7 @@ void SkeletonModification2DFABRIK::chain_forwards() { Transform2D next_pose = fabrik_transform_chain[i + 1]; float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); - float length = current_bone2d_node_length / (current_pose.get_origin() - next_pose.get_origin()).length(); + float length = current_bone2d_node_length / (next_pose.get_origin().distance_to(current_pose.get_origin())); Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length); current_pose.set_origin(finish_position); diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp index ee02ede2d5..b476952d86 100644 --- a/scene/resources/skeleton_modification_3d.cpp +++ b/scene/resources/skeleton_modification_3d.cpp @@ -72,37 +72,25 @@ real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, r p_max_bound = Math_TAU + p_max_bound; } if (p_min_bound > p_max_bound) { - real_t tmp = p_min_bound; - p_min_bound = p_max_bound; - p_max_bound = tmp; + SWAP(p_min_bound, p_max_bound); } + bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound); + bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound); + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (p_invert == false) { - if (p_angle < p_min_bound || p_angle > p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } - } - } else { - if (p_angle > p_min_bound && p_angle < p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } + if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) { + Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); + Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); + + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + p_angle = p_min_bound; + } else { + p_angle = p_max_bound; } } + return p_angle; } diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp index 69f75eb7b5..20ebbda256 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.cpp +++ b/scene/resources/skeleton_modification_3d_fabrik.cpp @@ -168,7 +168,7 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) { // Apply magnet positions: if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) { int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx); - Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx) * stack->skeleton->get_bone_rest(parent_bone_idx)); + Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx)); local_pose_override.origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position); } else { local_pose_override.origin += fabrik_data_chain[i].magnet_position; @@ -232,7 +232,7 @@ void SkeletonModification3DFABRIK::chain_backwards() { int current_bone_idx = fabrik_data_chain[i].bone_idx; Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx)); - real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length(); + real_t length = fabrik_data_chain[i].length / (current_trans.origin.distance_to(next_bone_trans.origin)); current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length); // Apply it back to the skeleton @@ -253,7 +253,7 @@ void SkeletonModification3DFABRIK::chain_forwards() { int next_bone_idx = fabrik_data_chain[i + 1].bone_idx; Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx)); - real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length(); + real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin.distance_to(current_trans.origin)); next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length); // Apply it back to the skeleton diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp index ae7a3bab7e..c1a71148a7 100644 --- a/scene/resources/skeleton_modification_3d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp @@ -455,7 +455,7 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() { joint_two_length = 0; for (int i = 0; i < bone_two_children.size(); i++) { joint_two_length += bone_two_rest_trans.origin.distance_to( - stack->skeleton->local_pose_to_global_pose(bone_two_children[i], stack->skeleton->get_bone_rest(bone_two_children[i])).origin); + stack->skeleton->get_bone_global_pose(bone_two_children[i]).origin); } joint_two_length = joint_two_length / bone_two_children.size(); } else { diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index 72c1c330ef..db9fe62b4d 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -247,7 +247,7 @@ void SkeletonModificationStack2D::_bind_methods() { ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack2D::delete_modification); ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack2D::set_modification); - ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack2D::set_modification_count); + ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack2D::set_modification_count); ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count); ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup); diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp index 3fce0e5dbd..a724b732b9 100644 --- a/scene/resources/skeleton_modification_stack_3d.cpp +++ b/scene/resources/skeleton_modification_stack_3d.cpp @@ -200,7 +200,7 @@ void SkeletonModificationStack3D::_bind_methods() { ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification); ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification); - ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack3D::set_modification_count); + ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack3D::set_modification_count); ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count); ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup); diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 39082b6f7a..de94c92cac 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; } @@ -179,14 +175,14 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("get_sun_curve"), &ProceduralSkyMaterial::get_sun_curve); ADD_GROUP("Sky", "sky_"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color"), "set_sky_top_color", "get_sky_top_color"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color"), "set_sky_horizon_color", "get_sky_horizon_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy"); ADD_GROUP("Ground", "ground_"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color"), "set_ground_bottom_color", "get_ground_bottom_color"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color"), "set_ground_horizon_color", "get_ground_horizon_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_horizon_color", "get_ground_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_curve", PROPERTY_HINT_EXP_EASING), "set_ground_curve", "get_ground_curve"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy"); @@ -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; } @@ -543,16 +531,16 @@ void PhysicalSkyMaterial::_bind_methods() { ADD_GROUP("Rayleigh", "rayleigh_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rayleigh_coefficient", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_rayleigh_coefficient", "get_rayleigh_coefficient"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "rayleigh_color"), "set_rayleigh_color", "get_rayleigh_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "rayleigh_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_rayleigh_color", "get_rayleigh_color"); ADD_GROUP("Mie", "mie_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mie_coefficient", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_mie_coefficient", "get_mie_coefficient"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mie_eccentricity", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_mie_eccentricity", "get_mie_eccentricity"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "mie_color"), "set_mie_color", "get_mie_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "mie_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_mie_color", "get_mie_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbidity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_turbidity", "get_turbidity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_disk_scale", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_disk_scale", "get_sun_disk_scale"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color"), "set_ground_color", "get_ground_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_color", "get_ground_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_exposure", "get_exposure"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dither_strength", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_dither_strength", "get_dither_strength"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_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/surface_tool.cpp b/scene/resources/surface_tool.cpp index d5e370568d..a8cd872408 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -1176,6 +1176,7 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun ERR_FAIL_COND_V(simplify_func == nullptr, lod); ERR_FAIL_COND_V(vertex_array.size() == 0, lod); ERR_FAIL_COND_V(index_array.size() == 0, lod); + ERR_FAIL_COND_V(index_array.size() % 3 != 0, lod); lod.resize(index_array.size()); LocalVector<float> vertices; //uses floats diff --git a/scene/resources/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 3dc32632cc..1b3a7a5f2a 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1248,6 +1248,14 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { return atlas->is_pixel_opaque(x, y); } +Ref<Image> AtlasTexture::get_image() const { + if (!atlas.is_valid() || !atlas->get_image().is_valid()) { + return Ref<Image>(); + } + + return atlas->get_image()->get_rect(region); +} + AtlasTexture::AtlasTexture() {} ///////////////////////////////////////// diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 93f4e2de5a..862b9a47a6 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -250,6 +250,8 @@ public: bool is_pixel_opaque(int p_x, int p_y) const override; + virtual Ref<Image> get_image() const override; + AtlasTexture(); }; diff --git a/scene/resources/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 67cbd0e094..b988b2ab3e 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -66,12 +66,13 @@ 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; tile_meshes_dirty = true; + notify_property_list_changed(); emit_changed(); } TileSet::TileShape TileSet::get_tile_shape() const { @@ -89,8 +90,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; @@ -170,6 +171,8 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) { source_ids.append(p_new_source_id); source_ids.sort(); + _compute_next_source_id(); + emit_changed(); } @@ -386,8 +389,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 +722,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 +861,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 +872,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 +883,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 +938,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 +949,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 +962,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); } @@ -1019,20 +1022,25 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() { void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { if (tile_meshes_dirty) { - Vector<Vector2> uvs = get_tile_shape_polygon(); + Vector<Vector2> shape = get_tile_shape_polygon(); + Vector<Vector2> uvs; + uvs.resize(shape.size()); + for (int i = 0; i < shape.size(); i++) { + uvs.write[i] = shape[i] + Vector2(0.5, 0.5); + } Vector<Color> colors; - colors.resize(uvs.size()); + colors.resize(shape.size()); colors.fill(Color(1.0, 1.0, 1.0, 1.0)); // Filled mesh. tile_filled_mesh->clear_surfaces(); Array a; a.resize(Mesh::ARRAY_MAX); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_TEX_UV] = uvs; a[Mesh::ARRAY_COLOR] = colors; - a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(shape); tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); // Lines mesh. @@ -1040,9 +1048,9 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform a.clear(); a.resize(Mesh::ARRAY_MAX); // Add the first point again when drawing lines. - uvs.push_back(uvs[0]); + shape.push_back(shape[0]); colors.push_back(colors[0]); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_COLOR] = colors; tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); @@ -1906,8 +1914,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); @@ -1945,11 +1953,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)); @@ -2037,11 +2045,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)); @@ -2123,8 +2131,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 *>(); } @@ -2574,25 +2582,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; @@ -2668,8 +2676,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. @@ -2680,6 +2688,14 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void TileSet::_validate_property(PropertyInfo &property) const { + if (property.name == "tile_layout" && tile_shape == TILE_SHAPE_SQUARE) { + property.usage ^= PROPERTY_USAGE_READ_ONLY; + } else if (property.name == "tile_offset_axis" && tile_shape == TILE_SHAPE_SQUARE) { + property.usage ^= PROPERTY_USAGE_READ_ONLY; + } +} + void TileSet::_bind_methods() { // Sources management. ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id); @@ -2845,8 +2861,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()) { @@ -2878,18 +2894,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(); } } } @@ -3040,9 +3056,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(); } } } @@ -3050,6 +3066,7 @@ void TileSetAtlasSource::reset_state() { void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) { texture = p_texture; + _clear_tiles_outside_texture(); emit_changed(); } @@ -3065,6 +3082,7 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) { margins = p_margins; } + _clear_tiles_outside_texture(); emit_changed(); } Vector2i TileSetAtlasSource::get_margins() const { @@ -3079,6 +3097,7 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) { separation = p_separation; } + _clear_tiles_outside_texture(); emit_changed(); } Vector2i TileSetAtlasSource::get_separation() const { @@ -3093,6 +3112,7 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) { texture_region_size = p_tile_size; } + _clear_tiles_outside_texture(); emit_changed(); } Vector2i TileSetAtlasSource::get_texture_region_size() const { @@ -3119,7 +3139,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(":"); @@ -3255,40 +3275,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); @@ -3297,8 +3317,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; @@ -3306,27 +3326,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); } } @@ -3338,7 +3358,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1); - ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover."); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile. The tile is outside the texture or tiles are already present in the space the tile would cover."); // Initialize the tile data. TileAlternativesData tad; @@ -3368,8 +3388,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 @@ -3520,6 +3540,13 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) const { } bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile) const { + if (p_atlas_coords.x < 0 || p_atlas_coords.y < 0) { + return false; + } + if (p_size.x <= 0 || p_size.y <= 0) { + return false; + } + Size2i atlas_grid_size = get_atlas_grid_size(); for (int frame = 0; frame < p_frames_count; frame++) { Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0)); for (int x = 0; x < p_size.x; x++) { @@ -3528,12 +3555,46 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s if (_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] != p_ignored_tile) { return false; } + if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) { + return false; + } } } } return true; } +PackedVector2Array TileSetAtlasSource::get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size) { + ERR_FAIL_COND_V(p_margins.x < 0 || p_margins.y < 0, PackedVector2Array()); + ERR_FAIL_COND_V(p_separation.x < 0 || p_separation.y < 0, PackedVector2Array()); + ERR_FAIL_COND_V(p_texture_region_size.x <= 0 || p_texture_region_size.y <= 0, PackedVector2Array()); + + // Compute the new atlas grid size. + Size2 new_grid_size; + if (p_texture.is_valid()) { + Size2i valid_area = p_texture->get_size() - p_margins; + + // Compute the number of valid tiles in the tiles atlas + if (valid_area.x >= p_texture_region_size.x && valid_area.y >= p_texture_region_size.y) { + valid_area -= p_texture_region_size; + new_grid_size = Size2i(1, 1) + valid_area / (p_texture_region_size + p_separation); + } + } + + Vector<Vector2> output; + for (KeyValue<Vector2i, TileAlternativesData> &E : tiles) { + for (unsigned int frame = 0; frame < E.value.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = E.key + (E.value.size_in_atlas + E.value.animation_separation) * ((E.value.animation_columns > 0) ? Vector2i(frame % E.value.animation_columns, frame / E.value.animation_columns) : Vector2i(frame, 0)); + frame_coords += E.value.size_in_atlas; + if (frame_coords.x > new_grid_size.x || frame_coords.y > new_grid_size.y) { + output.push_back(E.key); + break; + } + } + } + return output; +} + Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i()); @@ -3558,8 +3619,7 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_ margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset(); if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { - effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x); - effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y); + effective_texture_offset = effective_texture_offset.clamp(-margin, margin); } return effective_texture_offset; @@ -3599,34 +3659,6 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ emit_signal(SNAME("changed")); } -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) { - return true; - } - } - - return false; -} - -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 (int i = 0; i < to_remove.size(); i++) { - remove_tile(to_remove[i]); - } -} - int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override)); @@ -3727,7 +3759,7 @@ void TileSetAtlasSource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS)); - + ClassDB::bind_method(D_METHOD("get_tiles_to_be_removed_on_change", "texture", "margins", "separation", "texture_region_size"), &TileSetAtlasSource::get_tiles_to_be_removed_on_change); ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns); @@ -3752,16 +3784,14 @@ void TileSetAtlasSource::_bind_methods() { // Helpers. ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); - ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); - ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0)); } 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); } } } @@ -3827,6 +3857,20 @@ void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) { } } +void TileSetAtlasSource::_clear_tiles_outside_texture() { + LocalVector<Vector2i> to_remove; + + for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { + if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) { + to_remove.push_back(E.key); + } + } + + for (unsigned int i = 0; i < to_remove.size(); i++) { + remove_tile(to_remove[i]); + } +} + /////////////////////////////// TileSetScenesCollectionSource ////////////////////////////////////// void TileSetScenesCollectionSource::_compute_next_alternative_id() { @@ -4300,9 +4344,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) { @@ -4313,6 +4374,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()); @@ -4514,11 +4580,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; @@ -4526,8 +4588,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); @@ -4629,9 +4702,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); @@ -4702,6 +4784,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++) { @@ -4791,8 +4875,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..351bdff89d 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -180,6 +180,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + virtual void _validate_property(PropertyInfo &property) const override; private: // --- TileSet data --- @@ -478,8 +479,10 @@ private: void _compute_next_alternative_id(const Vector2i p_atlas_coords); - void _create_coords_mapping_cache(Vector2i p_atlas_coords); void _clear_coords_mapping_cache(Vector2i p_atlas_coords); + void _create_coords_mapping_cache(Vector2i p_atlas_coords); + + void _clear_tiles_outside_texture(); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -533,7 +536,7 @@ public: virtual Vector2i get_tile_id(int p_index) const override; bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const; - + PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size); Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const; // Animation. @@ -564,8 +567,6 @@ public: // Helpers. Vector2i get_atlas_grid_size() const; - bool has_tiles_outside_texture(); - void clear_tiles_outside_texture(); Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const; Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const; @@ -645,6 +646,8 @@ private: float one_way_margin = 1.0; }; + Vector2 linear_velocity; + float angular_velocity = 0.0; Vector<PolygonShapeTileData> polygons; }; Vector<PhysicsLayerTileData> physics; @@ -718,8 +721,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..0dc83b4bb8 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -113,6 +113,10 @@ bool VisualShaderNode::is_output_port_expandable(int p_port) const { return false; } +bool VisualShaderNode::has_output_port_preview(int p_port) const { + return true; +} + void VisualShaderNode::_set_output_ports_expanded(const Array &p_values) { for (int i = 0; i < p_values.size(); i++) { expanded_output_ports[p_values[i]] = true; @@ -197,9 +201,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 +464,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 +488,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 +565,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 +579,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 +823,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 +1045,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 +1275,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 +1284,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 +1720,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; } } @@ -2307,7 +2311,21 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { }; const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { + // Spatial, Vertex + + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Spatial, Fragment + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" }, @@ -2315,44 +2333,59 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" }, - - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Spatial, Light - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Vertex + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Fragment + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Light + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Sky + + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Particles + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; @@ -2413,13 +2446,10 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T case PORT_TYPE_VECTOR: { code = " " + p_output_vars[0] + " = vec3(0.0);\n"; } break; - case PORT_TYPE_TRANSFORM: { - code = " " + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; - } break; case PORT_TYPE_BOOLEAN: { code = " " + p_output_vars[0] + " = false;\n"; } break; - default: //default (none found) is scalar + default: break; } } diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index b3efac02aa..f910f28536 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -254,6 +254,8 @@ public: void set_input_port_connected(int p_port, bool p_connected); virtual bool is_generate_input_var(int p_port) const; + virtual bool has_output_port_preview(int p_port) const; + virtual bool is_output_port_expandable(int p_port) const; void _set_output_ports_expanded(const Array &p_data); Array _get_output_ports_expanded() const; diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index 5fe801e037..18b933e5cf 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -47,6 +47,10 @@ String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const { return String(); } +bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const { + return false; +} + VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() { } @@ -265,6 +269,10 @@ Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_pro return props; } +bool VisualShaderNodeParticleMultiplyByAxisAngle::has_output_port_preview(int p_port) const { + return false; +} + VisualShaderNodeParticleMultiplyByAxisAngle::VisualShaderNodeParticleMultiplyByAxisAngle() { set_input_port_default_value(1, Vector3(1, 0, 0)); set_input_port_default_value(2, 0.0); @@ -313,6 +321,10 @@ String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) co return String(); } +bool VisualShaderNodeParticleConeVelocity::has_output_port_preview(int p_port) const { + return false; +} + String VisualShaderNodeParticleConeVelocity::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; code += " __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n"; @@ -421,6 +433,10 @@ VisualShaderNodeParticleRandomness::OpType VisualShaderNodeParticleRandomness::g return op_type; } +bool VisualShaderNodeParticleRandomness::has_output_port_preview(int p_port) const { + return false; +} + VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() { set_input_port_default_value(0, 0.0); set_input_port_default_value(1, 1.0); @@ -521,6 +537,10 @@ VisualShaderNodeParticleAccelerator::Mode VisualShaderNodeParticleAccelerator::g return mode; } +bool VisualShaderNodeParticleAccelerator::has_output_port_preview(int p_port) const { + return false; +} + VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() { set_input_port_default_value(0, Vector3(1, 1, 1)); set_input_port_default_value(1, 0.0); diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h index f5435c3511..b8bc7992cc 100644 --- a/scene/resources/visual_shader_particle_nodes.h +++ b/scene/resources/visual_shader_particle_nodes.h @@ -42,6 +42,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; VisualShaderNodeParticleEmitter(); }; @@ -112,6 +113,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; @@ -135,6 +137,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; @@ -168,6 +171,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; @@ -209,6 +213,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index b283749ffa..186764e69e 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -64,6 +64,8 @@ SceneStringNames::SceneStringNames() { pose_updated = StaticCString::create("pose_updated"); bone_pose_changed = StaticCString::create("bone_pose_changed"); + bone_enabled_changed = StaticCString::create("bone_enabled_changed"); + show_rest_only_changed = StaticCString::create("show_rest_only_changed"); mouse_entered = StaticCString::create("mouse_entered"); mouse_exited = StaticCString::create("mouse_exited"); @@ -73,6 +75,7 @@ SceneStringNames::SceneStringNames() { focus_entered = StaticCString::create("focus_entered"); focus_exited = StaticCString::create("focus_exited"); + pre_sort_children = StaticCString::create("pre_sort_children"); sort_children = StaticCString::create("sort_children"); body_shape_entered = StaticCString::create("body_shape_entered"); @@ -134,6 +137,7 @@ SceneStringNames::SceneStringNames() { _spatial_editor_group = StaticCString::create("_spatial_editor_group"); _request_gizmo = StaticCString::create("_request_gizmo"); + _set_subgizmo_selection = StaticCString::create("_set_subgizmo_selection"); _clear_subgizmo_selection = StaticCString::create("_clear_subgizmo_selection"); offset = StaticCString::create("offset"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 2923351eab..67007c85e0 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -89,6 +89,7 @@ public: StringName focus_entered; StringName focus_exited; + StringName pre_sort_children; StringName sort_children; StringName finished; @@ -99,6 +100,8 @@ public: StringName pose_updated; StringName bone_pose_changed; + StringName bone_enabled_changed; + StringName show_rest_only_changed; StringName body_shape_entered; StringName body_entered; @@ -154,6 +157,7 @@ public: StringName _spatial_editor_group; StringName _request_gizmo; + StringName _set_subgizmo_selection; StringName _clear_subgizmo_selection; StringName offset; |