diff options
Diffstat (limited to 'scene')
112 files changed, 1676 insertions, 1079 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 9ac3083718..2ded209180 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -284,6 +284,9 @@ bool AudioStreamPlayer2D::is_playing() const { return true; } } + if (setplay.get() >= 0) { + return true; // play() has been called this frame, but no playback exists just yet. + } return false; } diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 616d7fdb60..5bc9083488 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -82,7 +82,7 @@ private: float attenuation = 1.0; float panning_strength = 1.0f; - float cached_global_panning_strength = 1.0f; + float cached_global_panning_strength = 0.5f; protected: void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 9786b01058..c8f5d7f5a6 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -141,16 +141,16 @@ void GPUParticles2D::set_process_material(const Ref<Material> &p_material) { void GPUParticles2D::set_trail_enabled(bool p_enabled) { trail_enabled = p_enabled; - RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime); queue_redraw(); RS::get_singleton()->particles_set_transform_align(particles, p_enabled ? RS::PARTICLES_TRANSFORM_ALIGN_Y_TO_VELOCITY : RS::PARTICLES_TRANSFORM_ALIGN_DISABLED); } -void GPUParticles2D::set_trail_length(double p_seconds) { +void GPUParticles2D::set_trail_lifetime(double p_seconds) { ERR_FAIL_COND(p_seconds < 0.001); - trail_length = p_seconds; - RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); + trail_lifetime = p_seconds; + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime); queue_redraw(); } @@ -181,8 +181,8 @@ bool GPUParticles2D::is_trail_enabled() const { return trail_enabled; } -double GPUParticles2D::get_trail_length() const { - return trail_length; +double GPUParticles2D::get_trail_lifetime() const { + return trail_lifetime; } void GPUParticles2D::_update_collision_size() { @@ -299,10 +299,6 @@ bool GPUParticles2D::get_interpolate() const { PackedStringArray GPUParticles2D::get_configuration_warnings() const { PackedStringArray warnings = Node2D::get_configuration_warnings(); - if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.")); - } - if (process_material.is_null()) { warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted.")); } else { @@ -614,10 +610,10 @@ void GPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles2D::emit_particle); ClassDB::bind_method(D_METHOD("set_trail_enabled", "enabled"), &GPUParticles2D::set_trail_enabled); - ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles2D::set_trail_length); + ClassDB::bind_method(D_METHOD("set_trail_lifetime", "secs"), &GPUParticles2D::set_trail_lifetime); ClassDB::bind_method(D_METHOD("is_trail_enabled"), &GPUParticles2D::is_trail_enabled); - ClassDB::bind_method(D_METHOD("get_trail_length"), &GPUParticles2D::get_trail_length); + ClassDB::bind_method(D_METHOD("get_trail_lifetime"), &GPUParticles2D::get_trail_lifetime); ClassDB::bind_method(D_METHOD("set_trail_sections", "sections"), &GPUParticles2D::set_trail_sections); ClassDB::bind_method(D_METHOD("get_trail_sections"), &GPUParticles2D::get_trail_sections); @@ -647,7 +643,7 @@ void GPUParticles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,Reverse Lifetime"), "set_draw_order", "get_draw_order"); ADD_GROUP("Trails", "trail_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_trail_enabled", "is_trail_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), "set_trail_length", "get_trail_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_lifetime", PROPERTY_HINT_RANGE, "0.01,10,0.01,or_greater,suffix:s"), "set_trail_lifetime", "get_trail_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_trail_sections", "get_trail_sections"); ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_section_subdivisions", PROPERTY_HINT_RANGE, "1,1024,1"), "set_trail_section_subdivisions", "get_trail_section_subdivisions"); ADD_GROUP("Process Material", "process_"); diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index d613b4ef51..94555e0bb0 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -64,7 +64,7 @@ private: #endif Ref<Material> process_material; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_LIFETIME; Ref<Texture2D> texture; @@ -74,7 +74,7 @@ private: real_t collision_base_size = 1.0; bool trail_enabled = false; - double trail_length = 0.3; + double trail_lifetime = 0.3; int trail_sections = 8; int trail_section_subdivisions = 4; @@ -104,7 +104,7 @@ public: void set_speed_scale(double p_scale); void set_collision_base_size(real_t p_ratio); void set_trail_enabled(bool p_enabled); - void set_trail_length(double p_seconds); + void set_trail_lifetime(double p_seconds); void set_trail_sections(int p_sections); void set_trail_section_subdivisions(int p_subdivisions); @@ -126,7 +126,7 @@ public: real_t get_collision_base_size() const; bool is_trail_enabled() const; - double get_trail_length() const; + double get_trail_lifetime() const; int get_trail_sections() const; int get_trail_section_subdivisions() const; diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 6000508f36..8de4c281f4 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -133,7 +133,13 @@ void Joint2D::set_node_a(const NodePath &p_node_a) { } a = p_node_a; - _update_joint(); + if (Engine::get_singleton()->is_editor_hint()) { + // When in editor, the setter may be called as a result of node rename. + // It happens before the node actually changes its name, which triggers false warning. + callable_mp(this, &Joint2D::_update_joint).call_deferred(); + } else { + _update_joint(); + } } NodePath Joint2D::get_node_a() const { @@ -150,7 +156,11 @@ void Joint2D::set_node_b(const NodePath &p_node_b) { } b = p_node_b; - _update_joint(); + if (Engine::get_singleton()->is_editor_hint()) { + callable_mp(this, &Joint2D::_update_joint).call_deferred(); + } else { + _update_joint(); + } } NodePath Joint2D::get_node_b() const { diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index 2a5092216d..9d36f80dd6 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -37,11 +37,11 @@ class NavigationLink2D : public Node2D { GDCLASS(NavigationLink2D, Node2D); bool enabled = true; - RID link = RID(); + RID link; bool bidirectional = true; uint32_t navigation_layers = 1; - Vector2 end_location = Vector2(); - Vector2 start_location = Vector2(); + Vector2 end_location; + Vector2 start_location; real_t enter_cost = 0.0; real_t travel_cost = 1.0; diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 2518069b78..84bfc48a43 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -30,6 +30,8 @@ #include "node_2d.h" +#include "scene/main/viewport.h" + #ifdef TOOLS_ENABLED Dictionary Node2D::_edit_get_state() const { Dictionary state; @@ -389,6 +391,16 @@ bool Node2D::is_y_sort_enabled() const { return y_sort_enabled; } +void Node2D::_notification(int p_notification) { + switch (p_notification) { + case NOTIFICATION_MOVED_IN_PARENT: { + if (get_viewport()) { + get_viewport()->gui_set_root_order_dirty(); + } + } break; + } +} + void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_position", "position"), &Node2D::set_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Node2D::set_rotation); diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 0d8a31e6bb..04bbdf639d 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -53,6 +53,7 @@ class Node2D : public CanvasItem { void _update_xform_values(); protected: + void _notification(int p_notification); static void _bind_methods(); public: diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 0f3e6c7529..7f5b5d1ea4 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", "distance", "test_only", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08), DEFVAL(false)); 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,8 +54,8 @@ PhysicsBody2D::~PhysicsBody2D() { } } -Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_distance, bool p_test_only, real_t p_margin, bool p_recovery_as_collision) { - PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); +Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin, bool p_recovery_as_collision) { + PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_motion, p_margin); parameters.recovery_as_collision = p_recovery_as_collision; PhysicsServer2D::MotionResult result; @@ -128,7 +128,7 @@ bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_ return colliding; } -bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision, real_t p_margin, bool p_recovery_as_collision) { +bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision, real_t p_margin, bool p_recovery_as_collision) { ERR_FAIL_COND_V(!is_inside_tree(), false); PhysicsServer2D::MotionResult *r = nullptr; @@ -140,7 +140,7 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distan r = &temp_result; } - PhysicsServer2D::MotionParameters parameters(p_from, p_distance, p_margin); + PhysicsServer2D::MotionParameters parameters(p_from, p_motion, p_margin); parameters.recovery_as_collision = p_recovery_as_collision; return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); @@ -162,14 +162,14 @@ TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() { void PhysicsBody2D::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(p_node); - ERR_FAIL_COND_MSG(!physics_body, "Collision exception only works between two objects of PhysicsBody2D type."); + ERR_FAIL_COND_MSG(!physics_body, "Collision exception only works between two nodes that inherit from PhysicsBody2D."); PhysicsServer2D::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid()); } void PhysicsBody2D::remove_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(p_node); - ERR_FAIL_COND_MSG(!physics_body, "Collision exception only works between two objects of PhysicsBody2D type."); + ERR_FAIL_COND_MSG(!physics_body, "Collision exception only works between two nodes that inherit from PhysicsBody2D."); PhysicsServer2D::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid()); } @@ -1761,7 +1761,7 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater,suffix:px"), "set_floor_snap_length", "get_floor_snap_length"); - ADD_GROUP("Moving Platform", "platform"); + ADD_GROUP("Moving Platform", "platform_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_on_leave", PROPERTY_HINT_ENUM, "Add Velocity,Add Upward Velocity,Do Nothing", PROPERTY_USAGE_DEFAULT), "set_platform_on_leave", "get_platform_on_leave"); ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_floor_layers", "get_platform_floor_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_wall_layers", "get_platform_wall_layers"); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 932ec1de16..504e1dc333 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_distance, bool p_test_only = false, real_t p_margin = 0.08, bool p_recovery_as_collision = false); + Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_test_only = false, real_t p_margin = 0.08, bool p_recovery_as_collision = false); public: 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_distance, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08, bool p_recovery_as_collision = false); + 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 p_recovery_as_collision = false); TypedArray<PhysicsBody2D> get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 5e77902977..e41664b006 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -114,7 +114,7 @@ void Polygon2D::_notification(int p_what) { ObjectID new_skeleton_id; - if (skeleton_node) { + if (skeleton_node && !invert && bone_weights.size()) { RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton()); new_skeleton_id = skeleton_node->get_instance_id(); } else { @@ -663,5 +663,7 @@ Polygon2D::Polygon2D() { } Polygon2D::~Polygon2D() { + // This will free the internally-allocated mesh instance, if allocated. + RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID()); RS::get_singleton()->free(mesh); } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 02d8e20ce8..62787d4488 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -213,7 +213,7 @@ void Bone2D::_notification(int p_what) { } // Undo scaling - Transform2D editor_gizmo_trans = Transform2D(); + Transform2D editor_gizmo_trans; editor_gizmo_trans.set_scale(Vector2(1, 1) / get_global_scale()); RenderingServer::get_singleton()->canvas_item_set_transform(editor_gizmo_rid, editor_gizmo_trans); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 40e8818262..0159e9f313 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1859,11 +1859,13 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ while (q_list_element) { TileMapQuadrant &q = *q_list_element->self(); - // Clear the scenes. - for (const KeyValue<Vector2i, String> &E : q.scenes) { - Node *node = get_node_or_null(E.value); - if (node) { - node->queue_free(); + // Clear the scenes if instance cache was cleared. + if (instantiated_scenes.is_empty()) { + for (const KeyValue<Vector2i, String> &E : q.scenes) { + Node *node = get_node_or_null(E.value); + if (node) { + node->queue_free(); + } } } @@ -1871,6 +1873,15 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ // Recreate the scenes. for (const Vector2i &E_cell : q.cells) { + Vector3i cell_coords = Vector3i(q.layer, E_cell.x, E_cell.y); + if (instantiated_scenes.has(cell_coords)) { + // Skip scene if the instance was cached (to avoid recreating scenes unnecessarily). + continue; + } + if (!Engine::get_singleton()->is_editor_hint()) { + instantiated_scenes.insert(cell_coords); + } + const TileMapCell &c = get_cell(q.layer, E_cell, true); TileSetSource *source; @@ -1907,15 +1918,16 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ } void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { - // Clear the scenes. - for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) { - Node *node = get_node_or_null(E.value); - if (node) { - node->queue_free(); + // Clear the scenes if instance cache was cleared. + if (instantiated_scenes.is_empty()) { + for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) { + Node *node = get_node_or_null(E.value); + if (node) { + node->queue_free(); + } } + p_quadrant->scenes.clear(); } - - p_quadrant->scenes.clear(); } void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { @@ -4037,6 +4049,7 @@ void TileMap::_bind_methods() { void TileMap::_tile_set_changed() { emit_signal(SNAME("changed")); _tile_set_changed_deferred_update_needed = true; + instantiated_scenes.clear(); call_deferred(SNAME("_tile_set_changed_deferred_update")); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 7f3e241871..68a5d3c80b 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -115,7 +115,7 @@ public: class TerrainConstraint { private: const TileMap *tile_map; - Vector2i base_cell_coords = Vector2i(); + Vector2i base_cell_coords; int bit = -1; int terrain = -1; @@ -236,6 +236,8 @@ private: void _clear_layer_internals(int p_layer); void _clear_internals(); + HashSet<Vector3i> instantiated_scenes; + // Rect caching. void _recompute_rect_cache(); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index b9d95672fe..75e840c24b 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -606,6 +606,9 @@ bool AudioStreamPlayer3D::is_playing() const { return true; } } + if (setplay.get() >= 0) { + return true; // play() has been called this frame, but no playback exists just yet. + } return false; } diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 913cc9fc00..806b250ba7 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -117,7 +117,7 @@ private: float _get_attenuation_db(float p_distance) const; float panning_strength = 1.0f; - float cached_global_panning_strength = 1.0f; + float cached_global_panning_strength = 0.5f; protected: void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index d8524a7392..9cf10dbef1 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -375,7 +375,7 @@ void BoneAttachment3D::on_bone_pose_update(int p_bone_index) { } } #ifdef TOOLS_ENABLED -void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Ref<BoneMap> p_bone_map) { +void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map) { const Skeleton3D *parent = nullptr; if (use_external_skeleton) { if (external_skeleton_node_cache.is_valid()) { @@ -385,7 +385,7 @@ void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skelet parent = Object::cast_to<Skeleton3D>(get_parent()); } if (parent && parent == p_skeleton) { - StringName bn = p_bone_map->find_profile_bone_name(bone_name); + StringName bn = p_rename_map[bone_name]; if (bn) { set_bone_name(bn); } diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 2db6ba6268..81338b30e9 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -72,7 +72,7 @@ protected: static void _bind_methods(); #ifdef TOOLS_ENABLED - virtual void _notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Ref<BoneMap> p_bone_map); + virtual void _notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map); #endif // TOOLS_ENABLED public: diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index a98be62dfa..66546092f2 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -30,6 +30,7 @@ #include "collision_object_3d.h" +#include "scene/resources/shape_3d.h" #include "scene/scene_string_names.h" void CollisionObject3D::_notification(int p_what) { @@ -330,7 +331,7 @@ bool CollisionObject3D::_are_collision_shapes_visible() { void CollisionObject3D::_update_shape_data(uint32_t p_owner) { if (_are_collision_shapes_visible()) { if (debug_shapes_to_update.is_empty()) { - callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferredp({}, 0); + callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferred(); } debug_shapes_to_update.insert(p_owner); } diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 7de685b469..5ac8535bb6 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -867,7 +867,7 @@ void CPUParticles3D::_particles_process(double p_delta) { real_t ring_random_angle = Math::randf() * Math_TAU; real_t ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius; Vector3 axis = emission_ring_axis.normalized(); - Vector3 ortho_axis = Vector3(); + Vector3 ortho_axis; if (axis == Vector3(1.0, 0.0, 0.0)) { ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis); } else { diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index dbbf196f7a..17dfe2610e 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -176,22 +176,22 @@ void GPUParticles3D::set_draw_order(DrawOrder p_order) { void GPUParticles3D::set_trail_enabled(bool p_enabled) { trail_enabled = p_enabled; - RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime); update_configuration_warnings(); } -void GPUParticles3D::set_trail_length(double p_seconds) { +void GPUParticles3D::set_trail_lifetime(double p_seconds) { ERR_FAIL_COND(p_seconds < 0.001); - trail_length = p_seconds; - RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); + trail_lifetime = p_seconds; + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime); } bool GPUParticles3D::is_trail_enabled() const { return trail_enabled; } -double GPUParticles3D::get_trail_length() const { - return trail_length; +double GPUParticles3D::get_trail_lifetime() const { + return trail_lifetime; } GPUParticles3D::DrawOrder GPUParticles3D::get_draw_order() const { @@ -272,10 +272,6 @@ bool GPUParticles3D::get_interpolate() const { PackedStringArray GPUParticles3D::get_configuration_warnings() const { PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings(); - if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); - } - bool meshes_found = false; bool anim_material_found = false; @@ -552,10 +548,10 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles3D::emit_particle); ClassDB::bind_method(D_METHOD("set_trail_enabled", "enabled"), &GPUParticles3D::set_trail_enabled); - ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles3D::set_trail_length); + ClassDB::bind_method(D_METHOD("set_trail_lifetime", "secs"), &GPUParticles3D::set_trail_lifetime); ClassDB::bind_method(D_METHOD("is_trail_enabled"), &GPUParticles3D::is_trail_enabled); - ClassDB::bind_method(D_METHOD("get_trail_length"), &GPUParticles3D::get_trail_length); + ClassDB::bind_method(D_METHOD("get_trail_lifetime"), &GPUParticles3D::get_trail_lifetime); ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align); ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align); @@ -583,7 +579,7 @@ void GPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,Z-Billboard,Y to Velocity,Z-Billboard + Y to Velocity"), "set_transform_align", "get_transform_align"); ADD_GROUP("Trails", "trail_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_trail_enabled", "is_trail_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), "set_trail_length", "get_trail_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_lifetime", PROPERTY_HINT_RANGE, "0.01,10,0.01,or_greater,suffix:s"), "set_trail_lifetime", "get_trail_lifetime"); ADD_GROUP("Process Material", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticleProcessMaterial"), "set_process_material", "get_process_material"); ADD_GROUP("Draw Passes", "draw_"); @@ -627,7 +623,7 @@ GPUParticles3D::GPUParticles3D() { set_pre_process_time(0); set_explosiveness_ratio(0); set_randomness_ratio(0); - set_trail_length(0.3); + set_trail_lifetime(0.3); set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8))); set_use_local_coordinates(false); set_draw_passes(1); diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index ef92218e4d..d1768436e5 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -76,13 +76,13 @@ private: real_t collision_base_size = 0.01; bool trail_enabled = false; - double trail_length = 0.3; + double trail_lifetime = 0.3; TransformAlign transform_align = TRANSFORM_ALIGN_DISABLED; Ref<Material> process_material; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_INDEX; Vector<Ref<Mesh>> draw_passes; Ref<Skin> skin; @@ -112,7 +112,7 @@ public: void set_speed_scale(double p_scale); void set_collision_base_size(real_t p_ratio); void set_trail_enabled(bool p_enabled); - void set_trail_length(double p_seconds); + void set_trail_lifetime(double p_seconds); bool is_emitting() const; int get_amount() const; @@ -127,7 +127,7 @@ public: double get_speed_scale() const; real_t get_collision_base_size() const; bool is_trail_enabled() const; - double get_trail_length() const; + double get_trail_lifetime() const; void set_fixed_fps(int p_count); int get_fixed_fps() const; diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 2c5df48b75..476820b1c4 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -347,7 +347,9 @@ void GPUParticlesCollisionSDF3D::_compute_sdf(ComputeSDFParams *params) { WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params, params->size.z); while (!WorkerThreadPool::get_singleton()->is_group_task_completed(group_task)) { OS::get_singleton()->delay_usec(10000); - bake_step_function(WorkerThreadPool::get_singleton()->get_group_processed_element_count(group_task) * 100 / params->size.z, "Baking SDF"); + if (bake_step_function) { + bake_step_function(WorkerThreadPool::get_singleton()->get_group_processed_element_count(group_task) * 100 / params->size.z, "Baking SDF"); + } } WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); } diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index d4f60503c2..04d164ba88 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -33,6 +33,8 @@ #include "collision_shape_3d.h" #include "core/core_string_names.h" #include "physics_body_3d.h" +#include "scene/resources/concave_polygon_shape_3d.h" +#include "scene/resources/convex_polygon_shape_3d.h" bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else. @@ -224,7 +226,7 @@ Node *MeshInstance3D::create_trimesh_collision_node() { return nullptr; } - Ref<Shape3D> shape = mesh->create_trimesh_shape(); + Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape(); if (shape.is_null()) { return nullptr; } @@ -254,7 +256,7 @@ Node *MeshInstance3D::create_convex_collision_node(bool p_clean, bool p_simplify return nullptr; } - Ref<Shape3D> shape = mesh->create_convex_shape(p_clean, p_simplify); + Ref<ConvexPolygonShape3D> shape = mesh->create_convex_shape(p_clean, p_simplify); if (shape.is_null()) { return nullptr; } diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index 1fc03546fa..46bc6318b8 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -37,11 +37,11 @@ class NavigationLink3D : public Node3D { GDCLASS(NavigationLink3D, Node3D); bool enabled = true; - RID link = RID(); + RID link; bool bidirectional = true; uint32_t navigation_layers = 1; - Vector3 end_location = Vector3(); - Vector3 start_location = Vector3(); + Vector3 end_location; + Vector3 start_location; real_t enter_cost = 0.0; real_t travel_cost = 1.0; diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index ab4cba86fb..02ab297d8e 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -182,125 +182,31 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { if (bl == 0.0) { return; } - real_t bi = c->get_bake_interval(); - real_t o_next = progress + bi; - real_t o_prev = progress - bi; - - if (loop) { - o_next = Math::fposmod(o_next, bl); - o_prev = Math::fposmod(o_prev, bl); - } else if (rotation_mode == ROTATION_ORIENTED) { - if (o_next >= bl) { - o_next = bl; - } - if (o_prev <= 0) { - o_prev = 0; - } - } - - Vector3 pos = c->sample_baked(progress, cubic); - Transform3D t = get_transform(); - // Vector3 pos_offset = Vector3(h_offset, v_offset, 0); not used in all cases - // will be replaced by "Vector3(h_offset, v_offset, 0)" where it was formerly used - - if (rotation_mode == ROTATION_ORIENTED) { - Vector3 forward = c->sample_baked(o_next, cubic) - pos; - - // Try with the previous position - if (forward.length_squared() < CMP_EPSILON2) { - forward = pos - c->sample_baked(o_prev, cubic); - } - - if (forward.length_squared() < CMP_EPSILON2) { - forward = Vector3(0, 0, 1); - } else { - forward.normalize(); - } - - Vector3 up = c->sample_baked_up_vector(progress, true); - if (o_next < progress) { - Vector3 up1 = c->sample_baked_up_vector(o_next, true); - Vector3 axis = up.cross(up1); - - if (axis.length_squared() < CMP_EPSILON2) { - axis = forward; - } else { - axis.normalize(); - } - - up.rotate(axis, up.angle_to(up1) * 0.5f); - } - - Vector3 scale = t.basis.get_scale(); - Vector3 sideways = up.cross(forward).normalized(); - up = forward.cross(sideways).normalized(); - - t.basis.set_columns(sideways, up, forward); - t.basis.scale_local(scale); - - t.origin = pos + sideways * h_offset + up * v_offset; - } else if (rotation_mode != ROTATION_NONE) { - // perform parallel transport - // - // see C. Dougan, The Parallel Transport Frame, Game Programming Gems 2 for example - // for a discussion about why not Frenet frame. + Transform3D t; + if (rotation_mode == ROTATION_NONE) { + Vector3 pos = c->sample_baked(progress, cubic); t.origin = pos; - if (p_update_xyz_rot && prev_offset != progress) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree. - real_t sample_distance = bi * 0.01; - Vector3 t_prev_pos_a = c->sample_baked(prev_offset - sample_distance, cubic); - Vector3 t_prev_pos_b = c->sample_baked(prev_offset + sample_distance, cubic); - Vector3 t_cur_pos_a = c->sample_baked(progress - sample_distance, cubic); - Vector3 t_cur_pos_b = c->sample_baked(progress + sample_distance, cubic); - Vector3 t_prev = (t_prev_pos_a - t_prev_pos_b).normalized(); - Vector3 t_cur = (t_cur_pos_a - t_cur_pos_b).normalized(); - - Vector3 axis = t_prev.cross(t_cur); - real_t dot = t_prev.dot(t_cur); - real_t angle = Math::acos(CLAMP(dot, -1, 1)); - - if (likely(!Math::is_zero_approx(angle))) { - if (rotation_mode == ROTATION_Y) { - // assuming we're referring to global Y-axis. is this correct? - axis.x = 0; - axis.z = 0; - } else if (rotation_mode == ROTATION_XY) { - axis.z = 0; - } else if (rotation_mode == ROTATION_XYZ) { - // all components are allowed - } + } else { + t = c->sample_baked_with_rotation(progress, cubic, false); + Vector3 forward = t.basis.get_column(2); // Retain tangent for applying tilt + t = PathFollow3D::correct_posture(t, rotation_mode); - if (likely(!Math::is_zero_approx(axis.length()))) { - t.rotate_basis(axis.normalized(), angle); - } - } + // Apply tilt *after* correct_posture + if (tilt_enabled) { + const real_t tilt = c->sample_baked_tilt(progress); - // do the additional tilting - real_t tilt_angle = c->sample_baked_tilt(progress); - Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct?? - - if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) { - if (rotation_mode == ROTATION_Y) { - tilt_axis.x = 0; - tilt_axis.z = 0; - } else if (rotation_mode == ROTATION_XY) { - tilt_axis.z = 0; - } else if (rotation_mode == ROTATION_XYZ) { - // all components are allowed - } - - if (likely(!Math::is_zero_approx(tilt_axis.length()))) { - t.rotate_basis(tilt_axis.normalized(), tilt_angle); - } - } + const Basis twist(forward, tilt); + t.basis = twist * t.basis; } - - t.translate_local(Vector3(h_offset, v_offset, 0)); - } else { - t.origin = pos + Vector3(h_offset, v_offset, 0); } + Vector3 scale = get_transform().basis.get_scale(); + + t.translate_local(Vector3(h_offset, v_offset, 0)); + t.basis.scale_local(scale); + set_transform(t); } @@ -358,6 +264,38 @@ PackedStringArray PathFollow3D::get_configuration_warnings() const { return warnings; } +Transform3D PathFollow3D::correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode) { + Transform3D t = p_transform; + + // Modify frame according to rotation mode. + if (p_rotation_mode == PathFollow3D::ROTATION_NONE) { + // Clear rotation. + t.basis = Basis(); + } else if (p_rotation_mode == PathFollow3D::ROTATION_ORIENTED) { + // Y-axis always straight up. + Vector3 up(0.0, 1.0, 0.0); + Vector3 forward = t.basis.get_column(2); + + t.basis = Basis::looking_at(-forward, up); + } else { + // Lock some euler axes. + Vector3 euler = t.basis.get_euler_normalized(EulerOrder::YXZ); + if (p_rotation_mode == PathFollow3D::ROTATION_Y) { + // Only Y-axis allowed. + euler[0] = 0; + euler[2] = 0; + } else if (p_rotation_mode == PathFollow3D::ROTATION_XY) { + // XY allowed. + euler[2] = 0; + } + + Basis locked = Basis::from_euler(euler, EulerOrder::YXZ); + t.basis = locked; + } + + return t; +} + void PathFollow3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_progress", "progress"), &PathFollow3D::set_progress); ClassDB::bind_method(D_METHOD("get_progress"), &PathFollow3D::get_progress); @@ -380,6 +318,11 @@ void PathFollow3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow3D::set_loop); ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow3D::has_loop); + ClassDB::bind_method(D_METHOD("set_tilt_enabled", "enabled"), &PathFollow3D::set_tilt_enabled); + ClassDB::bind_method(D_METHOD("is_tilt_enabled"), &PathFollow3D::is_tilt_enabled); + + ClassDB::bind_static_method("PathFollow3D", D_METHOD("correct_posture", "transform", "rotation_mode"), &PathFollow3D::correct_posture); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress", PROPERTY_HINT_RANGE, "0,10000,0.01,or_less,or_greater,suffix:m"), "set_progress", "get_progress"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress_ratio", PROPERTY_HINT_RANGE, "0,1,0.0001,or_less,or_greater", PROPERTY_USAGE_EDITOR), "set_progress_ratio", "get_progress_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset"); @@ -387,6 +330,7 @@ void PathFollow3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "None,Y,XY,XYZ,Oriented"), "set_rotation_mode", "get_rotation_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tilt_enabled"), "set_tilt_enabled", "is_tilt_enabled"); BIND_ENUM_CONSTANT(ROTATION_NONE); BIND_ENUM_CONSTANT(ROTATION_Y); @@ -397,7 +341,6 @@ void PathFollow3D::_bind_methods() { void PathFollow3D::set_progress(real_t p_progress) { ERR_FAIL_COND(!isfinite(p_progress)); - prev_offset = progress; progress = p_progress; if (path) { @@ -409,8 +352,6 @@ void PathFollow3D::set_progress(real_t p_progress) { if (!Math::is_zero_approx(p_progress) && Math::is_zero_approx(progress)) { progress = path_length; } - } else { - progress = CLAMP(progress, 0, path_length); } } @@ -476,3 +417,11 @@ void PathFollow3D::set_loop(bool p_loop) { bool PathFollow3D::has_loop() const { return loop; } + +void PathFollow3D::set_tilt_enabled(bool p_enable) { + tilt_enabled = p_enable; +} + +bool PathFollow3D::is_tilt_enabled() const { + return tilt_enabled; +} diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index b161b12185..9d5f694247 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -72,14 +72,16 @@ public: ROTATION_ORIENTED }; + static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode); + private: Path3D *path = nullptr; - real_t prev_offset = 0.0; // Offset during the last _update_transform. real_t progress = 0.0; real_t h_offset = 0.0; real_t v_offset = 0.0; bool cubic = true; bool loop = true; + bool tilt_enabled = true; RotationMode rotation_mode = ROTATION_XYZ; void _update_transform(bool p_update_xyz_rot = true); @@ -106,6 +108,9 @@ public: void set_loop(bool p_loop); bool has_loop() const; + void set_tilt_enabled(bool p_enable); + bool is_tilt_enabled() const; + void set_rotation_mode(RotationMode p_rotation_mode); RotationMode get_rotation_mode() const; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index d9fa37ce74..d29f337fc8 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -34,8 +34,8 @@ #include "scene/scene_string_names.h" void PhysicsBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(false), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(false), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(false), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(false), 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); @@ -80,19 +80,19 @@ TypedArray<PhysicsBody3D> PhysicsBody3D::get_collision_exceptions() { void PhysicsBody3D::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node); - ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two CollisionObject3Ds."); + ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two nodes that inherit from CollisionObject3D (such as Area3D or PhysicsBody3D)."); PhysicsServer3D::get_singleton()->body_add_collision_exception(get_rid(), collision_object->get_rid()); } void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node); - ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two CollisionObject3Ds."); + ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two nodes that inherit from CollisionObject3D (such as Area3D or PhysicsBody3D)."); PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } -Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_distance, bool p_test_only, real_t p_margin, bool p_recovery_as_collision, int p_max_collisions) { - PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); +Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_test_only, real_t p_margin, bool p_recovery_as_collision, int p_max_collisions) { + PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_motion, p_margin); parameters.max_collisions = p_max_collisions; parameters.recovery_as_collision = p_recovery_as_collision; @@ -169,7 +169,7 @@ bool PhysicsBody3D::move_and_collide(const PhysicsServer3D::MotionParameters &p_ return colliding; } -bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, bool p_recovery_as_collision, int p_max_collisions) { +bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, bool p_recovery_as_collision, int p_max_collisions) { ERR_FAIL_COND_V(!is_inside_tree(), false); PhysicsServer3D::MotionResult *r = nullptr; @@ -181,7 +181,7 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distan r = &temp_result; } - PhysicsServer3D::MotionParameters parameters(p_from, p_distance, p_margin); + PhysicsServer3D::MotionParameters parameters(p_from, p_motion, p_margin); parameters.recovery_as_collision = p_recovery_as_collision; return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 4b874b91d9..36b7ce774c 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_distance, bool p_test_only = false, real_t p_margin = 0.001, bool p_recovery_as_collision = false, int p_max_collisions = 1); + Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001, bool p_recovery_as_collision = false, int p_max_collisions = 1); public: 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_distance, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, bool p_recovery_as_collision = false, int p_max_collisions = 1); + bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, bool p_recovery_as_collision = false, 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; diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index a1f962c690..b205c2cde0 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -541,7 +541,7 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { for (int i = 0; i < bone_size; i++) { if (i != p_bone) { - ERR_FAIL_COND(bones[i].name == p_name); + ERR_FAIL_COND_MSG(bones[i].name == p_name, "Skeleton3D: '" + get_name() + "', bone name: '" + p_name + "' is already exist."); } } diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 83e3369423..1814baa9e9 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -607,14 +607,14 @@ TypedArray<PhysicsBody3D> SoftBody3D::get_collision_exceptions() { void SoftBody3D::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node); - ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two CollisionObject3Ds."); + ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two nodes that inherit from CollisionObject3D (such as Area3D or PhysicsBody3D)."); PhysicsServer3D::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid()); } void SoftBody3D::remove_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node); - ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two CollisionObject3Ds."); + ERR_FAIL_COND_MSG(!collision_object, "Collision exception only works between two nodes that inherit from CollisionObject3D (such as Area3D or PhysicsBody3D)."); PhysicsServer3D::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid()); } diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp index f855fce318..6d8ce06524 100644 --- a/scene/3d/spring_arm_3d.cpp +++ b/scene/3d/spring_arm_3d.cpp @@ -31,6 +31,7 @@ #include "spring_arm_3d.h" #include "scene/3d/camera_3d.h" +#include "scene/resources/shape_3d.h" void SpringArm3D::_notification(int p_what) { switch (p_what) { diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 09d31b1ca0..b5b37bf837 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -74,10 +74,6 @@ RID VisualInstance3D::get_instance() const { return instance; } -RID VisualInstance3D::_get_visual_instance_rid() const { - return instance; -} - void VisualInstance3D::set_layer_mask(uint32_t p_mask) { layers = p_mask; RenderingServer::get_singleton()->instance_set_layer_mask(instance, p_mask); @@ -106,7 +102,6 @@ bool VisualInstance3D::get_layer_mask_value(int p_layer_number) const { } void VisualInstance3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("_get_visual_instance_rid"), &VisualInstance3D::_get_visual_instance_rid); ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance3D::set_base); ClassDB::bind_method(D_METHOD("get_base"), &VisualInstance3D::get_base); ClassDB::bind_method(D_METHOD("get_instance"), &VisualInstance3D::get_instance); @@ -216,15 +211,20 @@ GeometryInstance3D::VisibilityRangeFadeMode GeometryInstance3D::get_visibility_r } const StringName *GeometryInstance3D::_instance_uniform_get_remap(const StringName p_name) const { - StringName *r = instance_uniform_property_remap.getptr(p_name); + StringName *r = instance_shader_parameter_property_remap.getptr(p_name); if (!r) { String s = p_name; +#ifndef DISABLE_DEPRECATED if (s.begins_with("shader_uniforms/")) { - StringName name = s.replace("shader_uniforms/", ""); - instance_uniform_property_remap[p_name] = name; - return instance_uniform_property_remap.getptr(p_name); + s = s.replace("shader_uniforms/", "instance_shader_parameters/"); + } +#endif // DISABLE_DEPRECATED + if (s.begins_with("instance_shader_parameters/")) { + StringName pname = StringName(s); + StringName name = s.replace("instance_shader_parameters/", ""); + instance_shader_parameter_property_remap[pname] = name; + return instance_shader_parameter_property_remap.getptr(pname); } - return nullptr; } @@ -247,7 +247,7 @@ bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value) set_gi_mode(GI_MODE_DYNAMIC); return true; } -#endif +#endif // DISABLE_DEPRECATED return false; } @@ -270,13 +270,13 @@ void GeometryInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { if (def_value.get_type() != Variant::NIL) { has_def_value = true; } - if (instance_uniforms.has(pi.name)) { + if (instance_shader_parameters.has(pi.name)) { pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : PROPERTY_USAGE_NONE); } else { pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : PROPERTY_USAGE_NONE); //do not save if not changed } - pi.name = "shader_uniforms/" + pi.name; + pi.name = "instance_shader_parameters/" + pi.name; p_list->push_back(pi); } } @@ -315,9 +315,9 @@ void GeometryInstance3D::set_instance_shader_parameter(const StringName &p_name, if (p_value.get_type() == Variant::NIL) { Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_name); RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_name, def_value); - instance_uniforms.erase(p_value); + instance_shader_parameters.erase(p_value); } else { - instance_uniforms[p_name] = p_value; + instance_shader_parameters[p_name] = p_value; if (p_value.get_type() == Variant::OBJECT) { RID tex_id = p_value; RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_name, tex_id); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index c9a25d9705..f18bff2ddc 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -40,8 +40,6 @@ class VisualInstance3D : public Node3D { RID instance; uint32_t layers = 1; - RID _get_visual_instance_rid() const; - protected: void _update_visibility(); @@ -119,8 +117,8 @@ private: float lod_bias = 1.0; - mutable HashMap<StringName, Variant> instance_uniforms; - mutable HashMap<StringName, StringName> instance_uniform_property_remap; + mutable HashMap<StringName, Variant> instance_shader_parameters; + mutable HashMap<StringName, StringName> instance_shader_parameter_property_remap; float extra_cull_margin = 0.0; LightmapScale lightmap_scale = LIGHTMAP_SCALE_1X; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index f5d30c584f..ca7d1dfc1d 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -644,6 +644,12 @@ void XROrigin3D::set_current(bool p_enabled) { origin_nodes[i]->set_current(false); } } + + // update XRServer with our current position + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + xr_server->set_world_origin(get_global_transform()); } else { bool found = false; // We no longer have a current origin so find the first one we can make current diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index f30aea3bdd..3153572517 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -309,8 +309,8 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_see if (i == point_lower || i == point_higher) { double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, true); max_time_remaining = MAX(max_time_remaining, remaining); - } else { - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + } else if (sync) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, true); } } diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 0d8b7ba8f7..b376f668ad 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -512,8 +512,8 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see } } - if (!found) { - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + if (sync && !found) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, true); } } } else { @@ -550,9 +550,11 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see mind = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true); } - for (int i = 0; i < blend_points_used; i++) { - if (i != cur_closest) { - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + if (sync) { + for (int i = 0; i < blend_points_used; i++) { + if (i != cur_closest) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, true); + } } } } diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 0c91729a6f..846c102e3f 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -726,9 +726,11 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_ double rem = 0.0; - for (int i = 0; i < enabled_inputs; i++) { - if (i != cur_current && i != cur_prev) { - blend_input(i, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + if (sync) { + for (int i = 0; i < enabled_inputs; i++) { + if (i != cur_current && i != cur_prev) { + blend_input(i, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, true); + } } } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 59930a3fbb..85bc4e9814 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1442,11 +1442,11 @@ void AnimationPlayer::remove_animation_library(const StringName &p_name) { } void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) { - Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), CONNECT_REFERENCE_COUNTED); + Ref<Animation>(p_anim)->connect("changed", callable_mp(this, &AnimationPlayer::_animation_changed), CONNECT_REFERENCE_COUNTED); } void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) { - Ref<Animation>(p_anim)->disconnect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed)); + Ref<Animation>(p_anim)->disconnect("changed", callable_mp(this, &AnimationPlayer::_animation_changed)); } void AnimationPlayer::rename_animation_library(const StringName &p_name, const StringName &p_new_name) { diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 517908077d..8729ab156b 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -221,7 +221,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri } blendw[i] = blendr[i] * p_blend; - if (blendw[i] > CMP_EPSILON) { + if (!Math::is_zero_approx(blendw[i])) { any_valid = true; } } @@ -236,7 +236,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri } blendw[i] = blendr[i] * p_blend; - if (blendw[i] > CMP_EPSILON) { + if (!Math::is_zero_approx(blendw[i])) { any_valid = true; } } @@ -252,7 +252,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri blendw[i] = blendr[i]; //not filtered, do not blend } - if (blendw[i] > CMP_EPSILON) { + if (!Math::is_zero_approx(blendw[i])) { any_valid = true; } } @@ -263,7 +263,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri for (int i = 0; i < blend_count; i++) { //regular blend blendw[i] = blendr[i] * p_blend; - if (blendw[i] > CMP_EPSILON) { + if (!Math::is_zero_approx(blendw[i])) { any_valid = true; } } @@ -289,11 +289,8 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri new_path = String(parent->base_path) + String(p_subpath) + "/"; } - // If tracks for blending don't exist for one of the animations, Rest or RESET animation is blended as init animation instead. - // Then blend weight is 0 means that the init animation blend weight is 1. - // In that case, processing only the animation with the lacking track will not process the lacking track, and will not properly apply the Reset value. - // This means that all tracks which the animations in the branch that may be blended have must be processed. - // Therefore, the blending process must be executed even if the blend weight is 0. + // This process, which depends on p_sync is needed to process sync correctly in the case of + // that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync. if (!p_seek && !p_sync && !any_valid) { return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections); } @@ -589,6 +586,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_value->object = child; } + track_value->is_discrete = anim->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE || anim->value_track_get_update_mode(i) == Animation::UPDATE_TRIGGER; track_value->is_using_angle = anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; track_value->subpath = leftover_path; @@ -596,6 +594,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track = track_value; + // If a value track without a key is cached first, the initial value cannot be determined. + // It is a corner case, but which may cause problems with blending. + ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, "AnimationTree: '" + String(E) + "', value track: '" + String(path) + "' must have at least one key to cache for blending."); + track_value->init_value = anim->track_get_key_value(i, 0); + track_value->init_value.zero(); + + // If there is a Reset Animation, it takes precedence by overwriting. if (has_reset_anim) { int rt = reset_anim->find_track(path, track_type); if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { @@ -795,8 +800,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } } else if (track_cache_type == Animation::TYPE_VALUE) { // If it has at least one angle interpolation, it also uses angle interpolation for blending. - TrackCacheValue *track_value = memnew(TrackCacheValue); + TrackCacheValue *track_value = static_cast<TrackCacheValue *>(track); + bool was_discrete = track_value->is_discrete; + bool was_using_angle = track_value->is_using_angle; + track_value->is_discrete |= anim->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE || anim->value_track_get_update_mode(i) == Animation::UPDATE_TRIGGER; track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; + + if (was_discrete != track_value->is_discrete) { + WARN_PRINT_ONCE("Tracks with different update modes are blended. Blending prioritizes Discrete/Trigger mode, so other update mode tracks will not be blended."); + } + if (was_using_angle != track_value->is_using_angle) { + WARN_PRINT_ONCE("Tracks for rotation with different interpolation types are blended. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value."); + } } track->setup_pass = setup_pass; @@ -955,8 +970,44 @@ void AnimationTree::_process_graph(double p_delta) { if (!state.valid) { return; //state is not valid. do nothing. } - //apply value/transform/bezier blends to track caches and execute method/audio/animation tracks + // Init all value/transform/blend/bezier tracks that track_cache has. + { + for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { + TrackCache *track = K.value; + + switch (track->type) { + case Animation::TYPE_POSITION_3D: { + TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); + if (track->root_motion) { + t->loc = Vector3(0, 0, 0); + t->rot = Quaternion(0, 0, 0, 1); + t->scale = Vector3(0, 0, 0); + } else { + t->loc = t->init_loc; + t->rot = t->init_rot; + t->scale = t->init_scale; + } + } break; + case Animation::TYPE_BLEND_SHAPE: { + TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); + t->value = t->init_value; + } break; + case Animation::TYPE_VALUE: { + TrackCacheValue *t = static_cast<TrackCacheValue *>(track); + t->value = t->init_value; + } break; + case Animation::TYPE_BEZIER: { + TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); + t->value = t->init_value; + } break; + default: { + } break; + } + } + } + + // Apply value/transform/blend/bezier blends to track caches and execute method/audio/animation tracks. { bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); @@ -968,7 +1019,7 @@ void AnimationTree::_process_graph(double p_delta) { bool seeked = as.seeked; int pingponged = as.pingponged; #ifndef _3D_DISABLED - bool backward = signbit(delta); + bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames. bool calc_root = !seeked || as.seek_root; #endif // _3D_DISABLED @@ -978,37 +1029,29 @@ void AnimationTree::_process_graph(double p_delta) { } NodePath path = a->track_get_path(i); - ERR_CONTINUE(!track_cache.has(path)); - TrackCache *track = track_cache[path]; + ERR_CONTINUE(!state.track_map.has(path)); + int blend_idx = state.track_map[path]; + ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count); + real_t blend = (*as.track_blends)[blend_idx] * weight; + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } + 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; - ERR_CONTINUE(!state.track_map.has(path)); - int blend_idx = state.track_map[path]; - - ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count); - - real_t blend = (*as.track_blends)[blend_idx] * weight; - switch (ttype) { case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (track->root_motion && calc_root) { - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = Vector3(0, 0, 0); - t->rot = Quaternion(0, 0, 0, 1); - t->scale = Vector3(0, 0, 0); - } double prev_time = time - delta; if (!backward) { if (prev_time < 0) { @@ -1084,12 +1127,6 @@ void AnimationTree::_process_graph(double p_delta) { prev_time = !backward ? 0 : (double)a->get_length(); } else { - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = t->init_loc; - t->rot = t->init_rot; - t->scale = t->init_scale; - } Vector3 loc; Error err = a->position_track_interpolate(i, time, &loc); @@ -1106,12 +1143,6 @@ void AnimationTree::_process_graph(double p_delta) { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (track->root_motion && calc_root) { - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = Vector3(0, 0, 0); - t->rot = Quaternion(0, 0, 0, 1); - t->scale = Vector3(0, 0, 0); - } double prev_time = time - delta; if (!backward) { if (prev_time < 0) { @@ -1186,12 +1217,6 @@ void AnimationTree::_process_graph(double p_delta) { prev_time = !backward ? 0 : (double)a->get_length(); } else { - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = t->init_loc; - t->rot = t->init_rot; - t->scale = t->init_scale; - } Quaternion rot; Error err = a->rotation_track_interpolate(i, time, &rot); @@ -1208,12 +1233,6 @@ void AnimationTree::_process_graph(double p_delta) { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (track->root_motion && calc_root) { - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = Vector3(0, 0, 0); - t->rot = Quaternion(0, 0, 0, 1); - t->scale = Vector3(0, 0, 0); - } double prev_time = time - delta; if (!backward) { if (prev_time < 0) { @@ -1289,12 +1308,6 @@ void AnimationTree::_process_graph(double p_delta) { prev_time = !backward ? 0 : (double)a->get_length(); } else { - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = t->init_loc; - t->rot = t->init_rot; - t->scale = t->init_scale; - } Vector3 scale; Error err = a->scale_track_interpolate(i, time, &scale); @@ -1311,11 +1324,6 @@ void AnimationTree::_process_graph(double p_delta) { #ifndef _3D_DISABLED TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->value = t->init_value; - } - float value; Error err = a->blend_shape_track_interpolate(i, time, &value); @@ -1342,15 +1350,6 @@ void AnimationTree::_process_graph(double p_delta) { continue; } - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - if (!t->init_value) { - t->init_value = value; - t->init_value.zero(); - } - t->value = t->init_value; - } - // Special case for angle interpolation. if (t->is_using_angle) { // For blending consistency, it prevents rotation of more than 180 degrees from init_value. @@ -1379,10 +1378,6 @@ void AnimationTree::_process_graph(double p_delta) { } } } else { - if (blend < CMP_EPSILON) { - continue; //nothing to blend - } - if (seeked) { int idx = a->track_find_key(i, time); if (idx < 0) { @@ -1404,9 +1399,6 @@ void AnimationTree::_process_graph(double p_delta) { } break; case Animation::TYPE_METHOD: { - if (blend < CMP_EPSILON) { - continue; //nothing to blend - } TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track); if (seeked) { @@ -1437,17 +1429,9 @@ void AnimationTree::_process_graph(double p_delta) { real_t bezier = a->bezier_track_interpolate(i, time); bezier = _post_process_key_value(a, i, bezier, t->object); - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->value = t->init_value; - } - t->value += (bezier - t->init_value) * blend; } break; case Animation::TYPE_AUDIO: { - if (blend < CMP_EPSILON) { - continue; //nothing to blend - } TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); if (seeked) { @@ -1555,9 +1539,6 @@ void AnimationTree::_process_graph(double p_delta) { t->object->call(SNAME("set_volume_db"), db); } break; case Animation::TYPE_ANIMATION: { - if (blend < CMP_EPSILON) { - continue; //nothing to blend - } TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track); AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object); @@ -1639,9 +1620,6 @@ void AnimationTree::_process_graph(double p_delta) { // finally, set the tracks for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { TrackCache *track = K.value; - if (track->process_pass != process_pass) { - continue; //not processed, ignore - } switch (track->type) { case Animation::TYPE_POSITION_3D: { @@ -1691,6 +1669,10 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); + if (t->is_discrete) { + break; // Don't overwrite the value set by UPDATE_DISCRETE or UPDATE_TRIGGER. + } + if (t->init_value.get_type() == Variant::BOOL) { t->object->set_indexed(t->subpath, t->value.operator real_t() >= 0.5); } else { diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 96c1279f82..84d0a8190a 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -190,7 +190,6 @@ private: struct TrackCache { bool root_motion = false; uint64_t setup_pass = 0; - uint64_t process_pass = 0; Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION; Object *object = nullptr; ObjectID object_id; @@ -233,6 +232,7 @@ private: Variant init_value; Variant value; Vector<StringName> subpath; + bool is_discrete = false; bool is_using_angle = false; TrackCacheValue() { type = Animation::TYPE_VALUE; } }; diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index b4bdda9ecb..599a759b08 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -39,87 +39,10 @@ #include "scene/main/window.h" #include "scene/resources/packed_scene.h" -Array SceneDebugger::RPCProfilerFrame::serialize() { - Array arr; - arr.push_back(infos.size() * 4); - for (int i = 0; i < infos.size(); ++i) { - arr.push_back(uint64_t(infos[i].node)); - arr.push_back(infos[i].node_path); - arr.push_back(infos[i].incoming_rpc); - arr.push_back(infos[i].outgoing_rpc); - } - return arr; -} - -bool SceneDebugger::RPCProfilerFrame::deserialize(const Array &p_arr) { - ERR_FAIL_COND_V(p_arr.size() < 1, false); - uint32_t size = p_arr[0]; - ERR_FAIL_COND_V(size % 4, false); - ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false); - infos.resize(size / 4); - int idx = 1; - for (uint32_t i = 0; i < size / 4; ++i) { - infos.write[i].node = uint64_t(p_arr[idx]); - infos.write[i].node_path = p_arr[idx + 1]; - infos.write[i].incoming_rpc = p_arr[idx + 2]; - infos.write[i].outgoing_rpc = p_arr[idx + 3]; - } - return true; -} - -class SceneDebugger::RPCProfiler : public EngineProfiler { - HashMap<ObjectID, RPCNodeInfo> rpc_node_data; - uint64_t last_profile_time = 0; - - void init_node(const ObjectID p_node) { - if (rpc_node_data.has(p_node)) { - return; - } - rpc_node_data.insert(p_node, RPCNodeInfo()); - rpc_node_data[p_node].node = p_node; - rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path(); - rpc_node_data[p_node].incoming_rpc = 0; - rpc_node_data[p_node].outgoing_rpc = 0; - } - -public: - void toggle(bool p_enable, const Array &p_opts) { - rpc_node_data.clear(); - } - - void add(const Array &p_data) { - ERR_FAIL_COND(p_data.size() < 2); - const ObjectID id = p_data[0]; - const String what = p_data[1]; - init_node(id); - RPCNodeInfo &info = rpc_node_data[id]; - if (what == "rpc_in") { - info.incoming_rpc++; - } else if (what == "rpc_out") { - info.outgoing_rpc++; - } - } - - void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { - uint64_t pt = OS::get_singleton()->get_ticks_msec(); - if (pt - last_profile_time > 100) { - last_profile_time = pt; - RPCProfilerFrame frame; - for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) { - frame.infos.push_back(E.value); - } - rpc_node_data.clear(); - EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize()); - } - } -}; - SceneDebugger *SceneDebugger::singleton = nullptr; SceneDebugger::SceneDebugger() { singleton = this; - rpc_profiler.instantiate(); - rpc_profiler->bind("rpc"); #ifdef DEBUG_ENABLED LiveEditor::singleton = memnew(LiveEditor); EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message)); @@ -264,7 +187,7 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); live_editor->_create_node_func(p_args[0], p_args[1], p_args[2]); - } else if (p_msg == "live_instance_node") { + } else if (p_msg == "live_instantiate_node") { ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); live_editor->_instance_node_func(p_args[0], p_args[1], p_args[2]); diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index fe35446aae..0428bfcc2e 100644 --- a/scene/debugger/scene_debugger.h +++ b/scene/debugger/scene_debugger.h @@ -42,28 +42,9 @@ class Node; class SceneDebugger { public: - // RPC profiler - struct RPCNodeInfo { - ObjectID node; - String node_path; - int incoming_rpc = 0; - int outgoing_rpc = 0; - }; - - struct RPCProfilerFrame { - Vector<RPCNodeInfo> infos; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; - private: - class RPCProfiler; - static SceneDebugger *singleton; - Ref<RPCProfiler> rpc_profiler; - SceneDebugger(); public: diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index c2b82e01d1..0e7bc5c306 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -231,7 +231,7 @@ void Button::_notification(int p_what) { _icon = icon; } - Rect2 icon_region = Rect2(); + Rect2 icon_region; HorizontalAlignment icon_align_rtl_checked = icon_alignment; HorizontalAlignment align_rtl_checked = alignment; // Swap icon and text alignment sides if right-to-left layout is set. diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 5f8f25154c..9e0dc049e5 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -694,8 +694,8 @@ void CodeEdit::_backspace_internal(int p_caret) { return; } - if (has_selection()) { - delete_selection(); + if (has_selection(p_caret)) { + delete_selection(p_caret); return; } @@ -1425,7 +1425,10 @@ bool CodeEdit::is_line_numbers_zero_padded() const { } void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { - String fc = TS->format_number(String::num(p_line + 1).lpad(line_number_digits, line_number_padding)); + String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding); + if (is_localizing_numeral_system()) { + fc = TS->format_number(fc); + } Ref<TextLine> tl; tl.instantiate(); tl->add_string(fc, font, font_size); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 724e5bcaf6..eb9f9039b7 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -959,7 +959,7 @@ void ColorPicker::_sample_draw() { // Draw both old and new colors for easier comparison (only if spawned from a ColorPickerButton). const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95)); - if (display_old_color && old_color.a < 1.0) { + if (old_color.a < 1.0) { sample->draw_texture_rect(get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), rect_old, true); } @@ -1088,7 +1088,9 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { } else if (p_which == 1) { if (actual_shape == SHAPE_HSV_RECTANGLE) { Ref<Texture2D> hue = get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); - c->draw_texture_rect(hue, Rect2(Point2(), c->get_size())); + c->draw_set_transform(Point2(), -Math_PI / 2, Size2(c->get_size().x, -c->get_size().y)); + c->draw_texture_rect(hue, Rect2(Point2(), Size2(1, 1))); + c->draw_set_transform(Point2(), 0, Size2(1, 1)); int y = c->get_size().y - c->get_size().y * (1.0 - h); Color col; col.set_hsv(h, 1, 1); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index c5cb7157e8..92ee21a916 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2773,6 +2773,20 @@ bool Control::is_layout_rtl() const { return data.is_rtl; } +void Control::set_localize_numeral_system(bool p_enable) { + if (p_enable == data.localize_numeral_system) { + return; + } + + data.localize_numeral_system = p_enable; + + notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); +} + +bool Control::is_localizing_numeral_system() const { + return data.localize_numeral_system; +} + void Control::set_auto_translate(bool p_enable) { if (p_enable == data.auto_translate) { return; @@ -2921,7 +2935,7 @@ void Control::_notification(int p_notification) { queue_redraw(); if (data.RI) { - get_viewport()->_gui_set_root_order_dirty(); + get_viewport()->gui_set_root_order_dirty(); } } break; @@ -3154,6 +3168,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate); ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating); + ClassDB::bind_method(D_METHOD("set_localize_numeral_system", "enable"), &Control::set_localize_numeral_system); + ClassDB::bind_method(D_METHOD("is_localizing_numeral_system"), &Control::is_localizing_numeral_system); + ADD_GROUP("Layout", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size"); @@ -3198,8 +3215,9 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_v_size_flags", "get_v_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); - ADD_GROUP("Auto Translate", ""); + ADD_GROUP("Localization", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "localize_numeral_system"), "set_localize_numeral_system", "is_localizing_numeral_system"); ADD_GROUP("Tooltip", "tooltip_"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text"); diff --git a/scene/gui/control.h b/scene/gui/control.h index 72e870930d..3e9bb48a4a 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -249,6 +249,7 @@ private: bool is_rtl = false; bool auto_translate = true; + bool localize_numeral_system = true; // Extra properties. @@ -595,6 +596,9 @@ public: LayoutDirection get_layout_direction() const; virtual bool is_layout_rtl() const; + void set_localize_numeral_system(bool p_enable); + bool is_localizing_numeral_system() const; + void set_auto_translate(bool p_enable); bool is_auto_translating() const; _FORCE_INLINE_ String atr(const String p_string) const { diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index bf4dd3d245..0d265719ec 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -377,7 +377,7 @@ void AcceptDialog::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "ok_button_text"), "set_ok_button_text", "get_ok_button_text"); - ADD_GROUP("Dialog", "dialog"); + ADD_GROUP("Dialog", "dialog_"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_close_on_escape"), "set_close_on_escape", "get_close_on_escape"); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index cade65108c..11a3803b35 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -632,8 +632,11 @@ void FileDialog::update_file_list() { files.pop_front(); } - if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { - tree->get_root()->get_first_child()->select(0); + if (mode != FILE_MODE_SAVE_FILE) { + // Select the first file from list if nothing is selected. + if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { + tree->get_root()->get_first_child()->select(0); + } } } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 46b712379d..40792dd43f 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -2406,7 +2406,7 @@ void GraphEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_step"), "set_zoom_step", "get_zoom_step"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_zoom_label"), "set_show_zoom_label", "is_showing_zoom_label"); - ADD_GROUP("Minimap", "minimap"); + ADD_GROUP("Minimap", "minimap_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_enabled"), "set_minimap_enabled", "is_minimap_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size", PROPERTY_HINT_NONE, "suffix:px"), "set_minimap_size", "get_minimap_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "minimap_opacity"), "set_minimap_opacity", "get_minimap_opacity"); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index d6b5557a3f..82f089735d 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -896,7 +896,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { if (k.is_valid() && k->get_unicode()) { uint64_t now = OS::get_singleton()->get_ticks_msec(); uint64_t diff = now - search_time_msec; - uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000)); + uint64_t max_interval = uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec")); search_time_msec = now; if (diff > max_interval) { @@ -1831,9 +1831,6 @@ void ItemList::_bind_methods() { ADD_SIGNAL(MethodInfo("item_clicked", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "selected"))); ADD_SIGNAL(MethodInfo("item_activated", PropertyInfo(Variant::INT, "index"))); - - GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000); - ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers } ItemList::ItemList() { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index fb5ab9f923..8a77c39487 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -960,17 +960,30 @@ void LineEdit::_notification(int p_what) { if (ime_text.length() == 0) { // Normal caret. CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column); - - if (caret.l_caret == Rect2() && caret.t_caret == Rect2()) { + if (using_placeholder || (caret.l_caret == Rect2() && caret.t_caret == Rect2())) { // No carets, add one at the start. int h = theme_cache.font->get_height(theme_cache.font_size); int y = style->get_offset().y + (y_area - h) / 2; - if (rtl) { - caret.l_dir = TextServer::DIRECTION_RTL; - caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h)); - } else { - caret.l_dir = TextServer::DIRECTION_LTR; - caret.l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h)); + caret.l_dir = (rtl) ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR; + switch (alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_LEFT: { + if (rtl) { + caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h)); + } else { + caret.l_caret = Rect2(Vector2(style->get_offset().x, y), Size2(caret_width, h)); + } + } break; + case HORIZONTAL_ALIGNMENT_CENTER: { + caret.l_caret = Rect2(Vector2(size.x / 2, y), Size2(caret_width, h)); + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (rtl) { + caret.l_caret = Rect2(Vector2(style->get_offset().x, y), Size2(caret_width, h)); + } else { + caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h)); + } + } break; } RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color); } else { diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 2cbece69f2..6d0bbdd6af 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -451,7 +451,7 @@ void OptionButton::_queue_refresh_cache() { } cache_refresh_pending = true; - callable_mp(this, &OptionButton::_refresh_size_cache).call_deferredp(nullptr, 0); + callable_mp(this, &OptionButton::_refresh_size_cache).call_deferred(); } void OptionButton::select(int p_idx) { @@ -491,9 +491,9 @@ void OptionButton::show_popup() { return; } - Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); - popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); - popup->set_size(Size2(size.width, 0)); + Size2 button_size = get_global_transform_with_canvas().get_scale() * get_size(); + popup->set_position(get_screen_position() + Size2(0, button_size.height)); + popup->set_size(Size2i(button_size.width, 0)); // If not triggered by the mouse, start the popup with the checked item (or the first enabled one) focused. if (current != NONE_SELECTED && !popup->is_item_disabled(current)) { diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 9a411ef7ed..ab74979777 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -472,7 +472,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { if (allow_search && k.is_valid() && k->get_unicode() && k->is_pressed()) { uint64_t now = OS::get_singleton()->get_ticks_msec(); uint64_t diff = now - search_time_msec; - uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000)); + uint64_t max_interval = uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec")); search_time_msec = now; if (diff > max_interval) { @@ -558,7 +558,7 @@ void PopupMenu::_draw_items() { check_ofs += theme_cache.h_separation; } - Point2 ofs = Point2(); + Point2 ofs; // Loop through all items and draw each. for (int i = 0; i < items.size(); i++) { diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 8369eaa227..50bcfa6a0c 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -103,7 +103,12 @@ void ProgressBar::_notification(int p_what) { } if (show_percentage) { - String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); + String txt = itos(int(get_as_ratio() * 100)); + if (is_localizing_numeral_system()) { + txt = TS->format_number(txt) + TS->percent_sign(); + } else { + txt += String("%"); + } TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size); Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 7f487175dc..642a94b23e 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -752,7 +752,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o prefix = _prefix; break; } else if (list_items[i]->list_type == LIST_NUMBERS) { - segment = TS->format_number(itos(list_index[i]), _find_language(l.from)); + segment = itos(list_index[i]); + if (is_localizing_numeral_system()) { + segment = TS->format_number(segment, _find_language(l.from)); + } } else if (list_items[i]->list_type == LIST_LETTERS) { segment = _letters(list_index[i], list_items[i]->capitalize); } else if (list_items[i]->list_type == LIST_ROMAN) { @@ -2686,6 +2689,7 @@ bool RichTextLabel::_validate_line_caches() { int ctrl_height = get_size().height; // Update fonts. + float old_scroll = vscroll->get_value(); if (main->first_invalid_font_line.load() != (int)main->lines.size()) { for (int i = main->first_invalid_font_line.load(); i < (int)main->lines.size(); i++) { _update_line_font(main, i, theme_cache.normal_font, theme_cache.normal_font_size); @@ -2695,6 +2699,7 @@ bool RichTextLabel::_validate_line_caches() { } if (main->first_resized_line.load() == (int)main->lines.size()) { + vscroll->set_value(old_scroll); return true; } @@ -2733,6 +2738,8 @@ bool RichTextLabel::_validate_line_caches() { vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { vscroll->set_value(total_height); + } else { + vscroll->set_value(old_scroll); } updating_scroll = false; @@ -3853,6 +3860,10 @@ void RichTextLabel::append_text(const String &p_bbcode) { Color color2 = Color::from_string(subtag_b[1], fallback_color); set_cell_row_background_color(color1, color2); } + if (subtag_b.size() == 1) { + Color color1 = Color::from_string(subtag_a[1], fallback_color); + set_cell_row_background_color(color1, color1); + } } } } @@ -4057,7 +4068,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { Color color = theme_cache.default_color; Color outline_color = theme_cache.font_outline_color; int outline_size = theme_cache.outline_size; - Rect2 dropcap_margins = Rect2(); + Rect2 dropcap_margins; for (int i = 0; i < subtag.size(); i++) { Vector<String> subtag_a = subtag[i].split("="); @@ -5385,7 +5396,7 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); ADD_GROUP("Markup", ""); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hint_underlined"), "set_hint_underline", "is_hint_underlined"); @@ -5716,11 +5727,11 @@ Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_id } Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) { - Dictionary d = Dictionary(); + Dictionary d; for (int i = 0; i < p_expressions.size(); i++) { String expression = p_expressions[i]; - Array a = Array(); + Array a; Vector<String> parts = expression.split("=", true); String key = parts[0]; if (parts.size() != 2) { diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index 13ca62d7ff..d62acd52af 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -72,7 +72,7 @@ class ScrollBar : public Range { NodePath drag_node_path; bool drag_node_enabled = true; - Vector2 drag_node_speed = Vector2(); + Vector2 drag_node_speed; Vector2 drag_node_accum; Vector2 drag_node_from; Vector2 last_drag_node_accum; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index ec34a8d26d..531226f938 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -182,14 +182,12 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { drag_accum = Vector2(); last_drag_accum = Vector2(); drag_from = Vector2(prev_h_scroll, prev_v_scroll); - drag_touching = screen_is_touchscreen; + drag_touching = true; drag_touching_deaccel = false; beyond_deadzone = false; time_since_motion = 0; - if (drag_touching) { - set_physics_process_internal(true); - time_since_motion = 0; - } + set_physics_process_internal(true); + time_since_motion = 0; } else { if (drag_touching) { diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index c4000120c8..e15b3b7bd4 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -40,7 +40,10 @@ Size2 SpinBox::get_minimum_size() const { } void SpinBox::_value_changed(double p_value) { - String value = TS->format_number(String::num(get_value(), Math::range_step_decimals(get_step()))); + String value = String::num(get_value(), Math::range_step_decimals(get_step())); + if (is_localizing_numeral_system()) { + value = TS->format_number(value); + } if (!line_edit->has_focus()) { if (!prefix.is_empty()) { diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index 2ca1d6239e..9830b41389 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -71,7 +71,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { Vector2i in_parent_pos = get_transform().xform(mm->get_position()); if (!sc->vertical && is_layout_rtl()) { - sc->split_offset = drag_ofs - ((sc->vertical ? in_parent_pos.y : in_parent_pos.x) - drag_from); + sc->split_offset = drag_ofs - (in_parent_pos.x - drag_from); } else { sc->split_offset = drag_ofs + ((sc->vertical ? in_parent_pos.y : in_parent_pos.x) - drag_from); } @@ -194,7 +194,6 @@ void SplitContainer::_compute_middle_sep(bool p_clamp) { // Clamp the split_offset if requested. if (p_clamp) { split_offset -= wished_middle_sep - middle_sep; - p_clamp = false; } } diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 3ad84cbc6d..f3d9a22342 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -227,6 +227,18 @@ void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) { } } +void SubViewportContainer::add_child_notify(Node *p_child) { + if (Object::cast_to<SubViewport>(p_child)) { + queue_redraw(); + } +} + +void SubViewportContainer::remove_child_notify(Node *p_child) { + if (Object::cast_to<SubViewport>(p_child)) { + queue_redraw(); + } +} + PackedStringArray SubViewportContainer::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index 63a58b5f07..fdd8fe9486 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -44,6 +44,9 @@ protected: void _notification(int p_what); static void _bind_methods(); + virtual void add_child_notify(Node *p_child) override; + virtual void remove_child_notify(Node *p_child) override; + public: void set_stretch(bool p_enable); bool is_stretch_enabled() const; diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index cf6681f809..f87829cf71 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -385,9 +385,6 @@ void TabBar::_notification(int p_what) { if (tabs[i].disabled) { sb = theme_cache.tab_disabled_style; col = theme_cache.font_disabled_color; - } else if (i == current) { - sb = theme_cache.tab_selected_style; - col = theme_cache.font_selected_color; } else { sb = theme_cache.tab_unselected_style; col = theme_cache.font_unselected_color; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 1b8444abf4..cce9fa4f34 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1854,6 +1854,12 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } else { if (mb->get_button_index() == MouseButton::LEFT) { if (selection_drag_attempt && is_mouse_over_selection()) { + remove_secondary_carets(); + + Point2i pos = get_line_column_at_pos(get_local_mouse_pos()); + set_caret_line(pos.y, false, true, 0, 0); + set_caret_column(pos.x, true, 0); + deselect(); } dragging_minimap = false; @@ -4228,7 +4234,7 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_ if (!p_allow_out_of_bounds) { return Point2i(-1, -1); } - return Point2i(text[row].size(), row); + return Point2i(text[row].length(), row); } int col = 0; diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index d9ab1c2c55..ccdf56c1d7 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -64,7 +64,7 @@ Size2 TextureButton::get_minimum_size() const { bool TextureButton::has_point(const Point2 &p_point) const { if (click_mask.is_valid()) { Point2 point = p_point; - Rect2 rect = Rect2(); + Rect2 rect; Size2 mask_size = click_mask->get_size(); if (!_position_rect.has_area()) { @@ -250,11 +250,11 @@ void TextureButton::_notification(int p_what) { } void TextureButton::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_normal_texture", "texture"), &TextureButton::set_normal_texture); - ClassDB::bind_method(D_METHOD("set_pressed_texture", "texture"), &TextureButton::set_pressed_texture); - ClassDB::bind_method(D_METHOD("set_hover_texture", "texture"), &TextureButton::set_hover_texture); - ClassDB::bind_method(D_METHOD("set_disabled_texture", "texture"), &TextureButton::set_disabled_texture); - ClassDB::bind_method(D_METHOD("set_focused_texture", "texture"), &TextureButton::set_focused_texture); + ClassDB::bind_method(D_METHOD("set_texture_normal", "texture"), &TextureButton::set_texture_normal); + ClassDB::bind_method(D_METHOD("set_texture_pressed", "texture"), &TextureButton::set_texture_pressed); + ClassDB::bind_method(D_METHOD("set_texture_hover", "texture"), &TextureButton::set_texture_hover); + ClassDB::bind_method(D_METHOD("set_texture_disabled", "texture"), &TextureButton::set_texture_disabled); + ClassDB::bind_method(D_METHOD("set_texture_focused", "texture"), &TextureButton::set_texture_focused); ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask); ClassDB::bind_method(D_METHOD("set_ignore_texture_size", "ignore"), &TextureButton::set_ignore_texture_size); ClassDB::bind_method(D_METHOD("set_stretch_mode", "mode"), &TextureButton::set_stretch_mode); @@ -263,21 +263,21 @@ void TextureButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureButton::set_flip_v); ClassDB::bind_method(D_METHOD("is_flipped_v"), &TextureButton::is_flipped_v); - ClassDB::bind_method(D_METHOD("get_normal_texture"), &TextureButton::get_normal_texture); - ClassDB::bind_method(D_METHOD("get_pressed_texture"), &TextureButton::get_pressed_texture); - ClassDB::bind_method(D_METHOD("get_hover_texture"), &TextureButton::get_hover_texture); - ClassDB::bind_method(D_METHOD("get_disabled_texture"), &TextureButton::get_disabled_texture); - ClassDB::bind_method(D_METHOD("get_focused_texture"), &TextureButton::get_focused_texture); + ClassDB::bind_method(D_METHOD("get_texture_normal"), &TextureButton::get_texture_normal); + ClassDB::bind_method(D_METHOD("get_texture_pressed"), &TextureButton::get_texture_pressed); + ClassDB::bind_method(D_METHOD("get_texture_hover"), &TextureButton::get_texture_hover); + ClassDB::bind_method(D_METHOD("get_texture_disabled"), &TextureButton::get_texture_disabled); + ClassDB::bind_method(D_METHOD("get_texture_focused"), &TextureButton::get_texture_focused); ClassDB::bind_method(D_METHOD("get_click_mask"), &TextureButton::get_click_mask); ClassDB::bind_method(D_METHOD("get_ignore_texture_size"), &TextureButton::get_ignore_texture_size); ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureButton::get_stretch_mode); ADD_GROUP("Textures", "texture_"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_texture", "get_normal_texture"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_pressed_texture", "get_pressed_texture"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_hover", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_hover_texture", "get_hover_texture"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_disabled", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_disabled_texture", "get_disabled_texture"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_focused", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_focused_texture", "get_focused_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_normal", "get_texture_normal"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_pressed", "get_texture_pressed"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_hover", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_hover", "get_texture_hover"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_disabled", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_disabled", "get_texture_disabled"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_focused", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_focused", "get_texture_focused"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_texture_size", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_ignore_texture_size", "get_ignore_texture_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode"); @@ -293,7 +293,7 @@ void TextureButton::_bind_methods() { BIND_ENUM_CONSTANT(STRETCH_KEEP_ASPECT_COVERED); } -void TextureButton::set_normal_texture(const Ref<Texture2D> &p_normal) { +void TextureButton::set_texture_normal(const Ref<Texture2D> &p_normal) { if (normal == p_normal) { return; } @@ -303,7 +303,7 @@ void TextureButton::set_normal_texture(const Ref<Texture2D> &p_normal) { update_minimum_size(); } -void TextureButton::set_pressed_texture(const Ref<Texture2D> &p_pressed) { +void TextureButton::set_texture_pressed(const Ref<Texture2D> &p_pressed) { if (pressed == p_pressed) { return; } @@ -313,7 +313,7 @@ void TextureButton::set_pressed_texture(const Ref<Texture2D> &p_pressed) { update_minimum_size(); } -void TextureButton::set_hover_texture(const Ref<Texture2D> &p_hover) { +void TextureButton::set_texture_hover(const Ref<Texture2D> &p_hover) { if (hover == p_hover) { return; } @@ -323,7 +323,7 @@ void TextureButton::set_hover_texture(const Ref<Texture2D> &p_hover) { update_minimum_size(); } -void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) { +void TextureButton::set_texture_disabled(const Ref<Texture2D> &p_disabled) { if (disabled == p_disabled) { return; } @@ -341,19 +341,19 @@ void TextureButton::set_click_mask(const Ref<BitMap> &p_click_mask) { update_minimum_size(); } -Ref<Texture2D> TextureButton::get_normal_texture() const { +Ref<Texture2D> TextureButton::get_texture_normal() const { return normal; } -Ref<Texture2D> TextureButton::get_pressed_texture() const { +Ref<Texture2D> TextureButton::get_texture_pressed() const { return pressed; } -Ref<Texture2D> TextureButton::get_hover_texture() const { +Ref<Texture2D> TextureButton::get_texture_hover() const { return hover; } -Ref<Texture2D> TextureButton::get_disabled_texture() const { +Ref<Texture2D> TextureButton::get_texture_disabled() const { return disabled; } @@ -361,11 +361,11 @@ Ref<BitMap> TextureButton::get_click_mask() const { return click_mask; } -Ref<Texture2D> TextureButton::get_focused_texture() const { +Ref<Texture2D> TextureButton::get_texture_focused() const { return focused; }; -void TextureButton::set_focused_texture(const Ref<Texture2D> &p_focused) { +void TextureButton::set_texture_focused(const Ref<Texture2D> &p_focused) { focused = p_focused; }; diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h index 9f6f7c1515..4b6d5b5bec 100644 --- a/scene/gui/texture_button.h +++ b/scene/gui/texture_button.h @@ -71,18 +71,18 @@ protected: static void _bind_methods(); public: - void set_normal_texture(const Ref<Texture2D> &p_normal); - void set_pressed_texture(const Ref<Texture2D> &p_pressed); - void set_hover_texture(const Ref<Texture2D> &p_hover); - void set_disabled_texture(const Ref<Texture2D> &p_disabled); - void set_focused_texture(const Ref<Texture2D> &p_focused); + void set_texture_normal(const Ref<Texture2D> &p_normal); + void set_texture_pressed(const Ref<Texture2D> &p_pressed); + void set_texture_hover(const Ref<Texture2D> &p_hover); + void set_texture_disabled(const Ref<Texture2D> &p_disabled); + void set_texture_focused(const Ref<Texture2D> &p_focused); void set_click_mask(const Ref<BitMap> &p_click_mask); - Ref<Texture2D> get_normal_texture() const; - Ref<Texture2D> get_pressed_texture() const; - Ref<Texture2D> get_hover_texture() const; - Ref<Texture2D> get_disabled_texture() const; - Ref<Texture2D> get_focused_texture() const; + Ref<Texture2D> get_texture_normal() const; + Ref<Texture2D> get_texture_pressed() const; + Ref<Texture2D> get_texture_hover() const; + Ref<Texture2D> get_texture_disabled() const; + Ref<Texture2D> get_texture_focused() const; Ref<BitMap> get_click_mask() const; bool get_ignore_texture_size() const; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 6f9a9a5141..2da76883b4 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -4338,6 +4338,12 @@ TreeItem *Tree::get_selected() const { return selected_item; } +void Tree::set_selected(TreeItem *p_item, int p_column) { + ERR_FAIL_INDEX(p_column, columns.size()); + ERR_FAIL_COND(!p_item); + select_single_item(p_item, get_root(), p_column); +} + int Tree::get_selected_column() const { return selected_col; } @@ -4547,6 +4553,7 @@ void Tree::ensure_cursor_is_visible() { return; // Nothing under cursor. } + // Note: Code below similar to Tree::scroll_to_item(), in case of bug fix both. const Size2 area_size = get_size() - theme_cache.panel_style->get_minimum_size(); int y_offset = get_item_offset(selected_item); @@ -4555,7 +4562,10 @@ void Tree::ensure_cursor_is_visible() { y_offset -= tbh; const int cell_h = compute_item_height(selected_item) + theme_cache.vseparation; - const int screen_h = area_size.height - h_scroll->get_combined_minimum_size().height - tbh; + int screen_h = area_size.height - tbh; + if (h_scroll->is_visible()) { + screen_h -= h_scroll->get_combined_minimum_size().height; + } if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet. v_scroll->set_value(y_offset); @@ -4706,26 +4716,32 @@ Point2 Tree::get_scroll() const { void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) { ERR_FAIL_NULL(p_item); - if (!is_visible_in_tree() || !p_item->is_visible()) { - return; // Hack to work around crash in get_item_rect() if Tree is not in tree. - } update_scrollbars(); - const real_t tree_height = get_size().y; - const Rect2 item_rect = get_item_rect(p_item); - const real_t item_y = item_rect.position.y; - const real_t item_height = item_rect.size.y + theme_cache.vseparation; + // Note: Code below similar to Tree::ensure_cursor_is_visible(), in case of bug fix both. + const Size2 area_size = get_size() - theme_cache.panel_style->get_minimum_size(); - if (p_center_on_item) { - v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f); - } else { - if (item_y < v_scroll->get_value()) { - v_scroll->set_value(item_y); + int y_offset = get_item_offset(p_item); + if (y_offset != -1) { + const int tbh = _get_title_button_height(); + y_offset -= tbh; + + const int cell_h = compute_item_height(p_item) + theme_cache.vseparation; + int screen_h = area_size.height - tbh; + if (h_scroll->is_visible()) { + screen_h -= h_scroll->get_combined_minimum_size().height; + } + + if (p_center_on_item) { + v_scroll->set_value(y_offset - (screen_h - cell_h) / 2.0f); } else { - const real_t new_position = item_y + item_height - tree_height; - if (new_position > v_scroll->get_value()) { - v_scroll->set_value(new_position); + if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet. + v_scroll->set_value(y_offset); + } else if (y_offset + cell_h > v_scroll->get_value() + screen_h) { + v_scroll->set_value(y_offset - screen_h + cell_h); + } else if (y_offset < v_scroll->get_value()) { + v_scroll->set_value(y_offset); } } } @@ -4818,7 +4834,7 @@ TreeItem *Tree::get_item_with_text(const String &p_find) const { void Tree::_do_incr_search(const String &p_add) { uint64_t time = OS::get_singleton()->get_ticks_usec() / 1000; // convert to msec uint64_t diff = time - last_keypress; - if (diff > uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000))) { + if (diff > uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec"))) { incr_search = p_add; } else if (incr_search != p_add) { incr_search += p_add; @@ -5156,6 +5172,7 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden); ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::get_next_selected); ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected); + ClassDB::bind_method(D_METHOD("set_selected", "item", "column"), &Tree::set_selected); ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column); ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button); ClassDB::bind_method(D_METHOD("set_select_mode", "mode"), &Tree::set_select_mode); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index f994a5cec1..77a62e1d6a 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -666,6 +666,7 @@ public: bool is_root_hidden() const; TreeItem *get_next_selected(TreeItem *p_item); TreeItem *get_selected() const; + void set_selected(TreeItem *p_item, int p_column = 0); int get_selected_column() const; int get_pressed_button() const; void set_select_mode(SelectMode p_mode); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index be5788739b..5fde18721a 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -38,7 +38,7 @@ void CanvasLayer::set_layer(int p_xform) { layer = p_xform; if (viewport.is_valid()) { RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index()); - vp->_gui_set_root_order_dirty(); + vp->gui_set_root_order_dirty(); } } diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 2c395ec07d..62f362553f 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -276,10 +276,10 @@ bool HTTPRequest::_handle_response(bool *ret_value) { } if (content_encoding == "gzip") { decompressor.instantiate(); - decompressor->start_decompression(false, get_download_chunk_size() * 2); + decompressor->start_decompression(false, get_download_chunk_size()); } else if (content_encoding == "deflate") { decompressor.instantiate(); - decompressor->start_decompression(true, get_download_chunk_size() * 2); + decompressor->start_decompression(true, get_download_chunk_size()); } return false; @@ -390,19 +390,38 @@ bool HTTPRequest::_update_connection() { return false; } - PackedByteArray chunk = client->read_response_body_chunk(); - downloaded.add(chunk.size()); - - // Decompress chunk if needed. - if (decompressor.is_valid()) { - Error err = decompressor->put_data(chunk.ptr(), chunk.size()); - if (err == OK) { - chunk.resize(decompressor->get_available_bytes()); - err = decompressor->get_data(chunk.ptrw(), chunk.size()); - } - if (err != OK) { - _defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray()); - return true; + PackedByteArray chunk; + if (decompressor.is_null()) { + // Chunk can be read directly. + chunk = client->read_response_body_chunk(); + downloaded.add(chunk.size()); + } else { + // Chunk is the result of decompression. + PackedByteArray compressed = client->read_response_body_chunk(); + downloaded.add(compressed.size()); + + int pos = 0; + int left = compressed.size(); + while (left) { + int w = 0; + Error err = decompressor->put_partial_data(compressed.ptr() + pos, left, w); + if (err == OK) { + PackedByteArray dc; + dc.resize(decompressor->get_available_bytes()); + err = decompressor->get_data(dc.ptrw(), dc.size()); + chunk.append_array(dc); + } + if (err != OK) { + _defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray()); + return true; + } + // We need this check here because a "zip bomb" could result in a chunk of few kilos decompressing into gigabytes of data. + if (body_size_limit >= 0 && final_body_size.get() + chunk.size() > body_size_limit) { + _defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); + return true; + } + pos += w; + left -= w; } } final_body_size.add(chunk.size()); diff --git a/scene/main/multiplayer_api.cpp b/scene/main/multiplayer_api.cpp index 946929aa89..8b4b98c172 100644 --- a/scene/main/multiplayer_api.cpp +++ b/scene/main/multiplayer_api.cpp @@ -39,7 +39,7 @@ #include "core/os/os.h" #endif -StringName MultiplayerAPI::default_interface = StringName(); +StringName MultiplayerAPI::default_interface; void MultiplayerAPI::set_default_interface(const StringName &p_interface) { ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface)); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 304504a82e..90b996e06b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -349,7 +349,7 @@ void Node::move_child(Node *p_child, int p_index) { } void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) { - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup)."); + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `move_child()` failed. Consider using `move_child.call_deferred(child, index)` instead (or `popup.call_deferred()` if this is from a popup)."); // Specifying one place beyond the end // means the same as moving to the last index @@ -1130,7 +1130,7 @@ void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_i #ifdef DEBUG_ENABLED ERR_FAIL_COND_MSG(p_child->is_ancestor_of(this), vformat("Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'.", p_child->get_name(), get_name(), p_child->get_name(), get_name())); #endif - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead."); + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_child()` failed. Consider using `add_child.call_deferred(child)` instead."); _validate_child_name(p_child, p_force_readable_name); _add_child_nocheck(p_child, p_child->data.name); @@ -1150,7 +1150,7 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { ERR_FAIL_NULL(p_sibling); ERR_FAIL_NULL(data.parent); ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself! - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_sibling() failed. Consider using call_deferred(\"add_sibling\", sibling) instead."); + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_sibling()` failed. Consider using `add_sibling.call_deferred(sibling)` instead."); InternalMode internal = INTERNAL_MODE_DISABLED; if (_is_internal_front()) { // The sibling will have the same internal status. @@ -1165,7 +1165,7 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { void Node::remove_child(Node *p_child) { ERR_FAIL_NULL(p_child); - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, remove_node() failed. Consider using call_deferred(\"remove_child\", child) instead."); + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `remove_child()` failed. Consider using `remove_child.call_deferred(child)` instead."); int child_count = data.children.size(); Node **children = data.children.ptrw(); @@ -1759,11 +1759,11 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) { } void Node::remove_from_group(const StringName &p_identifier) { - ERR_FAIL_COND(!data.grouped.has(p_identifier)); - HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier); - ERR_FAIL_COND(!E); + if (!E) { + return; + } if (data.tree) { data.tree->remove_from_group(E->key, this); @@ -2060,7 +2060,7 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c nip->set_instance_path(ip->get_instance_path()); node = nip; - } else if ((p_flags & DUPLICATE_USE_INSTANCING) && !get_scene_file_path().is_empty()) { + } else if ((p_flags & DUPLICATE_USE_INSTANTIATION) && !get_scene_file_path().is_empty()) { 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; @@ -2246,7 +2246,7 @@ Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) con } Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { - Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap); + Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANTIATION | DUPLICATE_FROM_EDITOR, &r_duplimap); // This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated. if (!p_resource_remap.is_empty()) { @@ -2570,10 +2570,6 @@ static void _Node_debug_sn(Object *p_obj) { } #endif // DEBUG_ENABLED -void Node::_print_orphan_nodes() { - print_orphan_nodes(); -} - void Node::print_orphan_nodes() { #ifdef DEBUG_ENABLED ObjectDB::debug_objects(_Node_debug_sn); @@ -2581,10 +2577,14 @@ void Node::print_orphan_nodes() { } void Node::queue_free() { + // There are users which instantiate multiple scene trees for their games. + // Use the node's own tree to handle its deletion when relevant. if (is_inside_tree()) { get_tree()->queue_delete(this); } else { - SceneTree::get_singleton()->queue_delete(this); + SceneTree *tree = SceneTree::get_singleton(); + ERR_FAIL_NULL_MSG(tree, "Can't queue free a node when no SceneTree is available."); + tree->queue_delete(this); } } @@ -2652,7 +2652,7 @@ PackedStringArray Node::get_configuration_warnings() const { String Node::get_configuration_warnings_as_string() const { PackedStringArray warnings = get_configuration_warnings(); - String all_warnings = String(); + String all_warnings; for (int i = 0; i < warnings.size(); i++) { if (i > 0) { all_warnings += "\n\n"; @@ -2741,6 +2741,7 @@ void Node::_bind_methods() { GLOBAL_DEF("editor/node_naming/name_casing", NAME_CASING_PASCAL_CASE); ProjectSettings::get_singleton()->set_custom_property_info("editor/node_naming/name_casing", PropertyInfo(Variant::INT, "editor/node_naming/name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case")); + ClassDB::bind_static_method("Node", D_METHOD("print_orphan_nodes"), &Node::print_orphan_nodes); ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "force_readable_name"), &Node::add_sibling, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name); @@ -2798,7 +2799,6 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Node::set_process_mode); ClassDB::bind_method(D_METHOD("get_process_mode"), &Node::get_process_mode); ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process); - ClassDB::bind_method(D_METHOD("print_orphan_nodes"), &Node::_print_orphan_nodes); ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded); ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded); @@ -2812,7 +2812,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree); ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween); - ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); + ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANTIATION | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_groups"), &Node::replace_by, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_scene_instance_load_placeholder", "load_placeholder"), &Node::set_scene_instance_load_placeholder); @@ -2918,7 +2918,7 @@ void Node::_bind_methods() { BIND_ENUM_CONSTANT(DUPLICATE_SIGNALS); BIND_ENUM_CONSTANT(DUPLICATE_GROUPS); BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS); - BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING); + BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANTIATION); BIND_ENUM_CONSTANT(INTERNAL_MODE_DISABLED); BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT); diff --git a/scene/main/node.h b/scene/main/node.h index 4a3ec253b1..e07fb003d4 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -57,7 +57,7 @@ public: DUPLICATE_SIGNALS = 1, DUPLICATE_GROUPS = 2, DUPLICATE_SCRIPTS = 4, - DUPLICATE_USE_INSTANCING = 8, + DUPLICATE_USE_INSTANTIATION = 8, #ifdef TOOLS_ENABLED DUPLICATE_FROM_EDITOR = 16, #endif @@ -173,7 +173,6 @@ private: void _propagate_ready(); void _propagate_exit_tree(); void _propagate_after_exit_tree(); - void _print_orphan_nodes(); void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification); void _propagate_groups_dirty(); Array _get_node_and_resource(const NodePath &p_path); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 41eefe0f85..ceb5b76ff2 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1447,7 +1447,7 @@ SceneTree::SceneTree() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/vrs/texture", PropertyInfo(Variant::STRING, "rendering/vrs/texture", - PROPERTY_HINT_FILE, "*.png")); + PROPERTY_HINT_FILE, "*.bmp,*.png,*.tga,*.webp")); if (vrs_mode == 1 && !vrs_texture_path.is_empty()) { Ref<Image> vrs_image; vrs_image.instantiate(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index f8b0a66a71..7430a0a835 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -362,6 +362,7 @@ void Viewport::_notification(int p_what) { current_canvas = find_world_2d()->get_canvas(); RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas); + RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, current_canvas, canvas_transform); RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask); _update_audio_listener_2d(); #ifndef _3D_DISABLED @@ -491,6 +492,7 @@ void Viewport::_notification(int p_what) { } break; case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { + _gui_cancel_tooltip(); _drop_physics_mouseover(); if (gui.mouse_focus && !gui.forced_mouse_focus) { _drop_mouse_focus(); @@ -1561,44 +1563,15 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { set_input_as_handled(); } - if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) { + if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) { // Alternate drop use (when using force_drag(), as proposed by #5342). - gui.drag_successful = false; - if (gui.mouse_focus) { - gui.drag_successful = _gui_drop(gui.mouse_focus, pos, false); - } - - gui.drag_data = Variant(); - gui.dragging = false; - - Control *drag_preview = _gui_get_drag_preview(); - if (drag_preview) { - memdelete(drag_preview); - gui.drag_preview_id = ObjectID(); - } - _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); - get_base_window()->update_mouse_cursor_shape(); + _perform_drop(gui.mouse_focus, pos); } _gui_cancel_tooltip(); } else { - if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) { - gui.drag_successful = false; - if (gui.drag_mouse_over) { - gui.drag_successful = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false); - } - - Control *drag_preview = _gui_get_drag_preview(); - if (drag_preview) { - memdelete(drag_preview); - gui.drag_preview_id = ObjectID(); - } - - gui.drag_data = Variant(); - gui.dragging = false; - gui.drag_mouse_over = nullptr; - _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); - get_base_window()->update_mouse_cursor_shape(); + if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) { + _perform_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos); } gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask. @@ -1652,7 +1625,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *control = Object::cast_to<Control>(ci); if (control) { gui.dragging = true; - gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos) - gui.drag_accum); + gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum)); if (gui.drag_data.get_type() != Variant::NIL) { gui.mouse_focus = nullptr; gui.forced_mouse_focus = false; @@ -1682,7 +1655,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } gui.drag_attempted = true; - if (gui.drag_data.get_type() != Variant::NIL) { + if (gui.dragging) { _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN); } } @@ -1698,6 +1671,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { _gui_cancel_tooltip(); if (over) { + if (!gui.mouse_over) { + _drop_physics_mouseover(); + } _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER); gui.mouse_over = over; } @@ -1795,7 +1771,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } - if (gui.drag_data.get_type() != Variant::NIL) { + if (gui.dragging) { // Handle drag & drop. Control *drag_preview = _gui_get_drag_preview(); @@ -1997,6 +1973,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } if (mm.is_null() && mb.is_null() && p_event->is_action_type()) { + if (gui.dragging && p_event->is_action_pressed("ui_cancel") && Input::get_singleton()->is_action_just_pressed("ui_cancel")) { + _perform_drop(); + set_input_as_handled(); + return; + } + if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) { gui.key_focus->release_focus(); } @@ -2078,6 +2060,27 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } +void Viewport::_perform_drop(Control *p_control, Point2 p_pos) { + // Without any arguments, simply cancel Drag and Drop. + if (p_control) { + gui.drag_successful = _gui_drop(p_control, p_pos, false); + } else { + gui.drag_successful = false; + } + + Control *drag_preview = _gui_get_drag_preview(); + if (drag_preview) { + memdelete(drag_preview); + gui.drag_preview_id = ObjectID(); + } + + gui.drag_data = Variant(); + gui.dragging = false; + gui.drag_mouse_over = nullptr; + _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); + get_base_window()->update_mouse_cursor_shape(); +} + void Viewport::_gui_cleanup_internal_state(Ref<InputEvent> p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -2094,7 +2097,7 @@ List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) { return gui.roots.push_back(p_control); } -void Viewport::_gui_set_root_order_dirty() { +void Viewport::gui_set_root_order_dirty() { gui.roots_order_dirty = true; } @@ -2662,6 +2665,11 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { } else { gui.subwindow_resize_mode = _sub_window_get_resize_margin(sw.window, mb->get_position()); if (gui.subwindow_resize_mode != SUB_WINDOW_RESIZE_DISABLED) { + if (gui.subwindow_focused != sw.window) { + // Refocus. + _sub_window_grab_focus(sw.window); + } + gui.subwindow_resize_from_rect = r; gui.subwindow_drag_from = mb->get_position(); gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE; @@ -3041,8 +3049,6 @@ bool Viewport::gui_is_drag_successful() const { } void Viewport::set_input_as_handled() { - _drop_physics_mouseover(); - if (!handle_input_locally) { ERR_FAIL_COND(!is_inside_tree()); Viewport *vp = this; @@ -4161,7 +4167,7 @@ DisplayServer::WindowID SubViewport::get_window_id() const { } Transform2D SubViewport::_stretch_transform() { - Transform2D transform = Transform2D(); + Transform2D transform; Size2i view_size_2d_override = _get_size_2d_override(); if (size_2d_override_stretch && view_size_2d_override.width > 0 && view_size_2d_override.height > 0) { Size2 scale = Size2(_get_size()) / Size2(view_size_2d_override); @@ -4172,7 +4178,7 @@ Transform2D SubViewport::_stretch_transform() { } Transform2D SubViewport::get_screen_transform() const { - Transform2D container_transform = Transform2D(); + Transform2D container_transform; SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent()); if (c) { if (c->is_stretch_enabled()) { @@ -4231,6 +4237,8 @@ void SubViewport::_bind_methods() { BIND_ENUM_CONSTANT(UPDATE_ALWAYS); } -SubViewport::SubViewport() {} +SubViewport::SubViewport() { + RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height); +} SubViewport::~SubViewport() {} diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 8911aea335..5659ee4000 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -405,6 +405,7 @@ private: Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform); void _gui_input_event(Ref<InputEvent> p_event); + void _perform_drop(Control *p_control = nullptr, Point2 p_pos = Point2()); void _gui_cleanup_internal_state(Ref<InputEvent> p_event); _FORCE_INLINE_ Transform2D _get_input_pre_xform() const; @@ -455,8 +456,6 @@ private: void _update_canvas_items(Node *p_node); - void _gui_set_root_order_dirty(); - friend class Window; void _sub_window_update_order(); @@ -513,6 +512,8 @@ public: Transform2D get_final_transform() const; + void gui_set_root_order_dirty(); + void set_transparent_background(bool p_enable); bool has_transparent_background() const; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index ebb11ecee8..aff2c594d9 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1633,7 +1633,7 @@ void Window::_validate_property(PropertyInfo &p_property) const { } Transform2D Window::get_screen_transform() const { - Transform2D embedder_transform = Transform2D(); + Transform2D embedder_transform; if (_get_embedder()) { embedder_transform.translate_local(get_position()); embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform; diff --git a/scene/property_utils.h b/scene/property_utils.h index 5ada35fdd7..5b0200b491 100644 --- a/scene/property_utils.h +++ b/scene/property_utils.h @@ -42,7 +42,7 @@ public: // Gets the instance/inheritance states of this node, in order of precedence, // that is, from the topmost (the most able to override values) to the lowermost - // (Note that in nested instancing the one with the greatest precedence is the furthest + // (Note that in nested instantiation, the one with the greatest precedence is the furthest // in the tree, since every owner found while traversing towards the root gets a chance // to override property values.) static Vector<SceneState::PackState> get_node_states_stack(const Node *p_node, const Node *p_owner = nullptr, bool *r_instantiated_by_owner = nullptr); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 0f55c7b3b8..ee45a8ea6f 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -960,6 +960,7 @@ void register_scene_types() { ClassDB::add_compatibility_class("Navigation3D", "Node3D"); ClassDB::add_compatibility_class("Navigation2D", "Node2D"); ClassDB::add_compatibility_class("OpenSimplexNoise", "FastNoiseLite"); + ClassDB::add_compatibility_class("ProximityGroup", "Node3D"); ClassDB::add_compatibility_class("ToolButton", "Button"); ClassDB::add_compatibility_class("YSort", "Node2D"); // Portal and room occlusion was replaced by raster occlusion (OccluderInstance3D node). diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index dfd9c6eb2f..da73a479ce 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -888,7 +888,6 @@ int Animation::add_track(TrackType p_type, int p_at_pos) { } } emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); return p_at_pos; } @@ -951,7 +950,6 @@ void Animation::remove_track(int p_track) { memdelete(t); tracks.remove_at(p_track); emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } int Animation::get_track_count() const { @@ -967,7 +965,6 @@ void Animation::track_set_path(int p_track, const NodePath &p_path) { ERR_FAIL_INDEX(p_track, tracks.size()); tracks[p_track]->path = p_path; emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } NodePath Animation::track_get_path(int p_track) const { @@ -2467,7 +2464,6 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol ERR_FAIL_COND_V(idx == -2, T()); - bool result = true; int next = 0; real_t c = 0.0; // prepare for all cases of interpolation @@ -2599,10 +2595,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol } if (p_ok) { - *p_ok = result; - } - if (!result) { - return T(); + *p_ok = true; } real_t tr = p_keys[idx].transition; @@ -3834,7 +3827,6 @@ void Animation::track_move_up(int p_track) { } emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } void Animation::track_move_down(int p_track) { @@ -3843,7 +3835,6 @@ void Animation::track_move_down(int p_track) { } emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } void Animation::track_move_to(int p_track, int p_to_index) { @@ -3859,7 +3850,6 @@ void Animation::track_move_to(int p_track, int p_to_index) { tracks.insert(p_to_index > p_track ? p_to_index - 1 : p_to_index, track); emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } void Animation::track_swap(int p_track, int p_with_track) { @@ -3871,7 +3861,6 @@ void Animation::track_swap(int p_track, int p_with_track) { SWAP(tracks.write[p_track], tracks.write[p_with_track]); emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } void Animation::set_step(real_t p_step) { @@ -4001,8 +3990,6 @@ void Animation::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001,suffix:s"), "set_step", "get_step"); - ADD_SIGNAL(MethodInfo("tracks_changed")); - BIND_ENUM_CONSTANT(TYPE_VALUE); BIND_ENUM_CONSTANT(TYPE_POSITION_3D); BIND_ENUM_CONSTANT(TYPE_ROTATION_3D); @@ -4041,7 +4028,6 @@ void Animation::clear() { compression.pages.clear(); compression.fps = 120; emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } bool Animation::_float_track_optimize_key(const TKey<float> t0, const TKey<float> t1, const TKey<float> t2, real_t p_allowed_velocity_err, real_t p_allowed_precision_error) { @@ -4964,7 +4950,7 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol if (rollback || best_frame == FRAME_MAX) { // Commit the page if had to rollback or if no track was found - print_animc("\tCommiting page.."); + print_animc("\tCommiting page..."); // The end frame for the page depends entirely on whether its valid or // no more keys were found. @@ -5851,18 +5837,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float return dst; } case Variant::PACKED_INT32_ARRAY: { - const Vector<int32_t> *arr_a = Object::cast_to<Vector<int32_t>>(a); - const Vector<int32_t> *arr_b = Object::cast_to<Vector<int32_t>>(b); - int32_t sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { + const Vector<int32_t> arr_a = a; + const Vector<int32_t> arr_b = b; + int32_t sz = arr_a.size(); + if (sz == 0 || arr_b.size() != sz) { return a; } else { Vector<int32_t> v; v.resize(sz); { int32_t *vw = v.ptrw(); - const int32_t *ar = arr_a->ptr(); - const int32_t *br = arr_b->ptr(); + const int32_t *ar = arr_a.ptr(); + const int32_t *br = arr_b.ptr(); Variant va; for (int32_t i = 0; i < sz; i++) { @@ -5874,18 +5860,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } case Variant::PACKED_INT64_ARRAY: { - const Vector<int64_t> *arr_a = Object::cast_to<Vector<int64_t>>(a); - const Vector<int64_t> *arr_b = Object::cast_to<Vector<int64_t>>(b); - int64_t sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { + const Vector<int64_t> arr_a = a; + const Vector<int64_t> arr_b = b; + int64_t sz = arr_a.size(); + if (sz == 0 || arr_b.size() != sz) { return a; } else { Vector<int64_t> v; v.resize(sz); { int64_t *vw = v.ptrw(); - const int64_t *ar = arr_a->ptr(); - const int64_t *br = arr_b->ptr(); + const int64_t *ar = arr_a.ptr(); + const int64_t *br = arr_b.ptr(); Variant va; for (int64_t i = 0; i < sz; i++) { @@ -5897,18 +5883,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } case Variant::PACKED_FLOAT32_ARRAY: { - const Vector<float> *arr_a = Object::cast_to<Vector<float>>(a); - const Vector<float> *arr_b = Object::cast_to<Vector<float>>(b); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { + const Vector<float> arr_a = a; + const Vector<float> arr_b = b; + int sz = arr_a.size(); + if (sz == 0 || arr_b.size() != sz) { return a; } else { Vector<float> v; v.resize(sz); { float *vw = v.ptrw(); - const float *ar = arr_a->ptr(); - const float *br = arr_b->ptr(); + const float *ar = arr_a.ptr(); + const float *br = arr_b.ptr(); Variant va; for (int i = 0; i < sz; i++) { @@ -5920,18 +5906,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } case Variant::PACKED_FLOAT64_ARRAY: { - const Vector<double> *arr_a = Object::cast_to<Vector<double>>(a); - const Vector<double> *arr_b = Object::cast_to<Vector<double>>(b); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { + const Vector<double> arr_a = a; + const Vector<double> arr_b = b; + int sz = arr_a.size(); + if (sz == 0 || arr_b.size() != sz) { return a; } else { Vector<double> v; v.resize(sz); { double *vw = v.ptrw(); - const double *ar = arr_a->ptr(); - const double *br = arr_b->ptr(); + const double *ar = arr_a.ptr(); + const double *br = arr_b.ptr(); Variant va; for (int i = 0; i < sz; i++) { @@ -5943,18 +5929,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } case Variant::PACKED_VECTOR2_ARRAY: { - const Vector<Vector2> *arr_a = Object::cast_to<Vector<Vector2>>(a); - const Vector<Vector2> *arr_b = Object::cast_to<Vector<Vector2>>(b); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { + const Vector<Vector2> arr_a = a; + const Vector<Vector2> arr_b = b; + int sz = arr_a.size(); + if (sz == 0 || arr_b.size() != sz) { return a; } else { Vector<Vector2> v; v.resize(sz); { Vector2 *vw = v.ptrw(); - const Vector2 *ar = arr_a->ptr(); - const Vector2 *br = arr_b->ptr(); + const Vector2 *ar = arr_a.ptr(); + const Vector2 *br = arr_b.ptr(); for (int i = 0; i < sz; i++) { vw[i] = ar[i].lerp(br[i], c); @@ -5964,18 +5950,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } case Variant::PACKED_VECTOR3_ARRAY: { - const Vector<Vector3> *arr_a = Object::cast_to<Vector<Vector3>>(a); - const Vector<Vector3> *arr_b = Object::cast_to<Vector<Vector3>>(b); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { + const Vector<Vector3> arr_a = a; + const Vector<Vector3> arr_b = b; + int sz = arr_a.size(); + if (sz == 0 || arr_b.size() != sz) { return a; } else { Vector<Vector3> v; v.resize(sz); { Vector3 *vw = v.ptrw(); - const Vector3 *ar = arr_a->ptr(); - const Vector3 *br = arr_b->ptr(); + const Vector3 *ar = arr_a.ptr(); + const Vector3 *br = arr_b.ptr(); for (int i = 0; i < sz; i++) { vw[i] = ar[i].lerp(br[i], c); @@ -5985,18 +5971,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } case Variant::PACKED_COLOR_ARRAY: { - const Vector<Color> *arr_a = Object::cast_to<Vector<Color>>(a); - const Vector<Color> *arr_b = Object::cast_to<Vector<Color>>(b); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { + const Vector<Color> arr_a = a; + const Vector<Color> arr_b = b; + int sz = arr_a.size(); + if (sz == 0 || arr_b.size() != sz) { return a; } else { Vector<Color> v; v.resize(sz); { Color *vw = v.ptrw(); - const Color *ar = arr_a->ptr(); - const Color *br = arr_b->ptr(); + const Color *ar = arr_a.ptr(); + const Color *br = arr_b.ptr(); for (int i = 0; i < sz; i++) { vw[i] = ar[i].lerp(br[i], c); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 1b06e09bb8..0df61871d8 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -169,7 +169,7 @@ Dictionary BitMap::_get_data() const { return d; } -Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { +Vector<Vector<Vector2>> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { int stepx = 0; int stepy = 0; int prevx = 0; @@ -179,9 +179,17 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta int curx = startx; int cury = starty; unsigned int count = 0; - HashSet<Point2i> case9s; - HashSet<Point2i> case6s; + + HashMap<Point2i, int> cross_map; + Vector<Vector2> _points; + int points_size = 0; + + Vector<Vector<Vector2>> ret; + + // Add starting entry at start of return. + ret.resize(1); + do { int sv = 0; { // Square value @@ -202,7 +210,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta sv += (p_rect.has_point(bl) && get_bitv(bl)) ? 4 : 0; Point2i br = Point2i(curx, cury); sv += (p_rect.has_point(br) && get_bitv(br)) ? 8 : 0; - ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>()); + ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector<Vector2>>()); } switch (sv) { @@ -266,70 +274,95 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta stepy = 0; break; case 9: - /* + /* Going DOWN if coming from the LEFT, otherwise go UP. + 9 +---+---+ | 1 | | +---+---+ | | 8 | +---+---+ - this should normally go UP, but if we already been here, we go down */ - if (case9s.has(Point2i(curx, cury))) { - //found, so we go down, and delete from case9s; + + if (prevx == 1) { stepx = 0; stepy = 1; - case9s.erase(Point2i(curx, cury)); } else { - //not found, we go up, and add to case9s; stepx = 0; stepy = -1; - case9s.insert(Point2i(curx, cury)); } break; case 6: - /* + /* Going RIGHT if coming from BELOW, otherwise go LEFT. 6 +---+---+ | | 2 | +---+---+ | 4 | | +---+---+ - this normally go RIGHT, but if it's coming from RIGHT, it should go LEFT */ - if (case6s.has(Point2i(curx, cury))) { - //found, so we go left, and delete from case6s; - stepx = -1; + + if (prevy == -1) { + stepx = 1; stepy = 0; - case6s.erase(Point2i(curx, cury)); } else { - //not found, we go right, and add to case6s; - stepx = 1; + stepx = -1; stepy = 0; - case6s.insert(Point2i(curx, cury)); } break; default: ERR_PRINT("this shouldn't happen."); } + + // Handle crossing points. + if (sv == 6 || sv == 9) { + const Point2i cur_pos(curx, cury); + + // Find if this point has occured before. + if (HashMap<Point2i, int>::Iterator found = cross_map.find(cur_pos)) { + // Add points after the previous crossing to the result. + ret.push_back(_points.slice(found->value + 1, points_size)); + + // Remove points after crossing point. + points_size = found->value + 1; + + // Erase trailing map elements. + while (cross_map.last() != found) { + cross_map.remove(cross_map.last()); + } + + cross_map.erase(cur_pos); + } else { + // Add crossing point to map. + cross_map.insert(cur_pos, points_size - 1); + } + } + // Small optimization: // If the previous direction is same as the current direction, // then we should modify the last vector to current. curx += stepx; cury += stepy; if (stepx == prevx && stepy == prevy) { - _points.write[_points.size() - 1].x = (float)(curx - p_rect.position.x); - _points.write[_points.size() - 1].y = (float)(cury + p_rect.position.y); + _points.set(points_size - 1, Vector2(curx, cury) - p_rect.position); } else { - _points.push_back(Vector2((float)(curx - p_rect.position.x), (float)(cury + p_rect.position.y))); + _points.resize(MAX(points_size + 1, _points.size())); + _points.set(points_size, Vector2(curx, cury) - p_rect.position); + points_size++; } count++; prevx = stepx; prevy = stepy; - ERR_FAIL_COND_V((int)count > width * height, _points); + ERR_FAIL_COND_V((int)count > width * height, Vector<Vector<Vector2>>()); } while (curx != startx || cury != starty); - return _points; + + // Add remaining points to result. + _points.resize(points_size); + + ret.set(0, _points); + + return ret; } static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) { @@ -442,7 +475,7 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ for (int j = next_j; j <= pos.y + 1; j++) { if (popped) { // The next loop over j must start normally. - next_j = pos.y; + next_j = pos.y - 1; popped = false; // Skip because an iteration was already executed with current counter values. continue; @@ -486,13 +519,10 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ } } } while (reenter || popped); - - print_verbose("BitMap: Max stack size: " + itos(stack.size())); } Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon) const { Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); - print_verbose("BitMap: Rect: " + r); Point2i from; Ref<BitMap> fill; @@ -505,17 +535,16 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, fl if (!fill->get_bit(j, i) && get_bit(j, i)) { fill_bits(this, fill, Point2i(j, i), r); - Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); - print_verbose("BitMap: Pre reduce: " + itos(polygon.size())); - polygon = reduce(polygon, r, p_epsilon); - print_verbose("BitMap: Post reduce: " + itos(polygon.size())); + for (Vector<Vector2> polygon : _march_square(r, Point2i(j, i))) { + polygon = reduce(polygon, r, p_epsilon); - if (polygon.size() < 3) { - print_verbose("Invalid polygon, skipped"); - continue; - } + if (polygon.size() < 3) { + print_verbose("Invalid polygon, skipped"); + continue; + } - polygons.push_back(polygon); + polygons.push_back(polygon); + } } } } diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index 291ed8c4d0..0ec5772fd1 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -46,7 +46,7 @@ class BitMap : public Resource { int width = 0; int height = 0; - Vector<Vector2> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; + Vector<Vector<Vector2>> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; TypedArray<PackedVector2Array> _opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const; diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp index dfaf82f36a..5698e61004 100644 --- a/scene/resources/bone_map.cpp +++ b/scene/resources/bone_map.cpp @@ -93,7 +93,7 @@ void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const Strin } StringName BoneMap::find_profile_bone_name(StringName p_skeleton_bone_name) const { - StringName profile_bone_name = StringName(); + StringName profile_bone_name; HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); while (E) { if (E->value == p_skeleton_bone_name) { diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index 3c322f32b6..8e4876e01f 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -120,7 +120,7 @@ void CameraAttributes::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_exposure_scale", "exposure_grey"), &CameraAttributes::set_auto_exposure_scale); ClassDB::bind_method(D_METHOD("get_auto_exposure_scale"), &CameraAttributes::get_auto_exposure_scale); - ADD_GROUP("Exposure", "exposure"); + ADD_GROUP("Exposure", "exposure_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_sensitivity", PROPERTY_HINT_RANGE, "0.1,32000.0,0.1,suffix:ISO"), "set_exposure_sensitivity", "get_exposure_sensitivity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,2048.0,0.001"), "set_exposure_multiplier", "get_exposure_multiplier"); @@ -472,7 +472,7 @@ void CameraAttributesPhysical::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); - ADD_GROUP("Exposure", "exposure"); + ADD_GROUP("Exposure", "exposure_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_aperture", PROPERTY_HINT_RANGE, "0.5,64.0,0.01,exp,suffix:f-stop"), "set_aperture", "get_aperture"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_shutter_speed", PROPERTY_HINT_RANGE, "0.1,8000.0,0.001,suffix:1/s"), "set_shutter_speed", "get_shutter_speed"); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index eda9af9dde..9289c5da4a 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -31,6 +31,7 @@ #include "curve.h" #include "core/core_string_names.h" +#include "core/math/math_funcs.h" const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed"; @@ -1402,6 +1403,22 @@ void Curve3D::_bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, re } } +void Curve3D::_bake_segment3d_even_length(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_length) const { + Vector3 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin); + Vector3 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end); + + size_t length = beg.distance_to(end); + + if (length > p_length && p_depth < p_max_depth) { + real_t mp = (p_begin + p_end) * 0.5; + Vector3 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp); + r_bake[mp] = mid; + + _bake_segment3d(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); + _bake_segment3d(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); + } +} + void Curve3D::_bake() const { if (!baked_cache_dirty) { return; @@ -1413,8 +1430,10 @@ void Curve3D::_bake() const { if (points.size() == 0) { baked_point_cache.clear(); baked_tilt_cache.clear(); - baked_up_vector_cache.clear(); baked_dist_cache.clear(); + + baked_forward_vector_cache.clear(); + baked_up_vector_cache.clear(); return; } @@ -1425,10 +1444,12 @@ void Curve3D::_bake() const { baked_tilt_cache.set(0, points[0].tilt); baked_dist_cache.resize(1); baked_dist_cache.set(0, 0.0); + baked_forward_vector_cache.resize(1); + baked_forward_vector_cache.set(0, Vector3(0.0, 0.0, 1.0)); if (up_vector_enabled) { baked_up_vector_cache.resize(1); - baked_up_vector_cache.set(0, Vector3(0, 1, 0)); + baked_up_vector_cache.set(0, Vector3(0.0, 1.0, 0.0)); } else { baked_up_vector_cache.clear(); } @@ -1436,136 +1457,135 @@ void Curve3D::_bake() const { return; } - Vector3 position = points[0].position; - real_t dist = 0.0; - List<Plane> pointlist; - List<real_t> distlist; + // Step 1: Tesselate curve to (almost) even length segments + { + Vector<RBMap<real_t, Vector3>> midpoints = _tessellate_even_length(10, bake_interval); - // Start always from origin. - pointlist.push_back(Plane(position, points[0].tilt)); - distlist.push_back(0.0); - - for (int i = 0; i < points.size() - 1; i++) { - real_t step = 0.1; // at least 10 substeps ought to be enough? - real_t p = 0.0; + int pc = 1; + for (int i = 0; i < points.size() - 1; i++) { + pc++; + pc += midpoints[i].size(); + } - while (p < 1.0) { - real_t np = p + step; - if (np > 1.0) { - np = 1.0; + baked_point_cache.resize(pc); + baked_tilt_cache.resize(pc); + baked_dist_cache.resize(pc); + baked_forward_vector_cache.resize(pc); + + Vector3 *bpw = baked_point_cache.ptrw(); + real_t *btw = baked_tilt_cache.ptrw(); + Vector3 *bfw = baked_forward_vector_cache.ptrw(); + + // Collect positions and sample tilts and tangents for each baked points. + bpw[0] = points[0].position; + bfw[0] = points[0].position.bezier_derivative(points[0].position + points[0].out, points[1].position + points[1].in, points[1].position, 0.0).normalized(); + btw[0] = points[0].tilt; + int pidx = 0; + + for (int i = 0; i < points.size() - 1; i++) { + for (const KeyValue<real_t, Vector3> &E : midpoints[i]) { + pidx++; + bpw[pidx] = E.value; + bfw[pidx] = points[i].position.bezier_derivative(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key).normalized(); + btw[pidx] = Math::lerp(points[i].tilt, points[i + 1].tilt, E.key); } - Vector3 npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, np); - real_t d = position.distance_to(npp); - - if (d > bake_interval) { - // OK! between P and NP there _has_ to be Something, let's go searching! - - int iterations = 10; //lots of detail! - - real_t low = p; - real_t hi = np; - real_t mid = low + (hi - low) * 0.5; - - for (int j = 0; j < iterations; j++) { - npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, mid); - d = position.distance_to(npp); - - if (bake_interval < d) { - hi = mid; - } else { - low = mid; - } - mid = low + (hi - low) * 0.5; - } - - position = npp; - p = mid; - Plane post; - post.normal = position; - post.d = Math::lerp(points[i].tilt, points[i + 1].tilt, mid); - dist += d; - - pointlist.push_back(post); - distlist.push_back(dist); - } else { - p = np; - } + pidx++; + bpw[pidx] = points[i + 1].position; + bfw[pidx] = points[i].position.bezier_derivative(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0).normalized(); + btw[pidx] = points[i + 1].tilt; } - Vector3 npp = points[i + 1].position; - real_t d = position.distance_to(npp); - - position = npp; - Plane post; - post.normal = position; - post.d = points[i + 1].tilt; - - dist += d; - - pointlist.push_back(post); - distlist.push_back(dist); + // Recalculate the baked distances. + real_t *bdw = baked_dist_cache.ptrw(); + bdw[0] = 0.0; + for (int i = 0; i < pc - 1; i++) { + bdw[i + 1] = bdw[i] + bpw[i].distance_to(bpw[i + 1]); + } + baked_max_ofs = bdw[pc - 1]; } - baked_max_ofs = dist; - - baked_point_cache.resize(pointlist.size()); - Vector3 *w = baked_point_cache.ptrw(); - int idx = 0; + if (!up_vector_enabled) { + baked_up_vector_cache.resize(0); + return; + } - baked_tilt_cache.resize(pointlist.size()); - real_t *wt = baked_tilt_cache.ptrw(); + // Step 2: Calculate the up vectors and the whole local reference frame + // + // See Dougan, Carl. "The parallel transport frame." Game Programming Gems 2 (2001): 215-219. + // for an example discussing about why not the Frenet frame. + { + int point_count = baked_point_cache.size(); - baked_up_vector_cache.resize(up_vector_enabled ? pointlist.size() : 0); - Vector3 *up_write = baked_up_vector_cache.ptrw(); + baked_up_vector_cache.resize(point_count); + Vector3 *up_write = baked_up_vector_cache.ptrw(); - baked_dist_cache.resize(pointlist.size()); - real_t *wd = baked_dist_cache.ptrw(); + const Vector3 *forward_ptr = baked_forward_vector_cache.ptr(); + const Vector3 *points_ptr = baked_point_cache.ptr(); - Vector3 sideways; - Vector3 up; - Vector3 forward; + Basis frame; // X-right, Y-up, Z-forward. + Basis frame_prev; - Vector3 prev_sideways = Vector3(1, 0, 0); - Vector3 prev_up = Vector3(0, 1, 0); - Vector3 prev_forward = Vector3(0, 0, 1); + // Set the initial frame based on Y-up rule. + { + Vector3 forward = forward_ptr[0]; - for (const Plane &E : pointlist) { - w[idx] = E.normal; - wt[idx] = E.d; - wd[idx] = distlist[idx]; + if (abs(forward.dot(Vector3(0, 1, 0))) > 1.0 - UNIT_EPSILON) { + frame_prev = Basis::looking_at(-forward, Vector3(1, 0, 0)); + } else { + frame_prev = Basis::looking_at(-forward, Vector3(0, 1, 0)); + } - if (!up_vector_enabled) { - idx++; - continue; + up_write[0] = frame_prev.get_column(1); } - forward = idx > 0 ? (w[idx] - w[idx - 1]).normalized() : prev_forward; + // Calculate the Parallel Transport Frame. + for (int idx = 1; idx < point_count; idx++) { + Vector3 forward = forward_ptr[idx]; - real_t y_dot = prev_up.dot(forward); + Basis rotate; + rotate.rotate_to_align(frame_prev.get_column(2), forward); + frame = rotate * frame_prev; + frame.orthonormalize(); // guard against float error accumulation - if (y_dot > (1.0f - CMP_EPSILON)) { - sideways = prev_sideways; - up = -prev_forward; - } else if (y_dot < -(1.0f - CMP_EPSILON)) { - sideways = prev_sideways; - up = prev_forward; - } else { - sideways = prev_up.cross(forward).normalized(); - up = forward.cross(sideways).normalized(); + up_write[idx] = frame.get_column(1); + frame_prev = frame; } - if (idx == 1) { - up_write[0] = up; + bool is_loop = true; + // Loop smoothing only applies when the curve is a loop, which means two ends meet, and share forward directions. + { + if (!points_ptr[0].is_equal_approx(points_ptr[point_count - 1])) { + is_loop = false; + } + + real_t dot = forward_ptr[0].dot(forward_ptr[point_count - 1]); + if (dot < 1.0 - UNIT_EPSILON) { // Alignment should not be too tight, or it dosen't work for coarse bake interval. + is_loop = false; + } } - up_write[idx] = up; + // Twist up vectors, so that they align at two ends of the curve. + if (is_loop) { + const Vector3 up_start = up_write[0]; + const Vector3 up_end = up_write[point_count - 1]; + + real_t sign = SIGN(up_end.cross(up_start).dot(forward_ptr[0])); + real_t full_angle = Quaternion(up_end, up_start).get_angle(); - prev_sideways = sideways; - prev_up = up; - prev_forward = forward; + if (abs(full_angle) < CMP_EPSILON) { + return; + } else { + const real_t *dists = baked_dist_cache.ptr(); + for (int idx = 1; idx < point_count; idx++) { + const real_t frac = dists[idx] / baked_max_ofs; + const real_t angle = Math::lerp((real_t)0.0, full_angle, frac); + Basis twist(forward_ptr[idx] * sign, angle); - idx++; + up_write[idx] = twist.xform(up_write[idx]); + } + } + } } } @@ -1577,27 +1597,15 @@ real_t Curve3D::get_baked_length() const { return baked_max_ofs; } -Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { - if (baked_cache_dirty) { - _bake(); - } +Curve3D::Interval Curve3D::_find_interval(real_t p_offset) const { + Interval interval = { + -1, + 0.0 + }; + ERR_FAIL_COND_V_MSG(baked_cache_dirty, interval, "Backed cache is dirty"); - // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); - ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D."); - - if (pc == 1) { - return baked_point_cache.get(0); - } - - const Vector3 *r = baked_point_cache.ptr(); - - if (p_offset < 0) { - return r[0]; - } - if (p_offset >= baked_max_ofs) { - return r[pc - 1]; - } + ERR_FAIL_COND_V_MSG(pc < 2, interval, "Less than two points in cache"); int start = 0; int end = pc; @@ -1617,9 +1625,27 @@ Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "Couldn't find baked segment."); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, interval, "Offset out of range."); - real_t frac = (p_offset - offset_begin) / idx_interval; + interval.idx = idx; + if (idx_interval < FLT_EPSILON) { + interval.frac = 0.5; // For a very short interval, 0.5 is a reasonable choice. + ERR_FAIL_V_MSG(interval, "Zero length interval."); + } + + interval.frac = (p_offset - offset_begin) / idx_interval; + return interval; +} + +Vector3 Curve3D::_sample_baked(Interval p_interval, bool p_cubic) const { + // Assuming p_interval is valid. + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_point_cache.size(), Vector3(), "Invalid interval"); + + int idx = p_interval.idx; + real_t frac = p_interval.frac; + + const Vector3 *r = baked_point_cache.ptr(); + int pc = baked_point_cache.size(); if (p_cubic) { Vector3 pre = idx > 0 ? r[idx - 1] : r[idx]; @@ -1630,114 +1656,142 @@ Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { } } -real_t Curve3D::sample_baked_tilt(real_t p_offset) const { - if (baked_cache_dirty) { - _bake(); - } +real_t Curve3D::_sample_baked_tilt(Interval p_interval) const { + // Assuming that p_interval is valid. + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_tilt_cache.size(), 0.0, "Invalid interval"); - // Validate: Curve may not have baked tilts. - int pc = baked_tilt_cache.size(); - ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D."); + int idx = p_interval.idx; + real_t frac = p_interval.frac; - if (pc == 1) { - return baked_tilt_cache.get(0); + const real_t *r = baked_tilt_cache.ptr(); + + return Math::lerp(r[idx], r[idx + 1], frac); +} + +Basis Curve3D::_sample_posture(Interval p_interval, bool p_apply_tilt) const { + // Assuming that p_interval is valid. + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_point_cache.size(), Basis(), "Invalid interval"); + if (up_vector_enabled) { + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_up_vector_cache.size(), Basis(), "Invalid interval"); } - const real_t *r = baked_tilt_cache.ptr(); + int idx = p_interval.idx; + real_t frac = p_interval.frac; - if (p_offset < 0) { - return r[0]; + Vector3 forward_begin = baked_forward_vector_cache[idx]; + Vector3 forward_end = baked_forward_vector_cache[idx + 1]; + + Vector3 up_begin; + Vector3 up_end; + if (up_vector_enabled) { + up_begin = baked_up_vector_cache[idx]; + up_end = baked_up_vector_cache[idx + 1]; + } else { + up_begin = Vector3(0.0, 1.0, 0.0); + up_end = Vector3(0.0, 1.0, 0.0); } - if (p_offset >= baked_max_ofs) { - return r[pc - 1]; + + // Build frames at both ends of the interval, then interpolate. + const Basis frame_begin = Basis::looking_at(-forward_begin, up_begin); + const Basis frame_end = Basis::looking_at(-forward_end, up_end); + const Basis frame = frame_begin.slerp(frame_end, frac).orthonormalized(); + + if (!p_apply_tilt) { + return frame; } - int start = 0; - int end = pc; - int idx = (end + start) / 2; - // Binary search to find baked points. - while (start < idx) { - real_t offset = baked_dist_cache[idx]; - if (p_offset <= offset) { - end = idx; - } else { - start = idx; - } - idx = (end + start) / 2; + // Applying tilt. + const real_t tilt = _sample_baked_tilt(p_interval); + Vector3 forward = frame.get_column(2); + + const Basis twist(forward, tilt); + return twist * frame; +} + +Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { + if (baked_cache_dirty) { + _bake(); } - real_t offset_begin = baked_dist_cache[idx]; - real_t offset_end = baked_dist_cache[idx + 1]; + // Validate: Curve may not have baked points. + int pc = baked_point_cache.size(); + ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D."); - real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "Couldn't find baked segment."); + if (pc == 1) { + return baked_point_cache[0]; + } - real_t frac = (p_offset - offset_begin) / idx_interval; + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. - return Math::lerp(r[idx], r[idx + 1], (real_t)frac); + Curve3D::Interval interval = _find_interval(p_offset); + return _sample_baked(interval, p_cubic); } -Vector3 Curve3D::sample_baked_up_vector(real_t p_offset, bool p_apply_tilt) const { +Transform3D Curve3D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_apply_tilt) const { if (baked_cache_dirty) { _bake(); } - // Validate: Curve may not have baked up vectors. - int count = baked_up_vector_cache.size(); - ERR_FAIL_COND_V_MSG(count == 0, Vector3(0, 1, 0), "No up vectors in Curve3D."); + // Validate: Curve may not have baked points. + const int point_count = baked_point_cache.size(); + ERR_FAIL_COND_V_MSG(point_count == 0, Transform3D(), "No points in Curve3D."); - if (count == 1) { - return baked_up_vector_cache.get(0); + if (point_count == 1) { + Transform3D t; + t.origin = baked_point_cache.get(0); + ERR_FAIL_V_MSG(t, "Only 1 point in Curve3D."); } - const Vector3 *r = baked_up_vector_cache.ptr(); - const Vector3 *rp = baked_point_cache.ptr(); - const real_t *rt = baked_tilt_cache.ptr(); + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. - int start = 0; - int end = count; - int idx = (end + start) / 2; - // Binary search to find baked points. - while (start < idx) { - real_t offset = baked_dist_cache[idx]; - if (p_offset <= offset) { - end = idx; - } else { - start = idx; - } - idx = (end + start) / 2; - } + // 0. Find interval for all sampling steps. + Curve3D::Interval interval = _find_interval(p_offset); + + // 1. Sample position. + Vector3 pos = _sample_baked(interval, p_cubic); + + // 2. Sample rotation frame. + Basis frame = _sample_posture(interval, p_apply_tilt); + + return Transform3D(frame, pos); +} - if (idx == count - 1) { - return p_apply_tilt ? r[idx].rotated((rp[idx] - rp[idx - 1]).normalized(), rt[idx]) : r[idx]; +real_t Curve3D::sample_baked_tilt(real_t p_offset) const { + if (baked_cache_dirty) { + _bake(); } - real_t offset_begin = baked_dist_cache[idx]; - real_t offset_end = baked_dist_cache[idx + 1]; + // Validate: Curve may not have baked tilts. + int pc = baked_tilt_cache.size(); + ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D."); - real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "Couldn't find baked segment."); + if (pc == 1) { + return baked_tilt_cache.get(0); + } - real_t frac = (p_offset - offset_begin) / idx_interval; + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic - Vector3 forward = (rp[idx + 1] - rp[idx]).normalized(); - Vector3 up = r[idx]; - Vector3 up1 = r[idx + 1]; + Curve3D::Interval interval = _find_interval(p_offset); + return _sample_baked_tilt(interval); +} - if (p_apply_tilt) { - up.rotate(forward, rt[idx]); - up1.rotate(idx + 2 >= count ? forward : (rp[idx + 2] - rp[idx + 1]).normalized(), rt[idx + 1]); +Vector3 Curve3D::sample_baked_up_vector(real_t p_offset, bool p_apply_tilt) const { + if (baked_cache_dirty) { + _bake(); } - Vector3 axis = up.cross(up1); + // Validate: Curve may not have baked up vectors. + ERR_FAIL_COND_V_MSG(!up_vector_enabled, Vector3(0, 1, 0), "No up vectors in Curve3D."); - if (axis.length_squared() < CMP_EPSILON2) { - axis = forward; - } else { - axis.normalize(); + int count = baked_up_vector_cache.size(); + if (count == 1) { + return baked_up_vector_cache.get(0); } - return up.rotated(axis, up.angle_to(up1) * frac); + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. + + Curve3D::Interval interval = _find_interval(p_offset); + return _sample_posture(interval, p_apply_tilt).get_column(1); } PackedVector3Array Curve3D::get_baked_points() const { @@ -1942,6 +1996,50 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con return tess; } +Vector<RBMap<real_t, Vector3>> Curve3D::_tessellate_even_length(int p_max_stages, real_t p_length) const { + Vector<RBMap<real_t, Vector3>> midpoints; + ERR_FAIL_COND_V_MSG(points.size() < 2, midpoints, "Curve must have at least 2 control point"); + + midpoints.resize(points.size() - 1); + + for (int i = 0; i < points.size() - 1; i++) { + _bake_segment3d_even_length(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_length); + } + return midpoints; +} + +PackedVector3Array Curve3D::tessellate_even_length(int p_max_stages, real_t p_length) const { + PackedVector3Array tess; + + Vector<RBMap<real_t, Vector3>> midpoints = _tessellate_even_length(p_max_stages, p_length); + if (midpoints.size() == 0) { + return tess; + } + + int pc = 1; + for (int i = 0; i < points.size() - 1; i++) { + pc++; + pc += midpoints[i].size(); + } + + tess.resize(pc); + Vector3 *bpw = tess.ptrw(); + bpw[0] = points[0].position; + int pidx = 0; + + for (int i = 0; i < points.size() - 1; i++) { + for (const KeyValue<real_t, Vector3> &E : midpoints[i]) { + pidx++; + bpw[pidx] = E.value; + } + + pidx++; + bpw[pidx] = points[i + 1].position; + } + + return tess; +} + bool Curve3D::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) { @@ -2034,6 +2132,7 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve3D::get_baked_length); ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve3D::sample_baked, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked_with_rotation", "offset", "cubic", "apply_tilt"), &Curve3D::sample_baked_with_rotation, DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("sample_baked_up_vector", "offset", "apply_tilt"), &Curve3D::sample_baked_up_vector, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points); ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts); @@ -2041,6 +2140,7 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve3D::get_closest_point); ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve3D::get_closest_offset); ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4)); + ClassDB::bind_method(D_METHOD("tessellate_even_length", "max_stages", "tolerance_length"), &Curve3D::tessellate_even_length, DEFVAL(5), DEFVAL(0.2)); ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data); ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve3D::_set_data); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index fa1d35aab1..88c3cf3ae6 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -238,15 +238,11 @@ class Curve3D : public Resource { Vector<Point> points; - struct BakedPoint { - real_t ofs = 0.0; - Vector3 point; - }; - mutable bool baked_cache_dirty = false; mutable PackedVector3Array baked_point_cache; mutable Vector<real_t> baked_tilt_cache; mutable PackedVector3Array baked_up_vector_cache; + mutable PackedVector3Array baked_forward_vector_cache; mutable Vector<real_t> baked_dist_cache; mutable real_t baked_max_ofs = 0.0; @@ -254,10 +250,20 @@ class Curve3D : public Resource { void _bake() const; + struct Interval { + int idx; + real_t frac; + }; + Interval _find_interval(real_t p_offset) const; + Vector3 _sample_baked(Interval p_interval, bool p_cubic) const; + real_t _sample_baked_tilt(Interval p_interval) const; + Basis _sample_posture(Interval p_interval, bool p_apply_tilt = false) const; + real_t bake_interval = 0.2; bool up_vector_enabled = true; void _bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; + void _bake_segment3d_even_length(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_length) const; Dictionary _get_data() const; void _set_data(const Dictionary &p_data); @@ -268,6 +274,8 @@ class Curve3D : public Resource { void _add_point(const Vector3 &p_position, const Vector3 &p_in = Vector3(), const Vector3 &p_out = Vector3(), int p_atpos = -1); void _remove_point(int p_index); + Vector<RBMap<real_t, Vector3>> _tessellate_even_length(int p_max_stages = 5, real_t p_length = 0.2) const; + protected: static void _bind_methods(); @@ -296,15 +304,17 @@ public: real_t get_baked_length() const; Vector3 sample_baked(real_t p_offset, bool p_cubic = false) const; + Transform3D sample_baked_with_rotation(real_t p_offset, bool p_cubic = false, bool p_apply_tilt = false) const; real_t sample_baked_tilt(real_t p_offset) const; Vector3 sample_baked_up_vector(real_t p_offset, bool p_apply_tilt = false) const; - PackedVector3Array get_baked_points() const; //useful for going through + PackedVector3Array get_baked_points() const; // Useful for going through. Vector<real_t> get_baked_tilts() const; //useful for going through PackedVector3Array get_baked_up_vectors() const; Vector3 get_closest_point(const Vector3 &p_to_point) const; real_t get_closest_offset(const Vector3 &p_to_point) const; - PackedVector3Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; //useful for display + PackedVector3Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; // Useful for display. + PackedVector3Array tessellate_even_length(int p_max_stages = 5, real_t p_length = 0.2) const; // Useful for baking. Curve3D(); }; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 894936acd7..f179b4b818 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -84,7 +84,8 @@ static Ref<ImageTexture> generate_icon(int p_index) { // with integer scales. const bool upsample = !Math::is_equal_approx(Math::round(scale), scale); ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>()); + Error err = img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>()); + ERR_FAIL_COND_V_MSG(err != OK, Ref<ImageTexture>(), "Failed generating icon, unsupported or invalid SVG data in default theme."); #endif return ImageTexture::create_from_image(img); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 185b6f4bdc..af51d6539e 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -1016,7 +1016,7 @@ void FontFile::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks"); } bool FontFile::_set(const StringName &p_name, const Variant &p_value) { @@ -1396,6 +1396,9 @@ Error FontFile::load_bitmap_font(const String &p_path) { case 1: /* info */ { ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, RTR("Invalid BMFont info block size.")); base_size = f->get_16(); + if (base_size <= 0) { + base_size = 16; + } uint8_t flags = f->get_8(); if (flags & (1 << 3)) { st_flags.set_flag(TextServer::FONT_BOLD); @@ -1681,7 +1684,6 @@ Error FontFile::load_bitmap_font(const String &p_path) { if (type == "info") { if (keys.has("size")) { base_size = keys["size"].to_int(); - set_fixed_size(base_size); } if (keys.has("outline")) { outline = keys["outline"].to_int(); @@ -1730,6 +1732,7 @@ Error FontFile::load_bitmap_font(const String &p_path) { encoding = 2; } } + set_fixed_size(base_size); } else if (type == "common") { if (keys.has("lineHeight")) { height = keys["lineHeight"].to_int(); @@ -1788,7 +1791,10 @@ Error FontFile::load_bitmap_font(const String &p_path) { ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format.")); } } else { - if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline + if ((ch[3] == 0) && (ch[0] == 4) && (ch[1] == 4) && (ch[2] == 4) && img->get_format() == Image::FORMAT_RGBA8) { // might be RGBA8 color, no outline (color part of the image should be sold white, but some apps designed for Godot 3 generate color fonts with this config) + outline = 0; + set_texture_image(0, Vector2i(base_size, 0), page, img); + } else if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline outline = 0; ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); set_texture_image(0, Vector2i(base_size, 0), page, img); @@ -2597,18 +2603,18 @@ void FontVariation::_bind_methods() { ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &FontVariation::set_spacing); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_base_font", "get_base_font"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); - ADD_GROUP("Variation", "variation"); + ADD_GROUP("Variation", "variation_"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_opentype"), "set_variation_opentype", "get_variation_opentype"); ADD_PROPERTY(PropertyInfo(Variant::INT, "variation_face_index"), "set_variation_face_index", "get_variation_face_index"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "variation_embolden", PROPERTY_HINT_RANGE, "-2,2,0.01"), "set_variation_embolden", "get_variation_embolden"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "variation_transform", PROPERTY_HINT_NONE, "suffix:px"), "set_variation_transform", "get_variation_transform"); - ADD_GROUP("OpenType Features", "opentype"); + ADD_GROUP("OpenType Features", "opentype_"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_features"), "set_opentype_features", "get_opentype_features"); - ADD_GROUP("Extra Spacing", "spacing"); + ADD_GROUP("Extra Spacing", "spacing_"); ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_glyph", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_GLYPH); ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_space", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_SPACE); ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); @@ -2862,7 +2868,7 @@ void SystemFont::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), "set_subpixel_positioning", "get_subpixel_positioning"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field"), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); } void SystemFont::_update_rids() const { diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index b728c24e0d..d1278f9340 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -255,7 +255,7 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma } #define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \ - Vector3 transformed_vert = Vector3(); \ + Vector3 transformed_vert; \ for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \ int bone_idx = bone_array[vert_idx * bone_count + weight_idx]; \ float w = weight_array[vert_idx * bone_count + weight_idx]; \ @@ -971,10 +971,10 @@ Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecomposit return ret; } -Ref<Shape3D> ImporterMesh::create_trimesh_shape() const { +Ref<ConcavePolygonShape3D> ImporterMesh::create_trimesh_shape() const { Vector<Face3> faces = get_faces(); if (faces.size() == 0) { - return Ref<Shape3D>(); + return Ref<ConcavePolygonShape3D>(); } Vector<Vector3> face_points; diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h index 088a77edd1..bbd6498fcf 100644 --- a/scene/resources/importer_mesh.h +++ b/scene/resources/importer_mesh.h @@ -119,7 +119,7 @@ public: Vector<Face3> get_faces() const; Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const; - Ref<Shape3D> create_trimesh_shape() const; + Ref<ConcavePolygonShape3D> 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); diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp index ef380a68f9..c49620ce27 100644 --- a/scene/resources/label_settings.cpp +++ b/scene/resources/label_settings.cpp @@ -66,16 +66,16 @@ void LabelSettings::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); - ADD_GROUP("Font", "font"); + ADD_GROUP("Font", "font_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,1024,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "font_color"), "set_font_color", "get_font_color"); - ADD_GROUP("Outline", "outline"); + ADD_GROUP("Outline", "outline_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_outline_size", "get_outline_size"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color"); - ADD_GROUP("Shadow", "shadow"); + ADD_GROUP("Shadow", "shadow_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset"); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 706db3af16..4f68a6f69b 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -388,7 +388,7 @@ Vector<Face3> Mesh::get_surface_faces(int p_surface) const { return Vector<Face3>(); } -Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { +Ref<ConvexPolygonShape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { if (p_simplify) { ConvexDecompositionSettings settings; settings.max_convex_hulls = 1; @@ -425,10 +425,10 @@ Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { return shape; } -Ref<Shape3D> Mesh::create_trimesh_shape() const { +Ref<ConcavePolygonShape3D> Mesh::create_trimesh_shape() const { Vector<Face3> faces = get_faces(); if (faces.size() == 0) { - return Ref<Shape3D>(); + return Ref<ConcavePolygonShape3D>(); } Vector<Vector3> face_points; @@ -940,7 +940,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (int16_t)CLAMP(src[0] / 127.0f * 32767, -32768, 32767); dst[1] = (int16_t)CLAMP(src[1] / 127.0f * 32767, -32768, 32767); } - src_offset += sizeof(int16_t) * 2; + src_offset += sizeof(int8_t) * 2; } else { for (uint32_t i = 0; i < p_elements; i++) { const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; @@ -962,7 +962,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(uint8_t) * 4; // 1 byte padding } else { for (uint32_t i = 0; i < p_elements; i++) { const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; @@ -973,7 +973,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(float) * 3; } } @@ -988,7 +988,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP((src[0] / 127.0f * .5f + .5f) * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP((src[1] / 127.0f * .5f + .5f) * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(uint8_t) * 2; } else { // int16 SNORM -> uint16 UNORM for (uint32_t i = 0; i < p_elements; i++) { const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; @@ -1010,7 +1010,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(uint8_t) * 4; } else { for (uint32_t i = 0; i < p_elements; i++) { const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; @@ -1021,7 +1021,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(float) * 4; } } } break; diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 5ed4164117..6f995280e8 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -35,9 +35,12 @@ #include "core/math/face3.h" #include "core/math/triangle_mesh.h" #include "scene/resources/material.h" -#include "scene/resources/shape_3d.h" #include "servers/rendering_server.h" +class ConcavePolygonShape3D; +class ConvexPolygonShape3D; +class Shape3D; + class Mesh : public Resource { GDCLASS(Mesh, Resource); @@ -211,8 +214,8 @@ public: static ConvexDecompositionFunc convex_decomposition_function; Vector<Ref<Shape3D>> convex_decompose(const ConvexDecompositionSettings &p_settings) const; - Ref<Shape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; - Ref<Shape3D> create_trimesh_shape() const; + Ref<ConvexPolygonShape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; + Ref<ConcavePolygonShape3D> create_trimesh_shape() const; virtual int get_builtin_bind_pose_count() const; virtual Transform3D get_builtin_bind_pose(int p_index) const; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 1c99fa5554..f46faa1013 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -71,7 +71,7 @@ static Array _sanitize_node_pinned_properties(Node *p_node) { } Node *SceneState::instantiate(GenEditState p_edit_state) const { - // nodes where instancing failed (because something is missing) + // Nodes where instantiation failed (because something is missing.) List<Node *> stray_instances; #define NODE_FROM_ID(p_name, p_id) \ @@ -122,7 +122,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { NODE_FROM_ID(nparent, n.parent); #ifdef DEBUG_ENABLED if (!nparent && (n.parent & FLAG_ID_IS_PATH)) { - WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data()); + WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instantiating: '" + get_path() + "'.").ascii().get_data()); old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@"); nparent = ret_nodes[0]; } @@ -131,7 +131,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else { // i == 0 is root node. ERR_FAIL_COND_V_MSG(n.parent != -1, nullptr, vformat("Invalid scene: root node %s cannot specify a parent node.", snames[n.name])); - ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANCED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); + ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANTIATED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); } Node *node = nullptr; @@ -169,7 +169,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { ERR_FAIL_COND_V(!node, nullptr); } - } else if (n.type == TYPE_INSTANCED) { + } else if (n.type == TYPE_INSTANTIATED) { //get the node from somewhere, it likely already exists from another instance if (parent) { node = parent->_get_child_by_name(snames[n.name]); @@ -345,7 +345,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { node->add_to_group(snames[n.groups[j]], true); } - if (n.instance >= 0 || n.type != TYPE_INSTANCED || i == 0) { + if (n.instance >= 0 || n.type != TYPE_INSTANTIATED || i == 0) { //if node was not part of instance, must set its name, parenthood and ownership if (i > 0) { if (parent) { @@ -382,7 +382,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } - // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting) + // We only want to deal with pinned flag if instantiating as pure main (no instance, no inheriting.) if (p_edit_state == GEN_EDIT_STATE_MAIN) { _sanitize_node_pinned_properties(node); } else { @@ -665,7 +665,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused if (states_stack.is_empty() && !is_editable_instance) { - //this node is not part of an instancing process, so save the type + //This node is not part of an instantiation process, so save the type. if (missing_node != nullptr) { // It's a missing node (type non existent on load). nd.type = _nm_get_string(missing_node->get_original_class(), name_map); @@ -675,7 +675,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has } else { // this node is part of an instantiated process, so do not save the type. // instead, save that it was instantiated - nd.type = TYPE_INSTANCED; + nd.type = TYPE_INSTANTIATED; } // determine whether to save this node or not @@ -1351,7 +1351,7 @@ int SceneState::get_node_count() const { StringName SceneState::get_node_type(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, nodes.size(), StringName()); - if (nodes[p_idx].type == TYPE_INSTANCED) { + if (nodes[p_idx].type == TYPE_INSTANTIATED) { return StringName(); } return names[nodes[p_idx].type]; diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index c6f82ddd5e..a30ec54d85 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -108,7 +108,7 @@ protected: public: enum { FLAG_ID_IS_PATH = (1 << 30), - TYPE_INSTANCED = 0x7FFFFFFF, + TYPE_INSTANTIATED = 0x7FFFFFFF, FLAG_INSTANCE_IS_PLACEHOLDER = (1 << 30), FLAG_PATH_PROPERTY_IS_NODE = (1 << 30), FLAG_PROP_NAME_MASK = FLAG_PATH_PROPERTY_IS_NODE - 1, diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index eb83a37c7b..2e8fcb3717 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -30,6 +30,7 @@ #include "primitive_meshes.h" +#include "core/config/project_settings.h" #include "core/core_string_names.h" #include "scene/resources/theme.h" #include "scene/theme/theme_db.h" @@ -37,6 +38,8 @@ #include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/polypartition.h" +#define PADDING_REF_SIZE 1024.0 + /** PrimitiveMesh */ @@ -94,6 +97,26 @@ void PrimitiveMesh::_update() const { } } + if (add_uv2) { + // _create_mesh_array should populate our UV2, this is a fallback in case it doesn't. + // As we don't know anything about the geometry we only pad the right and bottom edge + // of our texture. + Vector<Vector2> uv = arr[RS::ARRAY_TEX_UV]; + Vector<Vector2> uv2 = arr[RS::ARRAY_TEX_UV2]; + + if (uv.size() > 0 && uv2.size() == 0) { + Vector2 uv2_scale = get_uv2_scale(); + uv2.resize(uv.size()); + + Vector2 *uv2w = uv2.ptrw(); + for (int i = 0; i < uv.size(); i++) { + uv2w[i] = uv[i] * uv2_scale; + } + } + + arr[RS::ARRAY_TEX_UV2] = uv2; + } + array_len = pc; index_array_len = indices.size(); // in with the new @@ -160,7 +183,12 @@ TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) c uint32_t PrimitiveMesh::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 | RS::ARRAY_FORMAT_INDEX; + uint32_t mesh_format = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX; + if (add_uv2) { + mesh_format |= RS::ARRAY_FORMAT_TEX_UV2; + } + + return mesh_format; } Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const { @@ -219,9 +247,17 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces); ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces); + ClassDB::bind_method(D_METHOD("set_add_uv2", "add_uv2"), &PrimitiveMesh::set_add_uv2); + ClassDB::bind_method(D_METHOD("get_add_uv2"), &PrimitiveMesh::get_add_uv2); + + ClassDB::bind_method(D_METHOD("set_uv2_padding", "uv2_padding"), &PrimitiveMesh::set_uv2_padding); + ClassDB::bind_method(D_METHOD("get_uv2_padding"), &PrimitiveMesh::get_uv2_padding); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_uv2"), "set_add_uv2", "get_add_uv2"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv2_padding"), "set_uv2_padding", "get_uv2_padding"); GDVIRTUAL_BIND(_create_mesh_array); } @@ -233,7 +269,7 @@ void PrimitiveMesh::set_material(const Ref<Material> &p_material) { RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid()); notify_property_list_changed(); emit_changed(); - }; + } } Ref<Material> PrimitiveMesh::get_material() const { @@ -263,6 +299,42 @@ bool PrimitiveMesh::get_flip_faces() const { return flip_faces; } +void PrimitiveMesh::set_add_uv2(bool p_enable) { + add_uv2 = p_enable; + _update_lightmap_size(); + _request_update(); +} + +void PrimitiveMesh::set_uv2_padding(float p_padding) { + uv2_padding = p_padding; + _update_lightmap_size(); + _request_update(); +} + +Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const { + Vector2 uv2_scale; + Vector2 lightmap_size = get_lightmap_size_hint(); + + // Calculate it as a margin, if no lightmap size hint is given we assume "PADDING_REF_SIZE" as our texture size. + uv2_scale.x = p_margin_scale.x * uv2_padding / (lightmap_size.x == 0.0 ? PADDING_REF_SIZE : lightmap_size.x); + uv2_scale.y = p_margin_scale.y * uv2_padding / (lightmap_size.y == 0.0 ? PADDING_REF_SIZE : lightmap_size.y); + + // Inverse it to turn our margin into a scale + uv2_scale = Vector2(1.0, 1.0) - uv2_scale; + + return uv2_scale; +} + +float PrimitiveMesh::get_lightmap_texel_size() const { + float texel_size = GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"); + + if (texel_size <= 0.0) { + texel_size = 0.2; + } + + return texel_size; +} + PrimitiveMesh::PrimitiveMesh() { mesh = RenderingServer::get_singleton()->mesh_create(); } @@ -275,22 +347,52 @@ PrimitiveMesh::~PrimitiveMesh() { CapsuleMesh */ +void CapsuleMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend + float vertical_length = radial_length * 2 + (height - 2.0 * radius); // total vertical length + + _lightmap_size_hint.x = MAX(1.0, 4.0 * radial_length / texel_size) + padding; + _lightmap_size_hint.y = MAX(1.0, vertical_length / texel_size) + padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void CapsuleMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, radius, height, radial_segments, rings); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding); } -void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings) { +void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z, u, v, w; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + float radial_width = 2.0 * radius * Math_PI; + float radial_h = radial_width / (radial_width + p_uv2_padding); + float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend + float vertical_length = radial_length * 2 + (height - 2.0 * radius) + p_uv2_padding; // total vertical length + float radial_v = radial_length / vertical_length; // v size of top and bottom section + float height_v = (height - 2.0 * radius) / vertical_length; // v size of height section + // note, this has been aligned with our collision shape but I've left the descriptions as top/middle/bottom Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -322,6 +424,9 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa normals.push_back(p.normalized()); ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * onethird)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, v * radial_v)); + } point++; if (i > 0 && j > 0) { @@ -332,12 +437,12 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } /* cylinder */ thisrow = point; @@ -361,6 +466,9 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa normals.push_back(Vector3(x, 0.0, -z)); ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, onethird + (v * onethird))); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, radial_v + (v * height_v))); + } point++; if (i > 0 && j > 0) { @@ -371,12 +479,12 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } /* bottom hemisphere */ thisrow = point; @@ -390,17 +498,20 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa y = radius * cos(0.5 * Math_PI * v); for (i = 0; i <= radial_segments; i++) { - float u2 = i; - u2 /= radial_segments; + u = i; + u /= radial_segments; - x = -sin(u2 * Math_TAU); - z = cos(u2 * Math_TAU); + x = -sin(u * Math_TAU); + z = cos(u * Math_TAU); Vector3 p = Vector3(x * radius * w, y, -z * radius * w); points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0)); normals.push_back(p.normalized()); ADD_TANGENT(-z, 0.0, -x, 1.0) - uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird))); + uvs.push_back(Vector2(u, twothirds + ((v - 1.0) * onethird))); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + ((v - 1.0) * radial_v))); + } point++; if (i > 0 && j > 0) { @@ -411,17 +522,20 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -450,6 +564,7 @@ void CapsuleMesh::set_radius(const float p_radius) { if (radius > height * 0.5) { height = radius * 2.0; } + _update_lightmap_size(); _request_update(); } @@ -462,6 +577,7 @@ void CapsuleMesh::set_height(const float p_height) { if (radius > height * 0.5) { radius = height * 0.5; } + _update_lightmap_size(); _request_update(); } @@ -493,16 +609,53 @@ CapsuleMesh::CapsuleMesh() {} BoxMesh */ +void BoxMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float width = (size.x + size.z) / texel_size; + float length = (size.y + size.y + MAX(size.x, size.z)) / texel_size; + + _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding; + _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void BoxMesh::_create_mesh_array(Array &p_arr) const { - BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d); + // Note about padding, with our box each face of the box faces a different direction so we want a seam + // around every face. We thus add our padding to the right and bottom of each face. + // With 3 faces along the width and 2 along the height of the texture we need to adjust our scale + // accordingly. + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding); } -void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d) { +void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + // TODO this could be improved by changing the order depending on which side is the longest (basically the below works best if size.y is the longest) + float total_h = (size.x + size.z + (2.0 * p_uv2_padding)); + float padding_h = p_uv2_padding / total_h; + float width_h = size.x / total_h; + float depth_h = size.z / total_h; + float total_v = (size.y + size.y + MAX(size.x, size.z) + (3.0 * p_uv2_padding)); + float padding_v = p_uv2_padding / total_v; + float width_v = size.x / total_v; + float height_v = size.y / total_v; + float depth_v = size.z / total_v; + Vector3 start_pos = size * -0.5; // set our bounding box @@ -511,6 +664,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -525,18 +679,24 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int thisrow = point; prevrow = 0; for (j = 0; j <= subdivide_h + 1; j++) { + float v = j; + float v2 = v / (subdivide_w + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + x = start_pos.x; for (i = 0; i <= subdivide_w + 1; i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); // front points.push_back(Vector3(x, -y, -start_pos.z)); // double negative on the Z! normals.push_back(Vector3(0.0, 0.0, 1.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, v2 * height_v)); + } point++; // back @@ -544,6 +704,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(0.0, 0.0, -1.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, height_v + padding_v + (v2 * height_v))); + } point++; if (i > 0 && j > 0) { @@ -564,33 +727,39 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } // left + right y = start_pos.y; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_h + 1); j++) { + float v = j; + float v2 = v / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + z = start_pos.z; for (i = 0; i <= (subdivide_d + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_d + 1.0); u /= (3.0 * (subdivide_d + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); // right points.push_back(Vector3(-start_pos.x, -y, -z)); normals.push_back(Vector3(1.0, 0.0, 0.0)); ADD_TANGENT(0.0, 0.0, -1.0, 1.0); uvs.push_back(Vector2(onethird + u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), v2 * height_v)); + } point++; // left @@ -598,6 +767,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(-1.0, 0.0, 0.0)); ADD_TANGENT(0.0, 0.0, 1.0, 1.0); uvs.push_back(Vector2(u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), height_v + padding_v + (v2 * height_v))); + } point++; if (i > 0 && j > 0) { @@ -618,33 +790,39 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } z += size.z / (subdivide_d + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } // top + bottom z = start_pos.z; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_d + 1); j++) { + float v = j; + float v2 = v / (subdivide_d + 1.0); + v /= (2.0 * (subdivide_d + 1.0)); + x = start_pos.x; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_d + 1.0)); // top points.push_back(Vector3(-x, -start_pos.y, -z)); normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(onethird + u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, ((height_v + padding_v) * 2.0) + (v2 * depth_v))); + } point++; // bottom @@ -652,6 +830,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), ((height_v + padding_v) * 2.0) + (v2 * width_v))); + } point++; if (i > 0 && j > 0) { @@ -672,20 +853,23 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.z / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -708,6 +892,7 @@ void BoxMesh::_bind_methods() { void BoxMesh::set_size(const Vector3 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -748,18 +933,58 @@ BoxMesh::BoxMesh() {} CylinderMesh */ +void CylinderMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float top_circumference = top_radius * Math_PI * 2.0; + float bottom_circumference = bottom_radius * Math_PI * 2.0; + + float _width = MAX(top_circumference, bottom_circumference) / texel_size + padding; + _width = MAX(_width, (((top_radius + bottom_radius) / texel_size) + padding) * 2.0); // this is extremely unlikely to be larger, will only happen if padding is larger then our diameter. + _lightmap_size_hint.x = MAX(1.0, _width); + + float _height = ((height + (MAX(top_radius, bottom_radius) * 2.0)) / texel_size) + (2.0 * padding); + + _lightmap_size_hint.y = MAX(1.0, _height); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void CylinderMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding); } -void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom) { +void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; - float x, y, z, u, v, radius; + float x, y, z, u, v, radius, radius_h; + + // Only used if we calculate UV2 + float top_circumference = top_radius * Math_PI * 2.0; + float bottom_circumference = bottom_radius * Math_PI * 2.0; + float vertical_length = height + MAX(2.0 * top_radius, 2.0 * bottom_radius) + (2.0 * p_uv2_padding); + float height_v = height / vertical_length; + float padding_v = p_uv2_padding / vertical_length; + + float horizonal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding); + float center_h = 0.5 * (horizonal_length - p_uv2_padding) / horizonal_length; + float top_h = top_circumference / horizonal_length; + float bottom_h = bottom_circumference / horizonal_length; + float padding_h = p_uv2_padding / horizonal_length; Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -777,6 +1002,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto v /= (rings + 1); radius = top_radius + ((bottom_radius - top_radius) * v); + radius_h = top_h + ((bottom_h - top_h) * v); y = height * v; y = (height * 0.5) - y; @@ -793,6 +1019,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(x, side_normal_y, z).normalized()); ADD_TANGENT(z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * 0.5)); + if (p_add_uv2) { + uv2s.push_back(Vector2(center_h + (u - 0.5) * radius_h, v * height_v)); + } point++; if (i > 0 && j > 0) { @@ -803,14 +1032,20 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } - // add top + // Adjust for bottom section, only used if we calculate UV2s. + top_h = top_radius / horizonal_length; + float top_v = top_radius / vertical_length; + bottom_h = bottom_radius / horizonal_length; + float bottom_v = bottom_radius / vertical_length; + + // Add top. if (cap_top && top_radius > 0.0) { y = height * 0.5; @@ -819,6 +1054,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(0.25, 0.75)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h, height_v + padding_v + MAX(top_v, bottom_v))); + } point++; for (i = 0; i <= radial_segments; i++) { @@ -836,17 +1074,20 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + (x * top_h), height_v + padding_v + MAX(top_v, bottom_v) + (z * top_v))); + } point++; if (i > 0) { indices.push_back(thisrow); indices.push_back(point - 1); indices.push_back(point - 2); - }; - }; - }; + } + } + } - // add bottom + // Add bottom. if (cap_bottom && bottom_radius > 0.0) { y = height * -0.5; @@ -855,6 +1096,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(0.75, 0.75)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h, height_v + padding_v + MAX(top_v, bottom_v))); + } point++; for (i = 0; i <= radial_segments; i++) { @@ -872,20 +1116,26 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h + (x * bottom_h), height_v + padding_v + MAX(top_v, bottom_v) - (z * bottom_v))); + } point++; if (i > 0) { indices.push_back(thisrow); indices.push_back(point - 2); indices.push_back(point - 1); - }; - }; - }; + } + } + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -919,6 +1169,7 @@ void CylinderMesh::_bind_methods() { void CylinderMesh::set_top_radius(const float p_radius) { top_radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -928,6 +1179,7 @@ float CylinderMesh::get_top_radius() const { void CylinderMesh::set_bottom_radius(const float p_radius) { bottom_radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -937,6 +1189,7 @@ float CylinderMesh::get_bottom_radius() const { void CylinderMesh::set_height(const float p_height) { height = p_height; + _update_lightmap_size(); _request_update(); } @@ -986,10 +1239,26 @@ CylinderMesh::CylinderMesh() {} PlaneMesh */ +void PlaneMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + _lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding); + _lightmap_size_hint.y = MAX(1.0, (size.y / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void PlaneMesh::_create_mesh_array(Array &p_arr) const { int i, j, prevrow, thisrow, point; float x, z; + // Plane mesh can use default UV2 calculation as implemented in Primitive Mesh + Size2 start_pos = size * -0.5; Vector3 normal = Vector3(0.0, 1.0, 0.0); @@ -1043,15 +1312,15 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.y / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; @@ -1088,6 +1357,7 @@ void PlaneMesh::_bind_methods() { void PlaneMesh::set_size(const Size2 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -1137,12 +1407,49 @@ PlaneMesh::PlaneMesh() {} PrismMesh */ +void PrismMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + // left_to_right does not effect the surface area of the prism so we ignore that. + // TODO we could combine the two triangles and save some space but we need to re-align the uv1 and adjust the tangent. + + float width = (size.x + size.z) / texel_size; + float length = (size.y + size.y + size.z) / texel_size; + + _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding; + _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void PrismMesh::_create_mesh_array(Array &p_arr) const { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + float horizontal_total = size.x + size.z + 2.0 * _uv2_padding; + float width_h = size.x / horizontal_total; + float depth_h = size.z / horizontal_total; + float padding_h = _uv2_padding / horizontal_total; + + float vertical_total = (size.y + size.y + size.z) + (3.0 * _uv2_padding); + float height_v = size.y / vertical_total; + float depth_v = size.z / vertical_total; + float padding_v = _uv2_padding / vertical_total; + + // and start building + Vector3 start_pos = size * -0.5; // set our bounding box @@ -1151,6 +1458,7 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -1171,12 +1479,15 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { float offset_front = (1.0 - scale) * onethird * left_to_right; float offset_back = (1.0 - scale) * onethird * (1.0 - left_to_right); + float v = j; + float v2 = j / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + x = 0.0; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = i / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); u *= scale; @@ -1185,6 +1496,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(Vector3(0.0, 0.0, 1.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(offset_front + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * scale * width_h, v2 * height_v)); + } point++; /* back */ @@ -1192,6 +1506,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(Vector3(0.0, 0.0, -1.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + offset_back + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * scale * width_h, height_v + padding_v + v2 * height_v)); + } point++; if (i > 0 && j == 1) { @@ -1224,15 +1541,15 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += scale * size.x / (subdivide_w + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } /* left + right */ Vector3 normal_left, normal_right; @@ -1246,6 +1563,10 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_h + 1); j++) { + float v = j; + float v2 = j / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + float left, right; float scale = (y - start_pos.y) / size.y; @@ -1255,15 +1576,17 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { z = start_pos.z; for (i = 0; i <= (subdivide_d + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_d + 1.0); u /= (3.0 * (subdivide_d + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); /* right */ points.push_back(Vector3(right, -y, -z)); normals.push_back(normal_right); ADD_TANGENT(0.0, 0.0, -1.0, 1.0); uvs.push_back(Vector2(onethird + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, v2 * height_v)); + } point++; /* left */ @@ -1271,6 +1594,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(normal_left); ADD_TANGENT(0.0, 0.0, 1.0, 1.0); uvs.push_back(Vector2(u, 0.5 + v)); + if (_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, height_v + padding_v + v2 * height_v)); + } point++; if (i > 0 && j > 0) { @@ -1291,33 +1617,39 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } z += size.z / (subdivide_d + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } /* bottom */ z = start_pos.z; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_d + 1); j++) { + float v = j; + float v2 = v / (subdivide_d + 1.0); + v /= (2.0 * (subdivide_d + 1.0)); + x = start_pos.x; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_d + 1.0)); /* bottom */ points.push_back(Vector3(x, start_pos.y, -z)); normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, 0.5 + v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, 2.0 * (height_v + padding_v) + v2 * depth_v)); + } point++; if (i > 0 && j > 0) { @@ -1328,20 +1660,23 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.z / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1377,6 +1712,7 @@ float PrismMesh::get_left_to_right() const { void PrismMesh::set_size(const Vector3 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -1417,22 +1753,50 @@ PrismMesh::PrismMesh() {} SphereMesh */ +void SphereMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float _width = radius * Math_TAU; + _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding); + float _height = (is_hemisphere ? 1.0 : 0.5) * height * Math_PI; // note, with hemisphere height is our radius, while with a full sphere it is the diameter.. + _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void SphereMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding); } -void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere) { +void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z; float scale = height * (is_hemisphere ? 1.0 : 0.5); + // Only used if we calculate UV2 + float circumference = radius * Math_TAU; + float horizontal_length = circumference + p_uv2_padding; + float center_h = 0.5 * circumference / horizontal_length; + + float height_v = scale * Math_PI / ((scale * Math_PI) + p_uv2_padding); + // set our bounding box Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -1467,9 +1831,13 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int points.push_back(p); 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) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + float w_h = w * 2.0 * center_h; + uv2s.push_back(Vector2(center_h + ((u - 0.5) * w_h), v * height_v)); + } point++; if (i > 0 && j > 0) { @@ -1480,17 +1848,20 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1517,6 +1888,7 @@ void SphereMesh::_bind_methods() { void SphereMesh::set_radius(const float p_radius) { radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -1526,6 +1898,7 @@ float SphereMesh::get_radius() const { void SphereMesh::set_height(const float p_height) { height = p_height; + _update_lightmap_size(); _request_update(); } @@ -1553,6 +1926,7 @@ int SphereMesh::get_rings() const { void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) { is_hemisphere = p_is_hemisphere; + _update_lightmap_size(); _request_update(); } @@ -1566,6 +1940,31 @@ SphereMesh::SphereMesh() {} TorusMesh */ +void TorusMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float min_radius = inner_radius; + float max_radius = outer_radius; + + if (min_radius > max_radius) { + SWAP(min_radius, max_radius); + } + + float radius = (max_radius - min_radius) * 0.5; + + float _width = max_radius * Math_TAU; + _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding); + float _height = radius * Math_TAU; + _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void TorusMesh::_create_mesh_array(Array &p_arr) const { // set our bounding box @@ -1573,6 +1972,7 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; #define ADD_TANGENT(m_x, m_y, m_z, m_d) \ @@ -1592,6 +1992,17 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { float radius = (max_radius - min_radius) * 0.5; + // Only used if we calculate UV2 + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + float horizontal_total = max_radius * Math_TAU + _uv2_padding; + float max_h = max_radius * Math_TAU / horizontal_total; + float delta_h = (max_radius - min_radius) * Math_TAU / horizontal_total; + + float height_v = radius * Math_TAU / (radius * Math_TAU + _uv2_padding); + for (int i = 0; i <= rings; i++) { int prevrow = (i - 1) * (ring_segments + 1); int thisrow = i * (ring_segments + 1); @@ -1607,10 +2018,17 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { Vector2 normalj = Vector2(-Math::cos(angj), Math::sin(angj)); Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0); + float offset_h = 0.5 * (1.0 - normalj.x) * delta_h; + float adj_h = max_h - offset_h; + offset_h *= 0.5; + points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x)); normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x)); ADD_TANGENT(-Math::cos(angi), 0.0, Math::sin(angi), 1.0); uvs.push_back(Vector2(inci, incj)); + if (_add_uv2) { + uv2s.push_back(Vector2(offset_h + inci * adj_h, incj * height_v)); + } if (i > 0 && j > 0) { indices.push_back(thisrow + j - 1); @@ -1628,6 +2046,9 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1785,6 +2206,8 @@ Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const { } void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { + // Seeing use case for TubeTrailMesh, no need to do anything more then default UV2 calculation + PackedVector3Array points; PackedVector3Array normals; PackedFloat32Array tangents; @@ -1920,9 +2343,9 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(thisrow); indices.push_back(point - 1); indices.push_back(point - 2); - }; - }; - }; + } + } + } float scale_neg = 1.0; if (curve.is_valid() && curve->get_point_count() > 0) { @@ -1983,9 +2406,9 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(thisrow); indices.push_back(point - 2); indices.push_back(point - 1); - }; - }; - }; + } + } + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; @@ -2109,6 +2532,8 @@ Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const { } void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const { + // Seeing use case of ribbon trail mesh, no need to implement special UV2 calculation + PackedVector3Array points; PackedVector3Array normals; PackedFloat32Array tangents; @@ -2354,13 +2779,7 @@ void TextMesh::_generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph real_t step = CLAMP(curve_step / (p0 - p3).length(), 0.01, 0.5); real_t t = step; while (t < 1.0) { - real_t omt = (1.0 - t); - real_t omt2 = omt * omt; - real_t omt3 = omt2 * omt; - real_t t2 = t * t; - real_t t3 = t2 * t; - - Vector2 point = p0 * omt3 + p1 * omt2 * t * 3.0 + p2 * omt * t2 * 3.0 + p3 * t3; + Vector2 point = p0.bezier_interpolate(p1, p2, p3, t); Vector2 p = point * pixel_size + origin; polygon.push_back(ContourPoint(p, false)); t += step; diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index ee61f0ac55..06f9781b84 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -56,6 +56,9 @@ private: Ref<Material> material; bool flip_faces = false; + bool add_uv2 = false; + float uv2_padding = 2.0; + // make sure we do an update after we've finished constructing our object mutable bool pending_request = true; void _update() const; @@ -70,6 +73,10 @@ protected: void _request_update(); GDVIRTUAL0RC(Array, _create_mesh_array) + Vector2 get_uv2_scale(Vector2 p_margin_scale = Vector2(1.0, 1.0)) const; + float get_lightmap_texel_size() const; + virtual void _update_lightmap_size(){}; + public: virtual int get_surface_count() const override; virtual int surface_get_array_len(int p_idx) const override; @@ -98,6 +105,12 @@ public: void set_flip_faces(bool p_enable); bool get_flip_faces() const; + void set_add_uv2(bool p_enable); + bool get_add_uv2() const { return add_uv2; } + + void set_uv2_padding(float p_padding); + float get_uv2_padding() const { return uv2_padding; } + PrimitiveMesh(); ~PrimitiveMesh(); }; @@ -118,8 +131,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8); + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_radius(const float p_radius); float get_radius() const; @@ -152,8 +167,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0); + static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_size(const Vector3 &p_size); Vector3 get_size() const; @@ -190,8 +207,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true); + static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_top_radius(const float p_radius); float get_top_radius() const; @@ -241,6 +260,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_size(const Size2 &p_size); Size2 get_size() const; @@ -292,6 +313,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_left_to_right(const float p_left_to_right); float get_left_to_right() const; @@ -328,8 +351,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false); + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_radius(const float p_radius); float get_radius() const; @@ -365,6 +390,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_inner_radius(const float p_inner_radius); float get_inner_radius() const; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 85b538b1d9..8b2b7e118c 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -217,7 +217,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars if (next_tag.fields.has("type")) { type = packed_scene->get_state()->add_name(next_tag.fields["type"]); } else { - type = SceneState::TYPE_INSTANCED; //no type? assume this was instantiated + type = SceneState::TYPE_INSTANTIATED; //no type? assume this was instantiated } HashSet<StringName> path_properties; @@ -256,7 +256,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars if (next_tag.fields.has("owner")) { owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]); } else { - if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1)) { + if (parent != -1 && !(type == SceneState::TYPE_INSTANTIATED && instance == -1)) { owner = 0; //if no owner, owner is root } } @@ -511,6 +511,7 @@ Error ResourceLoaderText::load() { if (error) { _printerr(); + return error; } resource_current++; @@ -600,9 +601,13 @@ Error ResourceLoaderText::load() { resource_current++; int_resources[id] = res; //always assign int resources - if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); - res->set_scene_unique_id(id); + if (do_assign) { + if (cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE) { + res->set_path(path); + } else { + res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); + res->set_scene_unique_id(id); + } } Dictionary missing_resource_properties; @@ -884,6 +889,7 @@ void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_d error_text = "Unexpected end of file"; _printerr(); error = ERR_FILE_CORRUPT; + return; } } } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 4d566178a5..3a671edeea 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -177,7 +177,7 @@ bool Shader::is_text_shader() const { } bool Shader::has_parameter(const StringName &p_name) const { - return params_cache.has("shader_parameter/" + p_name); + return params_cache.has(p_name); } void Shader::_update_shader() const { diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp index 61daf801e8..413670d23e 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -116,7 +116,7 @@ bool Shape2D::is_collision_outline_enabled() { return true; } #endif - return GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true); + return GLOBAL_GET("debug/shapes/collision/draw_2d_outlines"); } Shape2D::Shape2D(const RID &p_rid) { @@ -124,5 +124,7 @@ Shape2D::Shape2D(const RID &p_rid) { } Shape2D::~Shape2D() { - PhysicsServer2D::get_singleton()->free(shape); + if (PhysicsServer2D::get_singleton() != nullptr) { + PhysicsServer2D::get_singleton()->free(shape); + } } diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp index 4423c1d7bb..44f21d2a48 100644 --- a/scene/resources/shape_3d.cpp +++ b/scene/resources/shape_3d.cpp @@ -128,5 +128,7 @@ Shape3D::Shape3D(RID p_shape) : shape(p_shape) {} Shape3D::~Shape3D() { - PhysicsServer3D::get_singleton()->free(shape); + if (PhysicsServer3D::get_singleton() != nullptr) { + PhysicsServer3D::get_singleton()->free(shape); + } } diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h index 4a875d039f..0ca6582965 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.h +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -68,8 +68,8 @@ private: float chain_tolarance = 0.01; int chain_max_iterations = 10; int chain_iterations = 0; - Transform2D target_global_pose = Transform2D(); - Transform2D origin_global_pose = Transform2D(); + Transform2D target_global_pose; + Transform2D origin_global_pose; void fabrik_joint_update_bone2d_cache(int p_joint_idx); void chain_backwards(); diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h index e2e490d636..3e3aa5e587 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.h +++ b/scene/resources/skeleton_modification_3d_fabrik.h @@ -47,7 +47,7 @@ private: bool auto_calculate_length = true; bool use_tip_node = false; - NodePath tip_node = NodePath(); + NodePath tip_node; ObjectID tip_node_cache; bool use_target_basis = false; @@ -68,8 +68,8 @@ private: void update_joint_tip_cache(int p_joint_idx); int final_joint_idx = 0; - Transform3D target_global_pose = Transform3D(); - Transform3D origin_global_pose = Transform3D(); + Transform3D target_global_pose; + Transform3D origin_global_pose; void chain_backwards(); void chain_forwards(); diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp index f1eddd8ffc..cb5cb4ef96 100644 --- a/scene/resources/syntax_highlighter.cpp +++ b/scene/resources/syntax_highlighter.cpp @@ -336,7 +336,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) { } String word = str.substr(j, to - j); - Color col = Color(); + Color col; if (keywords.has(word)) { col = keywords[word]; } else if (member_keywords.has(word)) { diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index ca1cd54349..afab44834d 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -317,11 +317,7 @@ float TextLine::get_width() const { Size2 TextLine::get_size() const { const_cast<TextLine *>(this)->_shape(); - if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); - } else { - return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); - } + return TS->shaped_text_get_size(rid); } float TextLine::get_line_ascent() const { diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index a6fb359051..b5754caa6a 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -64,6 +64,7 @@ bool Texture2D::is_pixel_opaque(int p_x, int p_y) const { GDVIRTUAL_CALL(_is_pixel_opaque, p_x, p_y, ret); return ret; } + bool Texture2D::has_alpha() const { bool ret = true; GDVIRTUAL_CALL(_has_alpha, ret); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 3caf6484d9..9d2537bb4d 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -3789,7 +3789,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const { Size2i valid_area = txt->get_size() - margins; // Compute the number of valid tiles in the tiles atlas - Size2i grid_size = Size2i(); + Size2i grid_size; if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) { valid_area -= texture_region_size; grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation); @@ -4363,6 +4363,7 @@ int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, i tiles[p_atlas_coords].alternatives[new_alternative_id] = memnew(TileData); tiles[p_atlas_coords].alternatives[new_alternative_id]->set_tile_set(tile_set); tiles[p_atlas_coords].alternatives[new_alternative_id]->set_allow_transform(true); + tiles[p_atlas_coords].alternatives[new_alternative_id]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); tiles[p_atlas_coords].alternatives[new_alternative_id]->notify_property_list_changed(); tiles[p_atlas_coords].alternatives_ids.append(new_alternative_id); tiles[p_atlas_coords].alternatives_ids.sort(); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 8f175e99a6..9f465a17e9 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -785,7 +785,7 @@ private: bool flip_h = false; bool flip_v = false; bool transpose = false; - Vector2i tex_offset = Vector2i(); + Vector2i tex_offset; Ref<Material> material = Ref<Material>(); Color modulate = Color(1.0, 1.0, 1.0, 1.0); int z_index = 0; diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 04637983b5..03abac1b3e 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -3108,9 +3108,9 @@ void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_fun return; } if (p_func == FUNC_PANNING) { - set_input_port_default_value(2, Vector2()); // offset + set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // offset } else { // FUNC_SCALING - set_input_port_default_value(2, Vector2(0.5, 0.5)); // pivot + set_input_port_default_value(2, Vector2(0.5, 0.5), get_input_port_default_value(2)); // pivot } func = p_func; emit_changed(); diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index a15c03d675..bc7918c662 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -206,8 +206,6 @@ SceneStringNames::SceneStringNames() { theme_changed = StaticCString::create("theme_changed"); parameters_base_path = "parameters/"; - tracks_changed = "tracks_changed"; - shader_overrides_group = StaticCString::create("_shader_overrides_group_"); shader_overrides_group_active = StaticCString::create("_shader_overrides_group_active_"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 5589ab327f..7ff866cacd 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -209,8 +209,6 @@ public: StringName parameters_base_path; - StringName tracks_changed; - StringName _window_group; StringName _window_input; StringName _window_unhandled_input; |