diff options
Diffstat (limited to 'scene')
131 files changed, 2412 insertions, 1112 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 1cab29f383..108f200613 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -56,7 +56,9 @@ Point2 AnimatedSprite2D::_edit_get_pivot() const { bool AnimatedSprite2D::_edit_use_pivot() const { return true; } +#endif // TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 AnimatedSprite2D::_edit_get_rect() const { return _get_rect(); } @@ -75,7 +77,7 @@ bool AnimatedSprite2D::_edit_use_rect() const { } return t.is_valid(); } -#endif +#endif // DEBUG_ENABLED Rect2 AnimatedSprite2D::get_anchorable_rect() const { return _get_rect(); @@ -593,7 +595,7 @@ void AnimatedSprite2D::get_argument_options(const StringName &p_function, int p_ } Node2D::get_argument_options(p_function, p_idx, r_options); } -#endif +#endif // TOOLS_ENABLED #ifndef DISABLE_DEPRECATED bool AnimatedSprite2D::_set(const StringName &p_name, const Variant &p_value) { diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index 914dc405ab..3de36e7cab 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -66,7 +66,7 @@ class AnimatedSprite2D : public Node2D { protected: #ifndef DISABLE_DEPRECATED bool _set(const StringName &p_name, const Variant &p_value); -#endif +#endif // DISABLE_DEPRECATED static void _bind_methods(); void _notification(int p_what); void _validate_property(PropertyInfo &p_property) const; @@ -79,9 +79,12 @@ public: virtual void _edit_set_pivot(const Point2 &p_pivot) override; virtual Point2 _edit_get_pivot() const override; virtual bool _edit_use_pivot() const override; +#endif // TOOLS_ENABLED + +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; -#endif +#endif // DEBUG_ENABLED virtual Rect2 get_anchorable_rect() const override; @@ -129,7 +132,7 @@ public: #ifdef TOOLS_ENABLED virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; -#endif +#endif // TOOLS_ENABLED AnimatedSprite2D(); }; diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp index 4cae1affc3..045dd3be02 100644 --- a/scene/2d/back_buffer_copy.cpp +++ b/scene/2d/back_buffer_copy.cpp @@ -45,7 +45,7 @@ void BackBufferCopy::_update_copy_mode() { } } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 BackBufferCopy::_edit_get_rect() const { return rect; } @@ -53,7 +53,7 @@ Rect2 BackBufferCopy::_edit_get_rect() const { bool BackBufferCopy::_edit_use_rect() const { return true; } -#endif +#endif // DEBUG_ENABLED Rect2 BackBufferCopy::get_anchorable_rect() const { return rect; diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h index e918995708..9bcb731b50 100644 --- a/scene/2d/back_buffer_copy.h +++ b/scene/2d/back_buffer_copy.h @@ -54,10 +54,10 @@ protected: void _validate_property(PropertyInfo &p_property) const; public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; -#endif +#endif // DEBUG_ENABLED void set_rect(const Rect2 &p_rect); Rect2 get_rect() const; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 7020d162fe..74f26df706 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -115,11 +115,11 @@ void Camera2D::set_zoom(const Vector2 &p_zoom) { Point2 old_smoothed_camera_pos = smoothed_camera_pos; _update_scroll(); smoothed_camera_pos = old_smoothed_camera_pos; -}; +} Vector2 Camera2D::get_zoom() const { return zoom; -}; +} Transform2D Camera2D::get_camera_transform() { if (!get_tree()) { @@ -302,6 +302,7 @@ void Camera2D::_notification(int p_what) { _interpolation_data.xform_prev = _interpolation_data.xform_curr; } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (is_physics_interpolated_and_enabled()) { _update_scroll(); diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index cfdcbee86a..b50881cb89 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -696,6 +696,8 @@ void GPUParticles2D::_notification(int p_what) { RS::get_singleton()->particles_set_subemitter(particles, RID()); } break; + case NOTIFICATION_SUSPENDED: + case NOTIFICATION_UNSUSPENDED: case NOTIFICATION_PAUSED: case NOTIFICATION_UNPAUSED: { if (is_inside_tree()) { diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 50c5873781..21648bbc49 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -54,7 +54,7 @@ void Light2D::_update_light_visibility() { if (editor_only) { editor_ok = false; } -#endif +#endif // TOOLS_ENABLED RS::get_singleton()->canvas_light_set_enabled(canvas_light, enabled && is_visible_in_tree() && editor_ok); } @@ -343,7 +343,6 @@ Light2D::~Light2D() { ////////////////////////////// #ifdef TOOLS_ENABLED - Dictionary PointLight2D::_edit_get_state() const { Dictionary state = Node2D::_edit_get_state(); state["offset"] = get_texture_offset(); @@ -367,7 +366,9 @@ Point2 PointLight2D::_edit_get_pivot() const { bool PointLight2D::_edit_use_pivot() const { return true; } +#endif // TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 PointLight2D::_edit_get_rect() const { if (texture.is_null()) { return Rect2(); @@ -380,7 +381,7 @@ Rect2 PointLight2D::_edit_get_rect() const { bool PointLight2D::_edit_use_rect() const { return !texture.is_null(); } -#endif +#endif // DEBUG_ENABLED Rect2 PointLight2D::get_anchorable_rect() const { if (texture.is_null()) { diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index 8a0c2a2a92..0c1dae071c 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -160,9 +160,12 @@ public: virtual void _edit_set_pivot(const Point2 &p_pivot) override; virtual Point2 _edit_get_pivot() const override; virtual bool _edit_use_pivot() const override; +#endif // TOOLS_ENABLED + +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; -#endif +#endif // DEBUG_ENABLED virtual Rect2 get_anchorable_rect() const override; diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 7c3fb61d04..e2e4125d1f 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -35,7 +35,7 @@ #define LINE_GRAB_WIDTH 8 -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 OccluderPolygon2D::_edit_get_rect() const { if (rect_cache_dirty) { if (closed) { @@ -83,13 +83,14 @@ bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double return false; } } -#endif +#endif // DEBUG_ENABLED void OccluderPolygon2D::set_polygon(const Vector<Vector2> &p_polygon) { polygon = p_polygon; rect_cache_dirty = true; RS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon, p_polygon, closed); emit_changed(); + update_configuration_warning(); } Vector<Vector2> OccluderPolygon2D::get_polygon() const { @@ -155,7 +156,7 @@ OccluderPolygon2D::~OccluderPolygon2D() { void LightOccluder2D::_poly_changed() { #ifdef DEBUG_ENABLED queue_redraw(); -#endif +#endif // DEBUG_ENABLED } void LightOccluder2D::_physics_interpolated_changed() { @@ -217,7 +218,7 @@ void LightOccluder2D::_notification(int p_what) { } } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 LightOccluder2D::_edit_get_rect() const { return occluder_polygon.is_valid() ? occluder_polygon->_edit_get_rect() : Rect2(); } @@ -225,14 +226,14 @@ Rect2 LightOccluder2D::_edit_get_rect() const { bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return occluder_polygon.is_valid() ? occluder_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false; } -#endif +#endif // DEBUG_ENABLED void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) { #ifdef DEBUG_ENABLED if (occluder_polygon.is_valid()) { occluder_polygon->disconnect_changed(callable_mp(this, &LightOccluder2D::_poly_changed)); } -#endif +#endif // DEBUG_ENABLED occluder_polygon = p_polygon; if (occluder_polygon.is_valid()) { @@ -246,7 +247,7 @@ void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polyg occluder_polygon->connect_changed(callable_mp(this, &LightOccluder2D::_poly_changed)); } queue_redraw(); -#endif +#endif // DEBUG_ENABLED } Ref<OccluderPolygon2D> LightOccluder2D::get_occluder_polygon() const { diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index 4c499d0465..b9f7eec33d 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -56,11 +56,10 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; -#endif - +#endif // DEBUG_ENABLED void set_polygon(const Vector<Vector2> &p_polygon); Vector<Vector2> get_polygon() const; @@ -93,10 +92,10 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; -#endif +#endif // DEBUG_ENABLED void set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon); Ref<OccluderPolygon2D> get_occluder_polygon() const; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index be58875e0e..734db2ea2b 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -36,7 +36,7 @@ Line2D::Line2D() { } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 Line2D::_edit_get_rect() const { if (_points.size() == 0) { return Rect2(0, 0, 0, 0); diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 1d750ca456..0156336403 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -55,7 +55,7 @@ public: LINE_TEXTURE_STRETCH }; -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; diff --git a/scene/2d/marker_2d.cpp b/scene/2d/marker_2d.cpp index b1b9705bf8..1f4ea172e9 100644 --- a/scene/2d/marker_2d.cpp +++ b/scene/2d/marker_2d.cpp @@ -61,7 +61,7 @@ void Marker2D::_draw_cross() { draw_multiline_colors(points, colors); } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 Marker2D::_edit_get_rect() const { real_t extents = get_gizmo_extents(); return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2)); @@ -70,7 +70,7 @@ Rect2 Marker2D::_edit_get_rect() const { bool Marker2D::_edit_use_rect() const { return false; } -#endif +#endif // DEBUG_ENABLED void Marker2D::_notification(int p_what) { switch (p_what) { diff --git a/scene/2d/marker_2d.h b/scene/2d/marker_2d.h index e81454797b..6a1dfb1cb2 100644 --- a/scene/2d/marker_2d.h +++ b/scene/2d/marker_2d.h @@ -45,10 +45,10 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; -#endif +#endif // DEBUG_ENABLED void set_gizmo_extents(real_t p_extents); real_t get_gizmo_extents() const; diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp index 1031a67343..9b7a89fe3b 100644 --- a/scene/2d/mesh_instance_2d.cpp +++ b/scene/2d/mesh_instance_2d.cpp @@ -88,7 +88,7 @@ Ref<Texture2D> MeshInstance2D::get_texture() const { return texture; } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 MeshInstance2D::_edit_get_rect() const { if (mesh.is_valid()) { AABB aabb = mesh->get_aabb(); @@ -101,7 +101,7 @@ Rect2 MeshInstance2D::_edit_get_rect() const { bool MeshInstance2D::_edit_use_rect() const { return mesh.is_valid(); } -#endif +#endif // DEBUG_ENABLED MeshInstance2D::MeshInstance2D() { } diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h index c914f13ade..b23b2ca907 100644 --- a/scene/2d/mesh_instance_2d.h +++ b/scene/2d/mesh_instance_2d.h @@ -45,10 +45,10 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; -#endif +#endif // DEBUG_ENABLED void set_mesh(const Ref<Mesh> &p_mesh); Ref<Mesh> get_mesh() const; diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp index 417e628517..811dccab4a 100644 --- a/scene/2d/multimesh_instance_2d.cpp +++ b/scene/2d/multimesh_instance_2d.cpp @@ -84,7 +84,7 @@ Ref<Texture2D> MultiMeshInstance2D::get_texture() const { return texture; } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 MultiMeshInstance2D::_edit_get_rect() const { if (multimesh.is_valid()) { AABB aabb = multimesh->get_aabb(); @@ -93,7 +93,7 @@ Rect2 MultiMeshInstance2D::_edit_get_rect() const { return Node2D::_edit_get_rect(); } -#endif +#endif // DEBUG_ENABLED MultiMeshInstance2D::MultiMeshInstance2D() { } diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h index 0647412294..fec27a601a 100644 --- a/scene/2d/multimesh_instance_2d.h +++ b/scene/2d/multimesh_instance_2d.h @@ -46,9 +46,9 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; -#endif +#endif // DEBUG_ENABLED void set_multimesh(const Ref<MultiMesh> &p_multimesh); Ref<MultiMesh> get_multimesh() const; diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index d0fae611d8..f030473c4b 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -253,12 +253,20 @@ void NavigationAgent2D::_notification(int p_what) { #endif // DEBUG_ENABLED } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (agent_parent) { NavigationServer2D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process()); } } break; + case NOTIFICATION_UNSUSPENDED: { + if (get_tree()->is_paused()) { + break; + } + [[fallthrough]]; + } + case NOTIFICATION_UNPAUSED: { if (agent_parent) { NavigationServer2D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process()); diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index 4961e18dc9..d90793a4c5 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -132,7 +132,7 @@ void NavigationLink2D::_notification(int p_what) { } } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 NavigationLink2D::_edit_get_rect() const { if (!is_inside_tree()) { return Rect2(); @@ -152,7 +152,7 @@ bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, segment); return p_point.distance_to(closest_point) < p_tolerance; } -#endif // TOOLS_ENABLED +#endif // DEBUG_ENABLED RID NavigationLink2D::get_rid() const { return link; diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index c724096607..232efdd0e5 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -62,10 +62,10 @@ protected: #endif // DISABLE_DEPRECATED public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; -#endif +#endif // DEBUG_ENABLED RID get_rid() const; void set_enabled(bool p_enabled); diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index 3bf90249f8..f6502a77e9 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -104,6 +104,7 @@ void NavigationObstacle2D::_notification(int p_what) { #endif // DEBUG_ENABLED } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (!can_process()) { map_before_pause = map_current; @@ -115,6 +116,13 @@ void NavigationObstacle2D::_notification(int p_what) { NavigationServer2D::get_singleton()->obstacle_set_paused(obstacle, !can_process()); } break; + case NOTIFICATION_UNSUSPENDED: { + if (get_tree()->is_paused()) { + break; + } + [[fallthrough]]; + } + case NOTIFICATION_UNPAUSED: { if (!can_process()) { map_before_pause = map_current; diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h index 30328f7086..28c5f902e6 100644 --- a/scene/2d/navigation_obstacle_2d.h +++ b/scene/2d/navigation_obstacle_2d.h @@ -84,7 +84,7 @@ public: real_t get_radius() const { return radius; } void set_vertices(const Vector<Vector2> &p_vertices); - const Vector<Vector2> &get_vertices() const { return vertices; }; + const Vector<Vector2> &get_vertices() const { return vertices; } void set_avoidance_layers(uint32_t p_layers); uint32_t get_avoidance_layers() const; @@ -96,7 +96,7 @@ public: bool get_avoidance_layer_value(int p_layer_number) const; void set_velocity(const Vector2 p_velocity); - Vector2 get_velocity() const { return velocity; }; + Vector2 get_velocity() const { return velocity; } void _avoidance_done(Vector3 p_new_velocity); // Dummy diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index f65a3c0ecc..08fbf78c33 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -142,7 +142,7 @@ RID NavigationRegion2D::get_region_rid() const { return get_rid(); } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 NavigationRegion2D::_edit_get_rect() const { return navigation_polygon.is_valid() ? navigation_polygon->_edit_get_rect() : Rect2(); } @@ -150,7 +150,7 @@ Rect2 NavigationRegion2D::_edit_get_rect() const { bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return navigation_polygon.is_valid() ? navigation_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false; } -#endif +#endif // DEBUG_ENABLED void NavigationRegion2D::_notification(int p_what) { switch (p_what) { diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index 52101cb93e..f2bed0c44f 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -75,10 +75,10 @@ protected: #endif // DISABLE_DEPRECATED public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; -#endif +#endif // DEBUG_ENABLED RID get_rid() const; void set_enabled(bool p_enabled); diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 7d645ba448..fe0cb770e0 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -37,7 +37,7 @@ #include "editor/themes/editor_scale.h" #endif -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 Path2D::_edit_get_rect() const { if (!curve.is_valid() || curve->get_point_count() == 0) { return Rect2(0, 0, 0, 0); diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index bfd5cde5e9..5162f5144a 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -48,7 +48,7 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; diff --git a/scene/2d/physics/collision_polygon_2d.cpp b/scene/2d/physics/collision_polygon_2d.cpp index b49badac1f..a3e9fcce7e 100644 --- a/scene/2d/physics/collision_polygon_2d.cpp +++ b/scene/2d/physics/collision_polygon_2d.cpp @@ -217,7 +217,7 @@ CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const { return build_mode; } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 CollisionPolygon2D::_edit_get_rect() const { return aabb; } diff --git a/scene/2d/physics/collision_polygon_2d.h b/scene/2d/physics/collision_polygon_2d.h index f1ee30babe..93920124fe 100644 --- a/scene/2d/physics/collision_polygon_2d.h +++ b/scene/2d/physics/collision_polygon_2d.h @@ -65,7 +65,7 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; diff --git a/scene/2d/physics/collision_shape_2d.h b/scene/2d/physics/collision_shape_2d.h index 65436f1539..e4f04e77e4 100644 --- a/scene/2d/physics/collision_shape_2d.h +++ b/scene/2d/physics/collision_shape_2d.h @@ -59,11 +59,11 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; #else virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; -#endif // TOOLS_ENABLED +#endif // DEBUG_ENABLED void set_shape(const Ref<Shape2D> &p_shape); Ref<Shape2D> get_shape() const; diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 42f7a75c0a..dc5a08142d 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -57,7 +57,9 @@ Point2 Polygon2D::_edit_get_pivot() const { bool Polygon2D::_edit_use_pivot() const { return true; } +#endif // TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 Polygon2D::_edit_get_rect() const { if (rect_cache_dirty) { int l = polygon.size(); @@ -88,7 +90,7 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler } return Geometry2D::is_point_in_polygon(p_point - get_offset(), polygon2d); } -#endif +#endif // DEBUG_ENABLED void Polygon2D::_validate_property(PropertyInfo &p_property) const { if (!invert && p_property.name == "invert_border") { diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index b2cd06de53..0928fa150c 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -87,11 +87,14 @@ public: virtual void _edit_set_pivot(const Point2 &p_pivot) override; virtual Point2 _edit_get_pivot() const override; virtual bool _edit_use_pivot() const override; +#endif // TOOLS_ENABLED + +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; -#endif +#endif // DEBUG_ENABLED void set_polygon(const Vector<Vector2> &p_polygon); Vector<Vector2> get_polygon() const; diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index 2b5c40f212..a182f63171 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -56,7 +56,9 @@ Point2 Sprite2D::_edit_get_pivot() const { bool Sprite2D::_edit_use_pivot() const { return true; } +#endif // TOOLS_ENABLED +#ifdef DEBUG_ENABLED bool Sprite2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return is_pixel_opaque(p_point); } @@ -68,7 +70,7 @@ Rect2 Sprite2D::_edit_get_rect() const { bool Sprite2D::_edit_use_rect() const { return texture.is_valid(); } -#endif +#endif // DEBUG_ENABLED Rect2 Sprite2D::get_anchorable_rect() const { return get_rect(); diff --git a/scene/2d/sprite_2d.h b/scene/2d/sprite_2d.h index d084e9575d..5198e5efcf 100644 --- a/scene/2d/sprite_2d.h +++ b/scene/2d/sprite_2d.h @@ -74,11 +74,14 @@ public: virtual void _edit_set_pivot(const Point2 &p_pivot) override; virtual Point2 _edit_get_pivot() const override; virtual bool _edit_use_pivot() const override; +#endif // TOOLS_ENABLED + +#ifdef DEBUG_ENABLED virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; -#endif +#endif // DEBUG_ENABLED bool is_pixel_opaque(const Point2 &p_point) const; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 45cfb8cf33..20cbbc091a 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -75,7 +75,8 @@ void TileMap::_set_tile_map_data_using_compatibility_format(int p_layer, TileMap for (int i = 0; i < c; i += offset) { const uint8_t *ptr = (const uint8_t *)&r[i]; uint8_t local[12]; - for (int j = 0; j < ((p_format >= TileMapDataFormat::TILE_MAP_DATA_FORMAT_2) ? 12 : 8); j++) { + const int buffer_size = (format == TILE_MAP_DATA_FORMAT_2) ? 12 : 8; + for (int j = 0; j < buffer_size; j++) { local[j] = ptr[j]; } @@ -89,8 +90,8 @@ void TileMap::_set_tile_map_data_using_compatibility_format(int p_layer, TileMap SWAP(local[8], local[11]); SWAP(local[9], local[10]); } -#endif - // Extracts position in TileMap. +#endif // BIG_ENDIAN_ENABLED + // Extracts position in TileMap. int16_t x = decode_uint16(&local[0]); int16_t y = decode_uint16(&local[2]); @@ -172,7 +173,7 @@ void TileMap::_notification(int p_what) { bool in_editor = false; #ifdef TOOLS_ENABLED in_editor = Engine::get_singleton()->is_editor_hint(); -#endif +#endif // TOOLS_ENABLED if (is_inside_tree() && collision_animatable && !in_editor) { // Update transform on the physics tick when in animatable mode. last_valid_transform = new_transform; @@ -188,7 +189,7 @@ void TileMap::_notification(int p_what) { bool in_editor = false; #ifdef TOOLS_ENABLED in_editor = Engine::get_singleton()->is_editor_hint(); -#endif +#endif // TOOLS_ENABLED if (is_inside_tree() && collision_animatable && !in_editor) { // Store last valid transform. @@ -209,7 +210,7 @@ void TileMap::force_update(int p_layer) { notify_runtime_tile_data_update(p_layer); update_internals(); } -#endif +#endif // DISABLE_DEPRECATED void TileMap::set_rendering_quadrant_size(int p_size) { ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); @@ -649,7 +650,7 @@ void TileMap::notify_runtime_tile_data_update(int p_layer) { } } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 TileMap::_edit_get_rect() const { // Return the visible rect of the tilemap. if (layers.is_empty()) { @@ -667,7 +668,7 @@ Rect2 TileMap::_edit_get_rect() const { const_cast<TileMap *>(this)->item_rect_changed(any_changed); return rect; } -#endif +#endif // DEBUG_ENABLED bool TileMap::_set(const StringName &p_name, const Variant &p_value) { int index; @@ -726,7 +727,7 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_rendering_quadrant_size(); return true; } -#endif +#endif // DISABLE_DEPRECATED else { return property_helper.property_get_value(sname, r_ret); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 142dc1193f..47f5c8d996 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -107,16 +107,16 @@ protected: VisibilityMode _get_navigation_visibility_mode_bind_compat_87115(); static void _bind_compatibility_methods(); -#endif +#endif // DISABLE_DEPRECATED public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; -#endif +#endif // DEBUG_ENABLED #ifndef DISABLE_DEPRECATED void force_update(int p_layer); -#endif +#endif // DISABLE_DEPRECATED void set_rendering_quadrant_size(int p_size); int get_rendering_quadrant_size() const; diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index ebb03e4e73..c4a2f35d31 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -823,6 +823,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) { Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer); uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer); uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer); + real_t physics_priority = tile_set->get_physics_layer_collision_priority(tile_set_physics_layer); RID body = r_cell_data.bodies[tile_set_physics_layer]; if (tile_data->get_collision_polygons_count(tile_set_physics_layer) == 0) { @@ -849,6 +850,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) { ps->body_attach_object_instance_id(body, tile_map_node ? tile_map_node->get_instance_id() : get_instance_id()); ps->body_set_collision_layer(body, physics_layer); ps->body_set_collision_mask(body, physics_mask); + ps->body_set_collision_priority(body, physics_priority); ps->body_set_pickable(body, false); ps->body_set_state(body, PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, tile_data->get_constant_linear_velocity(tile_set_physics_layer)); ps->body_set_state(body, PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, tile_data->get_constant_angular_velocity(tile_set_physics_layer)); @@ -946,7 +948,7 @@ void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vect rs->canvas_item_add_set_transform(p_canvas_item, Transform2D()); } } -}; +} #endif // DEBUG_ENABLED /////////////////////////////// Navigation ////////////////////////////////////// @@ -1458,6 +1460,24 @@ void TileMapLayer::_clear_runtime_update_tile_data_for_cell(CellData &r_cell_dat } } +void TileMapLayer::_update_cells_callback(bool p_force_cleanup) { + if (!GDVIRTUAL_IS_OVERRIDDEN(_update_cells)) { + return; + } + + // Check if we should cleanup everything. + bool forced_cleanup = p_force_cleanup || !enabled || tile_set.is_null() || !is_visible_in_tree(); + + // List all the dirty cell's positions to notify script of cell updates. + TypedArray<Vector2i> dirty_cell_positions; + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + dirty_cell_positions.push_back(cell_data.coords); + } + + GDVIRTUAL_CALL(_update_cells, dirty_cell_positions, forced_cleanup); +} + TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) const { if (tile_set.is_null()) { return TileSet::TerrainsPattern(); @@ -1671,6 +1691,10 @@ void TileMapLayer::_internal_update(bool p_force_cleanup) { // This may add cells to the dirty list if a runtime modification has been notified. _build_runtime_update_tile_data(p_force_cleanup); + // Callback for implementing custom subsystems. + // This may add to the dirty list if some cells are changed inside _update_cells. + _update_cells_callback(p_force_cleanup); + // Update all subsystems. _rendering_update(p_force_cleanup); _physics_update(p_force_cleanup); @@ -1859,6 +1883,7 @@ void TileMapLayer::_bind_methods() { GDVIRTUAL_BIND(_use_tile_data_runtime_update, "coords"); GDVIRTUAL_BIND(_tile_data_runtime_update, "coords", "tile_data"); + GDVIRTUAL_BIND(_update_cells, "coords", "forced_cleanup"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "tile_map_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_tile_map_data_from_array", "get_tile_map_data_as_array"); diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index 6cb481849c..912fa2be15 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -321,6 +321,7 @@ private: bool _runtime_update_needs_all_cells_cleaned_up = false; void _clear_runtime_update_tile_data(); void _clear_runtime_update_tile_data_for_cell(CellData &r_cell_data); + void _update_cells_callback(bool p_force_cleanup); // Per-system methods. #ifdef DEBUG_ENABLED @@ -462,6 +463,7 @@ public: void notify_runtime_tile_data_update(); GDVIRTUAL1R(bool, _use_tile_data_runtime_update, Vector2i); GDVIRTUAL2(_tile_data_runtime_update, Vector2i, TileData *); + GDVIRTUAL2(_update_cells, TypedArray<Vector2i>, bool); // --- Shortcuts to methods defined in TileSet --- Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern); diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index ff409272c5..220608a250 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -185,6 +185,7 @@ void TouchScreenButton::_notification(int p_what) { } } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (is_pressed()) { _release(); @@ -329,7 +330,7 @@ void TouchScreenButton::_release(bool p_exiting_tree) { } } -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 TouchScreenButton::_edit_get_rect() const { if (texture_normal.is_null()) { return CanvasItem::_edit_get_rect(); @@ -341,7 +342,7 @@ Rect2 TouchScreenButton::_edit_get_rect() const { bool TouchScreenButton::_edit_use_rect() const { return !texture_normal.is_null(); } -#endif +#endif // DEBUG_ENABLED Rect2 TouchScreenButton::get_anchorable_rect() const { if (texture_normal.is_null()) { @@ -429,6 +430,6 @@ void TouchScreenButton::_bind_methods() { } TouchScreenButton::TouchScreenButton() { - unit_rect = Ref<RectangleShape2D>(memnew(RectangleShape2D)); + unit_rect.instantiate(); unit_rect->set_size(Vector2(1, 1)); } diff --git a/scene/2d/touch_screen_button.h b/scene/2d/touch_screen_button.h index 4467604e2b..da75fabfa0 100644 --- a/scene/2d/touch_screen_button.h +++ b/scene/2d/touch_screen_button.h @@ -76,10 +76,10 @@ protected: #endif // DISABLE_DEPRECATED public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; -#endif +#endif // DEBUG_ENABLED void set_texture_normal(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture_normal() const; diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp index c64507fe32..7bf6371ee9 100644 --- a/scene/2d/visible_on_screen_notifier_2d.cpp +++ b/scene/2d/visible_on_screen_notifier_2d.cpp @@ -30,7 +30,7 @@ #include "visible_on_screen_notifier_2d.h" -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 VisibleOnScreenNotifier2D::_edit_get_rect() const { return rect; } @@ -38,7 +38,7 @@ Rect2 VisibleOnScreenNotifier2D::_edit_get_rect() const { bool VisibleOnScreenNotifier2D::_edit_use_rect() const { return true; } -#endif +#endif // DEBUG_ENABLED void VisibleOnScreenNotifier2D::_visibility_enter() { if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) { diff --git a/scene/2d/visible_on_screen_notifier_2d.h b/scene/2d/visible_on_screen_notifier_2d.h index df2832a976..84545a2d6a 100644 --- a/scene/2d/visible_on_screen_notifier_2d.h +++ b/scene/2d/visible_on_screen_notifier_2d.h @@ -54,10 +54,10 @@ protected: static void _bind_methods(); public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED virtual Rect2 _edit_get_rect() const override; virtual bool _edit_use_rect() const override; -#endif +#endif // DEBUG_ENABLED void set_rect(const Rect2 &p_rect); Rect2 get_rect() const; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index c70fa3ca2e..2b841c4e0d 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -234,6 +234,7 @@ void Camera3D::_notification(int p_what) { } } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (is_physics_interpolated_and_enabled() && is_inside_tree() && is_visible_in_tree()) { _physics_interpolation_ensure_transform_calculated(true); @@ -377,7 +378,7 @@ void Camera3D::set_projection(ProjectionType p_mode) { RID Camera3D::get_camera() const { return camera; -}; +} void Camera3D::make_current() { current = true; @@ -423,7 +424,7 @@ bool Camera3D::is_current() const { Vector3 Camera3D::project_ray_normal(const Point2 &p_pos) const { Vector3 ray = project_local_ray_normal(p_pos); return get_camera_transform().basis.xform(ray).normalized(); -}; +} Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const { ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene."); @@ -441,7 +442,7 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const { } return ray; -}; +} Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene."); @@ -470,7 +471,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { } else { return get_camera_transform().origin; }; -}; +} bool Camera3D::is_position_behind(const Vector3 &p_pos) const { Transform3D t = get_global_transform(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 2cef607d29..b48a3a87c7 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -511,6 +511,8 @@ void GPUParticles3D::_notification(int p_what) { RS::get_singleton()->particles_set_subemitter(particles, RID()); } break; + case NOTIFICATION_SUSPENDED: + case NOTIFICATION_UNSUSPENDED: case NOTIFICATION_PAUSED: case NOTIFICATION_UNPAUSED: { if (is_inside_tree()) { diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 6b3510a72a..01f15137be 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -318,7 +318,7 @@ Ref<TriangleMesh> Label3D::generate_triangle_mesh() const { facesw[j] = vtx; } - triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh.instantiate(); triangle_mesh->create(faces); return triangle_mesh; diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 012ef7860d..c658bfd799 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -98,15 +98,15 @@ Array LightmapGIData::_get_user_data() const { } void LightmapGIData::set_lightmap_textures(const TypedArray<TextureLayered> &p_data) { - light_textures = p_data; + storage_light_textures = p_data; if (p_data.is_empty()) { - light_texture = Ref<TextureLayered>(); + combined_light_texture = Ref<TextureLayered>(); _reset_lightmap_textures(); return; } if (p_data.size() == 1) { - light_texture = p_data[0]; + combined_light_texture = p_data[0]; } else { Vector<Ref<Image>> images; for (int i = 0; i < p_data.size(); i++) { @@ -121,13 +121,13 @@ void LightmapGIData::set_lightmap_textures(const TypedArray<TextureLayered> &p_d combined_texture.instantiate(); combined_texture->create_from_images(images); - light_texture = combined_texture; + combined_light_texture = combined_texture; } _reset_lightmap_textures(); } TypedArray<TextureLayered> LightmapGIData::get_lightmap_textures() const { - return light_textures; + return storage_light_textures; } RID LightmapGIData::get_rid() const { @@ -139,7 +139,7 @@ void LightmapGIData::clear() { } void LightmapGIData::_reset_lightmap_textures() { - RS::get_singleton()->lightmap_set_textures(lightmap, light_texture.is_valid() ? light_texture->get_rid() : RID(), uses_spherical_harmonics); + RS::get_singleton()->lightmap_set_textures(lightmap, combined_light_texture.is_valid() ? combined_light_texture->get_rid() : RID(), uses_spherical_harmonics); } void LightmapGIData::set_uses_spherical_harmonics(bool p_enable) { @@ -238,10 +238,10 @@ void LightmapGIData::set_light_texture(const Ref<TextureLayered> &p_light_textur } Ref<TextureLayered> LightmapGIData::get_light_texture() const { - if (light_textures.is_empty()) { + if (storage_light_textures.is_empty()) { return Ref<TextureLayered>(); } - return light_textures.get(0); + return storage_light_textures.get(0); } void LightmapGIData::_set_light_textures_data(const Array &p_data) { @@ -249,7 +249,7 @@ void LightmapGIData::_set_light_textures_data(const Array &p_data) { } Array LightmapGIData::_get_light_textures_data() const { - return Array(light_textures); + return Array(storage_light_textures); } #endif @@ -274,7 +274,7 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_probe_data", "data"), &LightmapGIData::_set_probe_data); ClassDB::bind_method(D_METHOD("_get_probe_data"), &LightmapGIData::_get_probe_data); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "lightmap_textures", PROPERTY_HINT_ARRAY_TYPE, "TextureLayered", PROPERTY_USAGE_NO_EDITOR), "set_lightmap_textures", "get_lightmap_textures"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "lightmap_textures", PROPERTY_HINT_ARRAY_TYPE, "TextureLayered", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY), "set_lightmap_textures", "get_lightmap_textures"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data"); @@ -287,8 +287,8 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_light_textures_data", "data"), &LightmapGIData::_set_light_textures_data); ClassDB::bind_method(D_METHOD("_get_light_textures_data"), &LightmapGIData::_get_light_textures_data); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_EDITOR), "set_light_texture", "get_light_texture"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_NONE), "set_light_texture", "get_light_texture"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data"); #endif } @@ -740,6 +740,74 @@ void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, f } } +LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref<Lightmapper> p_lightmapper, const String &p_base_name, TypedArray<TextureLayered> &r_textures, bool p_compress) const { + Vector<Ref<Image>> images; + images.resize(p_lightmapper->get_bake_texture_count()); + + for (int i = 0; i < images.size(); i++) { + images.set(i, p_lightmapper->get_bake_texture(i)); + } + + const int slice_count = images.size(); + const int slice_width = images[0]->get_width(); + const int slice_height = images[0]->get_height(); + + const int slices_per_texture = Image::MAX_HEIGHT / slice_height; + const int texture_count = Math::ceil(slice_count / (float)slices_per_texture); + const int last_count = slice_count % slices_per_texture; + + r_textures.resize(texture_count); + + for (int i = 0; i < texture_count; i++) { + const int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture; + + Ref<Image> texture_image = Image::create_empty(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); + + for (int j = 0; j < texture_slice_count; j++) { + texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); + } + + const String atlas_path = (texture_count > 1 ? p_base_name + "_" + itos(i) : p_base_name) + ".exr"; + const String config_path = atlas_path + ".import"; + + Ref<ConfigFile> config; + config.instantiate(); + + // Load an import configuration if present. + if (FileAccess::exists(config_path)) { + config->load(config_path); + } + + config->set_value("remap", "importer", "2d_array_texture"); + config->set_value("remap", "type", "CompressedTexture2DArray"); + if (!config->has_section_key("params", "compress/mode")) { + // Do not override an existing compression mode. + config->set_value("params", "compress/mode", p_compress ? 2 : 3); + } + config->set_value("params", "compress/channel_pack", 1); + config->set_value("params", "mipmaps/generate", false); + config->set_value("params", "slices/horizontal", 1); + config->set_value("params", "slices/vertical", texture_slice_count); + + config->save(config_path); + + // Save the file. + Error save_err = texture_image->save_exr(atlas_path, false); + + ERR_FAIL_COND_V(save_err, LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE); + + // Reimport the file. + ResourceLoader::import(atlas_path); + Ref<TextureLayered> t = ResourceLoader::load(atlas_path); // If already loaded, it will be updated on refocus? + ERR_FAIL_COND_V(t.is_null(), LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE); + + // Store the atlas in the array. + r_textures[i] = t; + } + + return LightmapGI::BAKE_ERROR_OK; +} + LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_path, Lightmapper::BakeStepFunc p_bake_step, void *p_bake_userdata) { if (p_image_data_path.is_empty()) { if (get_light_data().is_null()) { @@ -1127,80 +1195,30 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa // POSTBAKE: Save Textures. - TypedArray<TextureLayered> textures; - { - Vector<Ref<Image>> images; - images.resize(lightmapper->get_bake_texture_count()); - for (int i = 0; i < images.size(); i++) { - images.set(i, lightmapper->get_bake_texture(i)); - } - - int slice_count = images.size(); - int slice_width = images[0]->get_width(); - int slice_height = images[0]->get_height(); - - int slices_per_texture = Image::MAX_HEIGHT / slice_height; - int texture_count = Math::ceil(slice_count / (float)slices_per_texture); - - textures.resize(texture_count); - - String base_path = p_image_data_path.get_basename(); - - int last_count = slice_count % slices_per_texture; - for (int i = 0; i < texture_count; i++) { - int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture; - - Ref<Image> texture_image = Image::create_empty(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); - - for (int j = 0; j < texture_slice_count; j++) { - texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); - } - - String texture_path = texture_count > 1 ? base_path + "_" + itos(i) + ".exr" : base_path + ".exr"; + TypedArray<TextureLayered> lightmap_textures; - Ref<ConfigFile> config; - config.instantiate(); + const String texture_filename = p_image_data_path.get_basename(); - if (FileAccess::exists(texture_path + ".import")) { - config->load(texture_path + ".import"); - } + // Save the lightmap atlases. + BakeError save_err = _save_and_reimport_atlas_textures(lightmapper, texture_filename, lightmap_textures, false); + ERR_FAIL_COND_V(save_err != BAKE_ERROR_OK, save_err); - config->set_value("remap", "importer", "2d_array_texture"); - config->set_value("remap", "type", "CompressedTexture2DArray"); - if (!config->has_section_key("params", "compress/mode")) { - // User may want another compression, so leave it be, but default to VRAM uncompressed. - config->set_value("params", "compress/mode", 3); - } - config->set_value("params", "compress/channel_pack", 1); - config->set_value("params", "mipmaps/generate", false); - config->set_value("params", "slices/horizontal", 1); - config->set_value("params", "slices/vertical", texture_slice_count); - - config->save(texture_path + ".import"); - - Error err = texture_image->save_exr(texture_path, false); - ERR_FAIL_COND_V(err, BAKE_ERROR_CANT_CREATE_IMAGE); - ResourceLoader::import(texture_path); - Ref<TextureLayered> t = ResourceLoader::load(texture_path); // If already loaded, it will be updated on refocus? - ERR_FAIL_COND_V(t.is_null(), BAKE_ERROR_CANT_CREATE_IMAGE); - textures[i] = t; - } - } - - /* POSTBAKE: Save Light Data */ + // POSTBAKE: Save Light Data. Ref<LightmapGIData> gi_data; + if (get_light_data().is_valid()) { gi_data = get_light_data(); - set_light_data(Ref<LightmapGIData>()); //clear + set_light_data(Ref<LightmapGIData>()); // Clear. gi_data->clear(); + } else { gi_data.instantiate(); } - gi_data->set_lightmap_textures(textures); - gi_data->_set_uses_packed_directional(directional); // New SH lightmaps are packed automatically. + gi_data->set_lightmap_textures(lightmap_textures); gi_data->set_uses_spherical_harmonics(directional); + gi_data->_set_uses_packed_directional(directional); // New SH lightmaps are packed automatically. for (int i = 0; i < lightmapper->get_bake_mesh_count(); i++) { Dictionary d = lightmapper->get_bake_mesh_userdata(i); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 6377c420d1..0476061c60 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -43,8 +43,12 @@ class LightmapGIData : public Resource { GDCLASS(LightmapGIData, Resource); RES_BASE_EXTENSION("lmbake") - Ref<TextureLayered> light_texture; - TypedArray<TextureLayered> light_textures; + // The 'merged' texture atlases actually used by the renderer. + Ref<TextureLayered> combined_light_texture; + + // The temporary texture atlas arrays which are used for storage. + // If a single atlas is too large, it's split and recombined during loading. + TypedArray<TextureLayered> storage_light_textures; bool uses_spherical_harmonics = false; bool interior = false; @@ -245,6 +249,8 @@ private: void _plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cell_size, const Vector3 *p_triangle); void _gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool> &positions_used, const AABB &p_bounds); + BakeError _save_and_reimport_atlas_textures(const Ref<Lightmapper> p_lightmapper, const String &p_base_name, TypedArray<TextureLayered> &r_textures, bool p_compress = false) const; + protected: void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 5bbb724e2f..faf138896a 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -272,12 +272,20 @@ void NavigationAgent3D::_notification(int p_what) { #endif // DEBUG_ENABLED } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (agent_parent) { NavigationServer3D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process()); } } break; + case NOTIFICATION_UNSUSPENDED: { + if (get_tree()->is_paused()) { + break; + } + [[fallthrough]]; + } + case NOTIFICATION_UNPAUSED: { if (agent_parent) { NavigationServer3D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process()); @@ -1073,8 +1081,8 @@ void NavigationAgent3D::_update_debug_path() { debug_path_instance = RenderingServer::get_singleton()->instance_create(); } - if (!debug_path_mesh.is_valid()) { - debug_path_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_path_mesh.is_null()) { + debug_path_mesh.instantiate(); } debug_path_mesh->clear_surfaces(); diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index 0cce21b9d0..9e29384fc9 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -56,8 +56,8 @@ void NavigationLink3D::_update_debug_mesh() { debug_instance = RenderingServer::get_singleton()->instance_create(); } - if (!debug_mesh.is_valid()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_mesh.is_null()) { + debug_mesh.instantiate(); } RID nav_map = get_world_3d()->get_navigation_map(); diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index f2ac8f789c..0735d5dde5 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -119,6 +119,7 @@ void NavigationObstacle3D::_notification(int p_what) { #endif // DEBUG_ENABLED } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (!can_process()) { map_before_pause = map_current; @@ -130,6 +131,13 @@ void NavigationObstacle3D::_notification(int p_what) { NavigationServer3D::get_singleton()->obstacle_set_paused(obstacle, !can_process()); } break; + case NOTIFICATION_UNSUSPENDED: { + if (get_tree()->is_paused()) { + break; + } + [[fallthrough]]; + } + case NOTIFICATION_UNPAUSED: { if (!can_process()) { map_before_pause = map_current; @@ -168,13 +176,19 @@ void NavigationObstacle3D::_notification(int p_what) { } #ifdef DEBUG_ENABLED if (fake_agent_radius_debug_instance.is_valid() && radius > 0.0) { - Transform3D debug_transform; - debug_transform.origin = get_global_position(); + // Prevent non-positive scaling. + const Vector3 safe_scale = get_global_basis().get_scale().abs().maxf(0.001); + // Agent radius is a scalar value and does not support non-uniform scaling, choose the largest axis. + const float scaling_max_value = safe_scale[safe_scale.max_axis_index()]; + const Vector3 uniform_max_scale = Vector3(scaling_max_value, scaling_max_value, scaling_max_value); + const Transform3D debug_transform = Transform3D(Basis().scaled(uniform_max_scale), get_global_position()); RS::get_singleton()->instance_set_transform(fake_agent_radius_debug_instance, debug_transform); } if (static_obstacle_debug_instance.is_valid() && get_vertices().size() > 0) { - Transform3D debug_transform; - debug_transform.origin = get_global_position(); + // Prevent non-positive scaling. + const Vector3 safe_scale = get_global_basis().get_scale().abs().maxf(0.001); + // Obstacles are projected to the xz-plane, so only rotation around the y-axis can be taken into account. + const Transform3D debug_transform = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), get_global_rotation().y), get_global_position()); RS::get_singleton()->instance_set_transform(static_obstacle_debug_instance, debug_transform); } #endif // DEBUG_ENABLED @@ -346,6 +360,25 @@ bool NavigationObstacle3D::get_carve_navigation_mesh() const { return carve_navigation_mesh; } +PackedStringArray NavigationObstacle3D::get_configuration_warnings() const { + PackedStringArray warnings = Node3D::get_configuration_warnings(); + + if (get_global_rotation().x != 0.0 || get_global_rotation().z != 0.0) { + warnings.push_back(RTR("NavigationObstacle3D only takes global rotation around the y-axis into account. Rotations around the x-axis or z-axis might lead to unexpected results.")); + } + + const Vector3 global_scale = get_global_basis().get_scale(); + if (global_scale.x < 0.001 || global_scale.y < 0.001 || global_scale.z < 0.001) { + warnings.push_back(RTR("NavigationObstacle3D does not support negative or zero scaling.")); + } + + if (radius > 0.0 && !get_global_basis().is_conformal()) { + warnings.push_back(RTR("The agent radius can only be scaled uniformly. The largest scale value along the three axes will be used.")); + } + + return warnings; +} + void NavigationObstacle3D::_update_map(RID p_map) { NavigationServer3D::get_singleton()->obstacle_set_map(obstacle, p_map); map_current = p_map; @@ -381,8 +414,8 @@ void NavigationObstacle3D::_update_fake_agent_radius_debug() { if (!fake_agent_radius_debug_instance.is_valid()) { fake_agent_radius_debug_instance = RenderingServer::get_singleton()->instance_create(); } - if (!fake_agent_radius_debug_mesh.is_valid()) { - fake_agent_radius_debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (fake_agent_radius_debug_mesh.is_null()) { + fake_agent_radius_debug_mesh.instantiate(); } fake_agent_radius_debug_mesh->clear_surfaces(); @@ -479,8 +512,8 @@ void NavigationObstacle3D::_update_static_obstacle_debug() { if (!static_obstacle_debug_instance.is_valid()) { static_obstacle_debug_instance = RenderingServer::get_singleton()->instance_create(); } - if (!static_obstacle_debug_mesh.is_valid()) { - static_obstacle_debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (static_obstacle_debug_mesh.is_null()) { + static_obstacle_debug_mesh.instantiate(); } static_obstacle_debug_mesh->clear_surfaces(); diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index e9a4669fa2..111abad842 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -95,7 +95,7 @@ public: real_t get_height() const { return height; } void set_vertices(const Vector<Vector3> &p_vertices); - const Vector<Vector3> &get_vertices() const { return vertices; }; + const Vector<Vector3> &get_vertices() const { return vertices; } void set_avoidance_layers(uint32_t p_layers); uint32_t get_avoidance_layers() const; @@ -107,7 +107,7 @@ public: bool get_use_3d_avoidance() const { return use_3d_avoidance; } void set_velocity(const Vector3 p_velocity); - Vector3 get_velocity() const { return velocity; }; + Vector3 get_velocity() const { return velocity; } void _avoidance_done(Vector3 p_new_velocity); // Dummy @@ -117,6 +117,8 @@ public: void set_carve_navigation_mesh(bool p_enabled); bool get_carve_navigation_mesh() const; + PackedStringArray get_configuration_warnings() const override; + private: void _update_map(RID p_map); void _update_position(const Vector3 p_position); diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index c0c254e7ed..2d67e4334e 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -492,8 +492,8 @@ void NavigationRegion3D::_update_debug_mesh() { debug_instance = RenderingServer::get_singleton()->instance_create(); } - if (!debug_mesh.is_valid()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_mesh.is_null()) { + debug_mesh.instantiate(); } debug_mesh->clear_surfaces(); @@ -669,8 +669,8 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() { debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create(); } - if (!debug_edge_connections_mesh.is_valid()) { - debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_edge_connections_mesh.is_null()) { + debug_edge_connections_mesh.instantiate(); } debug_edge_connections_mesh->clear_surfaces(); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 217ee28cf1..20288fad3a 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -233,8 +233,8 @@ public: #ifdef TOOLS_ENABLED virtual Transform3D get_global_gizmo_transform() const; virtual Transform3D get_local_gizmo_transform() const; - virtual void set_transform_gizmo_visible(bool p_enabled) { data.transform_gizmo_visible = p_enabled; }; - virtual bool is_transform_gizmo_visible() const { return data.transform_gizmo_visible; }; + virtual void set_transform_gizmo_visible(bool p_enabled) { data.transform_gizmo_visible = p_enabled; } + virtual bool is_transform_gizmo_visible() const { return data.transform_gizmo_visible; } #endif virtual void reparent(Node *p_parent, bool p_keep_global_transform = true) override; diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 64259a24b0..00c83101b9 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -88,8 +88,8 @@ void Path3D::_update_debug_mesh() { return; } - if (!debug_mesh.is_valid()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_mesh.is_null()) { + debug_mesh.instantiate(); } if (!(curve.is_valid())) { diff --git a/scene/3d/physics/ray_cast_3d.cpp b/scene/3d/physics/ray_cast_3d.cpp index a9272388c1..b9159f072b 100644 --- a/scene/3d/physics/ray_cast_3d.cpp +++ b/scene/3d/physics/ray_cast_3d.cpp @@ -464,7 +464,7 @@ void RayCast3D::_create_debug_shape() { } if (debug_mesh.is_null()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_mesh.instantiate(); } } diff --git a/scene/3d/physics/shape_cast_3d.cpp b/scene/3d/physics/shape_cast_3d.cpp index 8ad651fdf5..19c74bc925 100644 --- a/scene/3d/physics/shape_cast_3d.cpp +++ b/scene/3d/physics/shape_cast_3d.cpp @@ -546,7 +546,7 @@ void ShapeCast3D::_create_debug_shape() { } if (debug_mesh.is_null()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_mesh.instantiate(); } } diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index c1c992588b..abece303d1 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -183,17 +183,17 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { for (uint32_t i = 0; i < bones.size(); i++) { - const String prep = vformat("%s/%d/", PNAME("bones"), i); - p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + PNAME("rest"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::BOOL, prep + PNAME("enabled"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("position"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + PNAME("rotation"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + const String prep = vformat("%s/%d/", "bones", i); + p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); for (const KeyValue<StringName, Variant> &K : bones[i].metadata) { - PropertyInfo pi = PropertyInfo(bones[i].metadata[K.key].get_type(), prep + PNAME("bone_meta/") + K.key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); + PropertyInfo pi = PropertyInfo(bones[i].metadata[K.key].get_type(), prep + "bone_meta/" + K.key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR); p_list->push_back(pi); } } diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index ecfe095f1d..90902f71e2 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -162,7 +162,7 @@ private: Vector<int> parentless_bones; AHashMap<String, int> name_to_bone_index; - mutable StringName concatenated_bone_names = StringName(); + mutable StringName concatenated_bone_names; void _update_bone_names() const; void _make_dirty(); diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 5d6020194e..94145a6915 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -131,7 +131,7 @@ class SkeletonIK3D : public SkeletonModifier3D { real_t min_distance = 0.01; int max_iterations = 10; - Variant target_node_override_ref = Variant(); + Variant target_node_override_ref; FabrikInverseKinematic::Task *task = nullptr; #ifndef DISABLE_DEPRECATED diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 42460eec4c..6e3ada83ad 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -470,7 +470,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { facesw[j] = vtx; } - triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh.instantiate(); triangle_mesh->create(faces); return triangle_mesh; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 214c1f77ca..9869b241d3 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -89,7 +89,7 @@ PackedStringArray XRCamera3D::get_configuration_warnings() const { } return warnings; -}; +} Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { // get our XRServer @@ -114,7 +114,7 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized(); return ray; -}; +} Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { // get our XRServer @@ -144,7 +144,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y; return res; -}; +} Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) const { // get our XRServer @@ -174,7 +174,7 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) co Vector3 p(point.x, point.y, -p_z_depth); return get_camera_transform().xform(p); -}; +} Vector<Plane> XRCamera3D::get_frustum() const { // get our XRServer @@ -193,7 +193,7 @@ Vector<Plane> XRCamera3D::get_frustum() const { // TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here. Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); return cm.get_projection_planes(get_camera_transform()); -}; +} XRCamera3D::XRCamera3D() { XRServer *xr_server = XRServer::get_singleton(); @@ -240,7 +240,7 @@ void XRNode3D::_bind_methods() { ClassDB::bind_method(D_METHOD("trigger_haptic_pulse", "action_name", "frequency", "amplitude", "duration_sec", "delay_sec"), &XRNode3D::trigger_haptic_pulse); ADD_SIGNAL(MethodInfo("tracking_changed", PropertyInfo(Variant::BOOL, "tracking"))); -}; +} void XRNode3D::_validate_property(PropertyInfo &p_property) const { XRServer *xr_server = XRServer::get_singleton(); @@ -499,7 +499,7 @@ void XRController3D::_bind_methods() { ADD_SIGNAL(MethodInfo("input_float_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value"))); ADD_SIGNAL(MethodInfo("input_vector2_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::VECTOR2, "value"))); ADD_SIGNAL(MethodInfo("profile_changed", PropertyInfo(Variant::STRING, "role"))); -}; +} void XRController3D::_bind_tracker() { XRNode3D::_bind_tracker(); diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 8ee80f29ee..eb25a8db1b 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -186,7 +186,7 @@ protected: GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName) GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName) GDVIRTUAL1RC(bool, _is_parameter_read_only, StringName) - GDVIRTUAL4RC(double, _process, double, bool, bool, bool) + GDVIRTUAL4R(double, _process, double, bool, bool, bool) GDVIRTUAL0RC(String, _get_caption) GDVIRTUAL0RC(bool, _has_filter) diff --git a/scene/audio/audio_stream_player_internal.cpp b/scene/audio/audio_stream_player_internal.cpp index 7d1ed56ca8..621aa31fc6 100644 --- a/scene/audio/audio_stream_player_internal.cpp +++ b/scene/audio/audio_stream_player_internal.cpp @@ -112,6 +112,7 @@ void AudioStreamPlayerInternal::notification(int p_what) { stream_playbacks.clear(); } break; + case Node::NOTIFICATION_SUSPENDED: case Node::NOTIFICATION_PAUSED: { if (!node->can_process()) { // Node can't process so we start fading out to silence @@ -119,6 +120,13 @@ void AudioStreamPlayerInternal::notification(int p_what) { } } break; + case Node::NOTIFICATION_UNSUSPENDED: { + if (node->get_tree()->is_paused()) { + break; + } + [[fallthrough]]; + } + case Node::NOTIFICATION_UNPAUSED: { set_stream_paused(false); } break; diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 22e5238fae..377bc0eefe 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -30,21 +30,36 @@ #include "scene_debugger.h" +#include "core/debugger/debugger_marshalls.h" #include "core/debugger/engine_debugger.h" -#include "core/debugger/engine_profiler.h" #include "core/io/marshalls.h" #include "core/object/script_language.h" #include "core/templates/local_vector.h" +#include "scene/2d/physics/collision_object_2d.h" +#include "scene/2d/physics/collision_polygon_2d.h" +#include "scene/2d/physics/collision_shape_2d.h" +#ifndef _3D_DISABLED +#include "scene/3d/label_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/physics/collision_object_3d.h" +#include "scene/3d/physics/collision_shape_3d.h" +#include "scene/3d/sprite_3d.h" +#include "scene/resources/surface_tool.h" +#endif // _3D_DISABLED +#include "scene/gui/popup_menu.h" +#include "scene/main/canvas_layer.h" #include "scene/main/scene_tree.h" #include "scene/main/window.h" #include "scene/resources/packed_scene.h" - -SceneDebugger *SceneDebugger::singleton = nullptr; +#include "scene/theme/theme_db.h" SceneDebugger::SceneDebugger() { singleton = this; + #ifdef DEBUG_ENABLED LiveEditor::singleton = memnew(LiveEditor); + RuntimeNodeSelect::singleton = memnew(RuntimeNodeSelect); + EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message)); #endif } @@ -56,7 +71,13 @@ SceneDebugger::~SceneDebugger() { memdelete(LiveEditor::singleton); LiveEditor::singleton = nullptr; } + + if (RuntimeNodeSelect::singleton) { + memdelete(RuntimeNodeSelect::singleton); + RuntimeNodeSelect::singleton = nullptr; + } #endif + singleton = nullptr; } @@ -73,18 +94,33 @@ void SceneDebugger::deinitialize() { } #ifdef DEBUG_ENABLED +void SceneDebugger::_handle_input(const Ref<InputEvent> &p_event, const Ref<Shortcut> &p_shortcut) { + Ref<InputEventKey> k = p_event; + if (k.is_valid() && k->is_pressed() && !k->is_echo() && p_shortcut->matches_event(k)) { + EngineDebugger::get_singleton()->send_message("request_quit", Array()); + } +} + Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) { SceneTree *scene_tree = SceneTree::get_singleton(); if (!scene_tree) { return ERR_UNCONFIGURED; } + LiveEditor *live_editor = LiveEditor::get_singleton(); if (!live_editor) { return ERR_UNCONFIGURED; } + RuntimeNodeSelect *runtime_node_select = RuntimeNodeSelect::get_singleton(); + if (!runtime_node_select) { + return ERR_UNCONFIGURED; + } r_captured = true; - if (p_msg == "request_scene_tree") { // Scene tree + if (p_msg == "setup_scene") { + SceneTree::get_singleton()->get_root()->connect(SceneStringName(window_input), callable_mp_static(SceneDebugger::_handle_input).bind(DebuggerMarshalls::deserialize_key_shortcut(p_args))); + + } else if (p_msg == "request_scene_tree") { // Scene tree live_editor->_send_tree(); } else if (p_msg == "save_node") { // Save node. @@ -99,22 +135,33 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra ObjectID id = p_args[0]; _send_object_id(id); - } else if (p_msg == "override_camera_2D:set") { // Camera + } else if (p_msg == "suspend_changed") { ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); - bool enforce = p_args[0]; - scene_tree->get_root()->enable_canvas_transform_override(enforce); + bool suspended = p_args[0]; + scene_tree->set_suspend(suspended); + runtime_node_select->_update_input_state(); - } else if (p_msg == "override_camera_2D:transform") { - ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); - Transform2D transform = p_args[0]; - scene_tree->get_root()->set_canvas_transform_override(transform); -#ifndef _3D_DISABLED - } else if (p_msg == "override_camera_3D:set") { + } else if (p_msg == "next_frame") { + _next_frame(); + + } else if (p_msg == "override_cameras") { // Camera ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); bool enable = p_args[0]; + bool from_editor = p_args[1]; + scene_tree->get_root()->enable_canvas_transform_override(enable); +#ifndef _3D_DISABLED scene_tree->get_root()->enable_camera_3d_override(enable); +#endif // _3D_DISABLED + runtime_node_select->_set_camera_override_enabled(enable && !from_editor); - } else if (p_msg == "override_camera_3D:transform") { + } else if (p_msg == "transform_camera_2d") { + ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + Transform2D transform = p_args[0]; + scene_tree->get_root()->set_canvas_transform_override(transform); + runtime_node_select->_queue_selection_update(); + +#ifndef _3D_DISABLED + } else if (p_msg == "transform_camera_3d") { ERR_FAIL_COND_V(p_args.size() < 5, ERR_INVALID_DATA); Transform3D transform = p_args[0]; bool is_perspective = p_args[1]; @@ -127,95 +174,148 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra scene_tree->get_root()->set_camera_3d_override_orthogonal(size_or_fov, depth_near, depth_far); } scene_tree->get_root()->set_camera_3d_override_transform(transform); + runtime_node_select->_queue_selection_update(); #endif // _3D_DISABLED + } else if (p_msg == "set_object_property") { ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); _set_object_property(p_args[0], p_args[1], p_args[2]); + runtime_node_select->_queue_selection_update(); - } else if (!p_msg.begins_with("live_")) { // Live edits below. - return ERR_SKIP; - } else if (p_msg == "live_set_root") { - ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); - live_editor->_root_func(p_args[0], p_args[1]); + } else if (p_msg == "reload_cached_files") { + ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + PackedStringArray files = p_args[0]; + reload_cached_files(files); + + } else if (p_msg.begins_with("live_")) { /// Live Edit + if (p_msg == "live_set_root") { + ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); + live_editor->_root_func(p_args[0], p_args[1]); + + } else if (p_msg == "live_node_path") { + ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); + live_editor->_node_path_func(p_args[0], p_args[1]); + + } else if (p_msg == "live_res_path") { + ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); + live_editor->_res_path_func(p_args[0], p_args[1]); + + } else if (p_msg == "live_node_prop_res") { + ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); + live_editor->_node_set_res_func(p_args[0], p_args[1], p_args[2]); + + } else if (p_msg == "live_node_prop") { + ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); + live_editor->_node_set_func(p_args[0], p_args[1], p_args[2]); + + } else if (p_msg == "live_res_prop_res") { + ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); + live_editor->_res_set_res_func(p_args[0], p_args[1], p_args[2]); + + } else if (p_msg == "live_res_prop") { + ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); + live_editor->_res_set_func(p_args[0], p_args[1], p_args[2]); + + } else if (p_msg == "live_node_call") { + ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); + LocalVector<Variant> args; + LocalVector<Variant *> argptrs; + args.resize(p_args.size() - 2); + argptrs.resize(args.size()); + for (uint32_t i = 0; i < args.size(); i++) { + args[i] = p_args[i + 2]; + argptrs[i] = &args[i]; + } + live_editor->_node_call_func(p_args[0], p_args[1], argptrs.size() ? (const Variant **)argptrs.ptr() : nullptr, argptrs.size()); + + } else if (p_msg == "live_res_call") { + ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); + LocalVector<Variant> args; + LocalVector<Variant *> argptrs; + args.resize(p_args.size() - 2); + argptrs.resize(args.size()); + for (uint32_t i = 0; i < args.size(); i++) { + args[i] = p_args[i + 2]; + argptrs[i] = &args[i]; + } + live_editor->_res_call_func(p_args[0], p_args[1], argptrs.size() ? (const Variant **)argptrs.ptr() : nullptr, argptrs.size()); - } else if (p_msg == "live_node_path") { - ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); - live_editor->_node_path_func(p_args[0], p_args[1]); + } else if (p_msg == "live_create_node") { + 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_res_path") { - ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); - live_editor->_res_path_func(p_args[0], p_args[1]); + } 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]); - } else if (p_msg == "live_node_prop_res") { - ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); - live_editor->_node_set_res_func(p_args[0], p_args[1], p_args[2]); + } else if (p_msg == "live_remove_node") { + ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + live_editor->_remove_node_func(p_args[0]); - } else if (p_msg == "live_node_prop") { - ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); - live_editor->_node_set_func(p_args[0], p_args[1], p_args[2]); + if (!runtime_node_select->has_selection) { + runtime_node_select->_clear_selection(); + } - } else if (p_msg == "live_res_prop_res") { - ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); - live_editor->_res_set_res_func(p_args[0], p_args[1], p_args[2]); + } else if (p_msg == "live_remove_and_keep_node") { + ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); + live_editor->_remove_and_keep_node_func(p_args[0], p_args[1]); - } else if (p_msg == "live_res_prop") { - ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); - live_editor->_res_set_func(p_args[0], p_args[1], p_args[2]); + if (!runtime_node_select->has_selection) { + runtime_node_select->_clear_selection(); + } - } else if (p_msg == "live_node_call") { - ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); - LocalVector<Variant> args; - LocalVector<Variant *> argptrs; - args.resize(p_args.size() - 2); - argptrs.resize(args.size()); - for (uint32_t i = 0; i < args.size(); i++) { - args[i] = p_args[i + 2]; - argptrs[i] = &args[i]; - } - live_editor->_node_call_func(p_args[0], p_args[1], argptrs.size() ? (const Variant **)argptrs.ptr() : nullptr, argptrs.size()); + } else if (p_msg == "live_restore_node") { + ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); + live_editor->_restore_node_func(p_args[0], p_args[1], p_args[2]); - } else if (p_msg == "live_res_call") { - ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); - LocalVector<Variant> args; - LocalVector<Variant *> argptrs; - args.resize(p_args.size() - 2); - argptrs.resize(args.size()); - for (uint32_t i = 0; i < args.size(); i++) { - args[i] = p_args[i + 2]; - argptrs[i] = &args[i]; + } else if (p_msg == "live_duplicate_node") { + ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); + live_editor->_duplicate_node_func(p_args[0], p_args[1]); + + } else if (p_msg == "live_reparent_node") { + ERR_FAIL_COND_V(p_args.size() < 4, ERR_INVALID_DATA); + live_editor->_reparent_node_func(p_args[0], p_args[1], p_args[2], p_args[3]); + + } else { + return ERR_SKIP; } - live_editor->_res_call_func(p_args[0], p_args[1], argptrs.size() ? (const Variant **)argptrs.ptr() : nullptr, argptrs.size()); - } else if (p_msg == "live_create_node") { - 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.begins_with("runtime_node_select_")) { /// Runtime Node Selection + if (p_msg == "runtime_node_select_setup") { + ERR_FAIL_COND_V(p_args.is_empty() || p_args[0].get_type() != Variant::DICTIONARY, ERR_INVALID_DATA); + runtime_node_select->_setup(p_args[0]); - } 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]); + } else if (p_msg == "runtime_node_select_set_type") { + ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + RuntimeNodeSelect::NodeType type = (RuntimeNodeSelect::NodeType)(int)p_args[0]; + runtime_node_select->_node_set_type(type); - } else if (p_msg == "live_remove_node") { - ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); - live_editor->_remove_node_func(p_args[0]); + } else if (p_msg == "runtime_node_select_set_mode") { + ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + RuntimeNodeSelect::SelectMode mode = (RuntimeNodeSelect::SelectMode)(int)p_args[0]; + runtime_node_select->_select_set_mode(mode); - } else if (p_msg == "live_remove_and_keep_node") { - ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); - live_editor->_remove_and_keep_node_func(p_args[0], p_args[1]); + } else if (p_msg == "runtime_node_select_set_visible") { + ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + bool visible = p_args[0]; + runtime_node_select->_set_selection_visible(visible); - } else if (p_msg == "live_restore_node") { - ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); - live_editor->_restore_node_func(p_args[0], p_args[1], p_args[2]); + } else if (p_msg == "runtime_node_select_reset_camera_2d") { + runtime_node_select->_reset_camera_2d(); - } else if (p_msg == "live_duplicate_node") { - ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); - live_editor->_duplicate_node_func(p_args[0], p_args[1]); +#ifndef _3D_DISABLED + } else if (p_msg == "runtime_node_select_reset_camera_3d") { + runtime_node_select->_reset_camera_3d(); +#endif // _3D_DISABLED + + } else { + return ERR_SKIP; + } - } else if (p_msg == "live_reparent_node") { - ERR_FAIL_COND_V(p_args.size() < 4, ERR_INVALID_DATA); - live_editor->_reparent_node_func(p_args[0], p_args[1], p_args[2], p_args[3]); } else { r_captured = false; } + return OK; } @@ -260,6 +360,9 @@ void SceneDebugger::_send_object_id(ObjectID p_id, int p_max_size) { return; } + Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id)); + RuntimeNodeSelect::get_singleton()->_select_node(node); + Array arr; obj.serialize(arr); EngineDebugger::get_singleton()->send_message("scene:inspect_object", arr); @@ -280,6 +383,16 @@ void SceneDebugger::_set_object_property(ObjectID p_id, const String &p_property obj->set(prop_name, p_value); } +void SceneDebugger::_next_frame() { + SceneTree *scene_tree = SceneTree::get_singleton(); + if (!scene_tree->is_suspended()) { + return; + } + + scene_tree->set_suspend(false); + RenderingServer::get_singleton()->connect("frame_post_draw", callable_mp(scene_tree, &SceneTree::set_suspend).bind(true), Object::CONNECT_ONE_SHOT); +} + void SceneDebugger::add_to_cache(const String &p_filename, Node *p_node) { LiveEditor *debugger = LiveEditor::get_singleton(); if (!debugger) { @@ -316,6 +429,15 @@ void SceneDebugger::remove_from_cache(const String &p_filename, Node *p_node) { } } +void SceneDebugger::reload_cached_files(const PackedStringArray &p_files) { + for (const String &file : p_files) { + Ref<Resource> res = ResourceCache::get_ref(file); + if (res.is_valid()) { + res->reload_from_file(); + } + } +} + /// SceneDebuggerObject SceneDebuggerObject::SceneDebuggerObject(ObjectID p_id) { id = ObjectID(); @@ -580,7 +702,6 @@ void SceneDebuggerTree::deserialize(const Array &p_arr) { } /// LiveEditor -LiveEditor *LiveEditor::singleton = nullptr; LiveEditor *LiveEditor::get_singleton() { return singleton; } @@ -1089,4 +1210,950 @@ void LiveEditor::_reparent_node_func(const NodePath &p_at, const NodePath &p_new } } +/// RuntimeNodeSelect +RuntimeNodeSelect *RuntimeNodeSelect::get_singleton() { + return singleton; +} + +RuntimeNodeSelect::~RuntimeNodeSelect() { + if (selection_list && !selection_list->is_visible()) { + memdelete(selection_list); + } + + if (sbox_2d_canvas.is_valid()) { + RS::get_singleton()->free(sbox_2d_canvas); + RS::get_singleton()->free(sbox_2d_ci); + } + +#ifndef _3D_DISABLED + if (sbox_3d_instance.is_valid()) { + RS::get_singleton()->free(sbox_3d_instance); + RS::get_singleton()->free(sbox_3d_instance_ofs); + RS::get_singleton()->free(sbox_3d_instance_xray); + RS::get_singleton()->free(sbox_3d_instance_xray_ofs); + } +#endif // _3D_DISABLED +} + +void RuntimeNodeSelect::_setup(const Dictionary &p_settings) { + Window *root = SceneTree::get_singleton()->get_root(); + ERR_FAIL_COND(root->is_connected(SceneStringName(window_input), callable_mp(this, &RuntimeNodeSelect::_root_window_input))); + + root->connect(SceneStringName(window_input), callable_mp(this, &RuntimeNodeSelect::_root_window_input)); + root->connect("size_changed", callable_mp(this, &RuntimeNodeSelect::_queue_selection_update), CONNECT_DEFERRED); + + selection_list = memnew(PopupMenu); + selection_list->set_theme(ThemeDB::get_singleton()->get_default_theme()); + selection_list->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED); + selection_list->set_force_native(true); + selection_list->connect("index_pressed", callable_mp(this, &RuntimeNodeSelect::_items_popup_index_pressed).bind(selection_list)); + selection_list->connect("popup_hide", callable_mp(Object::cast_to<Node>(root), &Node::remove_child).bind(selection_list)); + + panner.instantiate(); + panner->set_callbacks(callable_mp(this, &RuntimeNodeSelect::_pan_callback), callable_mp(this, &RuntimeNodeSelect::_zoom_callback)); + + ViewPanner::ControlScheme panning_scheme = (ViewPanner::ControlScheme)p_settings.get("editors/panning/2d_editor_panning_scheme", 0).operator int(); + bool simple_panning = p_settings.get("editors/panning/simple_panning", false); + int pan_speed = p_settings.get("editors/panning/2d_editor_pan_speed", 20); + Array keys = p_settings.get("canvas_item_editor/pan_view", Array()).operator Array(); + panner->setup(panning_scheme, DebuggerMarshalls::deserialize_key_shortcut(keys), simple_panning); + panner->set_scroll_speed(pan_speed); + warped_panning = p_settings.get("editors/panning/warped_mouse_panning", false); + + /// 2D Selection Box Generation + + sbox_2d_canvas = RS::get_singleton()->canvas_create(); + sbox_2d_ci = RS::get_singleton()->canvas_item_create(); + RS::get_singleton()->viewport_attach_canvas(root->get_viewport_rid(), sbox_2d_canvas); + RS::get_singleton()->canvas_item_set_parent(sbox_2d_ci, sbox_2d_canvas); + +#ifndef _3D_DISABLED + cursor = Cursor(); + + /// 3D Selection Box Generation + // Copied from the Node3DEditor implementation. + + // Use two AABBs to create the illusion of a slightly thicker line. + AABB aabb(Vector3(), Vector3(1, 1, 1)); + + // Create a x-ray (visible through solid surfaces) and standard version of the selection box. + // Both will be drawn at the same position, but with different opacity. + // This lets the user see where the selection is while still having a sense of depth. + Ref<SurfaceTool> st = memnew(SurfaceTool); + Ref<SurfaceTool> st_xray = memnew(SurfaceTool); + + st->begin(Mesh::PRIMITIVE_LINES); + st_xray->begin(Mesh::PRIMITIVE_LINES); + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + + st->add_vertex(a); + st->add_vertex(b); + st_xray->add_vertex(a); + st_xray->add_vertex(b); + } + + Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D); + mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + // In the original Node3DEditor, this value would be fetched from the "editors/3d/selection_box_color" editor property, + // but since this is not accessible from here, we will just use the default value. + const Color selection_color_3d = Color(1, 0.5, 0); + mat->set_albedo(selection_color_3d); + mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + st->set_material(mat); + sbox_3d_mesh = st->commit(); + + Ref<StandardMaterial3D> mat_xray = memnew(StandardMaterial3D); + mat_xray->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + mat_xray->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + mat_xray->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); + mat_xray->set_albedo(selection_color_3d * Color(1, 1, 1, 0.15)); + mat_xray->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + st_xray->set_material(mat_xray); + sbox_3d_mesh_xray = st_xray->commit(); +#endif // _3D_DISABLED + + SceneTree::get_singleton()->connect("process_frame", callable_mp(this, &RuntimeNodeSelect::_process_frame)); + SceneTree::get_singleton()->connect("physics_frame", callable_mp(this, &RuntimeNodeSelect::_physics_frame)); + + // This function will be called before the root enters the tree at first when the Game view is passing its settings to + // the debugger, so queue the update for after it enters. + root->connect(SceneStringName(tree_entered), callable_mp(this, &RuntimeNodeSelect::_update_input_state), Object::CONNECT_ONE_SHOT); +} + +void RuntimeNodeSelect::_node_set_type(NodeType p_type) { + node_select_type = p_type; + _update_input_state(); +} + +void RuntimeNodeSelect::_select_set_mode(SelectMode p_mode) { + node_select_mode = p_mode; +} + +void RuntimeNodeSelect::_set_camera_override_enabled(bool p_enabled) { + camera_override = p_enabled; + + if (p_enabled) { + _update_view_2d(); + } + +#ifndef _3D_DISABLED + if (camera_first_override) { + _reset_camera_2d(); + _reset_camera_3d(); + + camera_first_override = false; + } else if (p_enabled) { + _update_view_2d(); + + SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform()); + SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR); + } +#endif // _3D_DISABLED +} + +void RuntimeNodeSelect::_root_window_input(const Ref<InputEvent> &p_event) { + Window *root = SceneTree::get_singleton()->get_root(); + if (node_select_type == NODE_TYPE_NONE || selection_list->is_visible()) { + // Workaround for platforms that don't allow subwindows. + if (selection_list->is_visible() && selection_list->is_embedded()) { + root->set_disable_input_override(false); + selection_list->push_input(p_event); + callable_mp(root->get_viewport(), &Viewport::set_disable_input_override).call_deferred(true); + } + + return; + } + + if (camera_override) { + if (node_select_type == NODE_TYPE_2D) { + if (panner->gui_input(p_event, warped_panning ? Rect2(Vector2(), root->get_size()) : Rect2())) { + return; + } + } else if (node_select_type == NODE_TYPE_3D) { +#ifndef _3D_DISABLED + if (root->get_camera_3d() && _handle_3d_input(p_event)) { + return; + } +#endif // _3D_DISABLED + } + } + + Ref<InputEventMouseButton> b = p_event; + if (!b.is_valid() || !b->is_pressed()) { + return; + } + + list_shortcut_pressed = node_select_mode == SELECT_MODE_SINGLE && b->get_button_index() == MouseButton::RIGHT && b->is_alt_pressed(); + if (list_shortcut_pressed || b->get_button_index() == MouseButton::LEFT) { + selection_position = b->get_position(); + } +} + +void RuntimeNodeSelect::_items_popup_index_pressed(int p_index, PopupMenu *p_popup) { + Object *obj = p_popup->get_item_metadata(p_index).get_validated_object(); + if (!obj) { + return; + } + + Array message; + message.append(obj->get_instance_id()); + EngineDebugger::get_singleton()->send_message("remote_node_clicked", message); +} + +void RuntimeNodeSelect::_update_input_state() { + SceneTree *scene_tree = SceneTree::get_singleton(); + // This function can be called at the very beginning, when the root hasn't entered the tree yet. + // So check first to avoid a crash. + if (!scene_tree->get_root()->is_inside_tree()) { + return; + } + + bool disable_input = scene_tree->is_suspended() || node_select_type != RuntimeNodeSelect::NODE_TYPE_NONE; + Input::get_singleton()->set_disable_input(disable_input); + Input::get_singleton()->set_mouse_mode_override_enabled(disable_input); + scene_tree->get_root()->set_disable_input_override(disable_input); +} + +void RuntimeNodeSelect::_process_frame() { +#ifndef _3D_DISABLED + if (camera_freelook) { + Transform3D transform = _get_cursor_transform(); + Vector3 forward = transform.basis.xform(Vector3(0, 0, -1)); + const Vector3 right = transform.basis.xform(Vector3(1, 0, 0)); + Vector3 up = transform.basis.xform(Vector3(0, 1, 0)); + + Vector3 direction; + + Input *input = Input::get_singleton(); + bool was_input_disabled = input->is_input_disabled(); + if (was_input_disabled) { + input->set_disable_input(false); + } + + if (input->is_physical_key_pressed(Key::A)) { + direction -= right; + } + if (input->is_physical_key_pressed(Key::D)) { + direction += right; + } + if (input->is_physical_key_pressed(Key::W)) { + direction += forward; + } + if (input->is_physical_key_pressed(Key::S)) { + direction -= forward; + } + if (input->is_physical_key_pressed(Key::E)) { + direction += up; + } + if (input->is_physical_key_pressed(Key::Q)) { + direction -= up; + } + + real_t speed = FREELOOK_BASE_SPEED; + if (input->is_physical_key_pressed(Key::SHIFT)) { + speed *= 3.0; + } + if (input->is_physical_key_pressed(Key::ALT)) { + speed *= 0.333333; + } + + if (was_input_disabled) { + input->set_disable_input(true); + } + + if (direction != Vector3()) { + // Calculate the process time manually, as the time scale is frozen. + const double process_time = (1.0 / Engine::get_singleton()->get_frames_per_second()) * Engine::get_singleton()->get_unfrozen_time_scale(); + const Vector3 motion = direction * speed * process_time; + cursor.pos += motion; + cursor.eye_pos += motion; + + SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform()); + } + } +#endif // _3D_DISABLED + + if (selection_update_queued || !SceneTree::get_singleton()->is_suspended()) { + selection_update_queued = false; + if (has_selection) { + _update_selection(); + } + } +} + +void RuntimeNodeSelect::_physics_frame() { + if (!Math::is_inf(selection_position.x) || !Math::is_inf(selection_position.y)) { + _click_point(); + selection_position = Point2(INFINITY, INFINITY); + } +} + +void RuntimeNodeSelect::_click_point() { + Window *root = SceneTree::get_singleton()->get_root(); + Point2 pos = root->get_screen_transform().affine_inverse().xform(selection_position); + Vector<SelectResult> items; + + if (node_select_type == NODE_TYPE_2D) { + for (int i = 0; i < root->get_child_count(); i++) { + _find_canvas_items_at_pos(pos, root->get_child(i), items); + } + + // Remove possible duplicates. + for (int i = 0; i < items.size(); i++) { + Node *item = items[i].item; + for (int j = 0; j < i; j++) { + if (items[j].item == item) { + items.remove_at(i); + i--; + + break; + } + } + } + } else if (node_select_type == NODE_TYPE_3D) { +#ifndef _3D_DISABLED + _find_3d_items_at_pos(pos, items); +#endif // _3D_DISABLED + } + + if (items.is_empty()) { + return; + } + + items.sort(); + + if ((!list_shortcut_pressed && node_select_mode == SELECT_MODE_SINGLE) || items.size() == 1) { + Array message; + message.append(items[0].item->get_instance_id()); + EngineDebugger::get_singleton()->send_message("remote_node_clicked", message); + } else if (list_shortcut_pressed || node_select_mode == SELECT_MODE_LIST) { + if (!selection_list->is_inside_tree()) { + root->add_child(selection_list); + } + + selection_list->clear(); + for (const SelectResult &I : items) { + selection_list->add_item(I.item->get_name()); + selection_list->set_item_metadata(-1, I.item); + } + + selection_list->set_position(selection_list->is_embedded() ? pos : selection_position + root->get_position()); + selection_list->reset_size(); + selection_list->popup(); + // FIXME: Ugly hack that stops the popup from hiding when the button is released. + selection_list->call_deferred(SNAME("set_position"), selection_list->get_position() + Point2(1, 0)); + } +} + +void RuntimeNodeSelect::_select_node(Node *p_node) { + if (p_node == selected_node) { + return; + } + + _clear_selection(); + + CanvasItem *ci = Object::cast_to<CanvasItem>(p_node); + if (ci) { + selected_node = p_node; + } else { +#ifndef _3D_DISABLED + Node3D *node_3d = Object::cast_to<Node3D>(p_node); + if (node_3d) { + if (!node_3d->is_inside_world()) { + return; + } + + selected_node = p_node; + + sbox_3d_instance = RS::get_singleton()->instance_create2(sbox_3d_mesh->get_rid(), node_3d->get_world_3d()->get_scenario()); + sbox_3d_instance_ofs = RS::get_singleton()->instance_create2(sbox_3d_mesh->get_rid(), node_3d->get_world_3d()->get_scenario()); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance, RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_ofs, RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_ofs, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_ofs, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + + sbox_3d_instance_xray = RS::get_singleton()->instance_create2(sbox_3d_mesh_xray->get_rid(), node_3d->get_world_3d()->get_scenario()); + sbox_3d_instance_xray_ofs = RS::get_singleton()->instance_create2(sbox_3d_mesh_xray->get_rid(), node_3d->get_world_3d()->get_scenario()); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_xray, RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_xray_ofs, RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray_ofs, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray_ofs, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + } +#endif // _3D_DISABLED + } + + has_selection = selected_node; + _queue_selection_update(); +} + +void RuntimeNodeSelect::_queue_selection_update() { + if (has_selection && selection_visible) { + if (SceneTree::get_singleton()->is_suspended()) { + _update_selection(); + } else { + selection_update_queued = true; + } + } +} + +void RuntimeNodeSelect::_update_selection() { + if (has_selection && (!selected_node || !selected_node->is_inside_tree())) { + _clear_selection(); + return; + } + + CanvasItem *ci = Object::cast_to<CanvasItem>(selected_node); + if (ci) { + Window *root = SceneTree::get_singleton()->get_root(); + Transform2D xform; + if (root->is_canvas_transform_override_enabled() && !ci->get_canvas_layer_node()) { + RS::get_singleton()->canvas_item_set_transform(sbox_2d_ci, (root->get_canvas_transform_override())); + xform = ci->get_global_transform(); + } else { + RS::get_singleton()->canvas_item_set_transform(sbox_2d_ci, Transform2D()); + xform = ci->get_global_transform_with_canvas(); + } + + // Fallback. + Rect2 rect = Rect2(Vector2(), Vector2(10, 10)); + + if (ci->_edit_use_rect()) { + rect = ci->_edit_get_rect(); + } else { + CollisionShape2D *collision_shape = Object::cast_to<CollisionShape2D>(ci); + if (collision_shape) { + Ref<Shape2D> shape = collision_shape->get_shape(); + if (shape.is_valid()) { + rect = shape->get_rect(); + } + } + } + + RS::get_singleton()->canvas_item_set_visible(sbox_2d_ci, selection_visible); + + if (xform == sbox_2d_xform && rect == sbox_2d_rect) { + return; // Nothing changed. + } + sbox_2d_xform = xform; + sbox_2d_rect = rect; + + RS::get_singleton()->canvas_item_clear(sbox_2d_ci); + + const Vector2 endpoints[4] = { + xform.xform(rect.position), + xform.xform(rect.position + Vector2(rect.size.x, 0)), + xform.xform(rect.position + rect.size), + xform.xform(rect.position + Vector2(0, rect.size.y)) + }; + + const Color selection_color_2d = Color(1, 0.6, 0.4, 0.7); + for (int i = 0; i < 4; i++) { + RS::get_singleton()->canvas_item_add_line(sbox_2d_ci, endpoints[i], endpoints[(i + 1) % 4], selection_color_2d, Math::round(2.f)); + } + } else { +#ifndef _3D_DISABLED + Node3D *node_3d = Object::cast_to<Node3D>(selected_node); + + // Fallback. + AABB bounds(Vector3(-0.5, -0.5, -0.5), Vector3(1, 1, 1)); + + VisualInstance3D *visual_instance = Object::cast_to<VisualInstance3D>(node_3d); + if (visual_instance) { + bounds = visual_instance->get_aabb(); + } else { + CollisionShape3D *collision_shape = Object::cast_to<CollisionShape3D>(node_3d); + if (collision_shape) { + Ref<Shape3D> shape = collision_shape->get_shape(); + if (shape.is_valid()) { + bounds = shape->get_debug_mesh()->get_aabb(); + } + } + } + + RS::get_singleton()->instance_set_visible(sbox_3d_instance, selection_visible); + RS::get_singleton()->instance_set_visible(sbox_3d_instance_ofs, selection_visible); + RS::get_singleton()->instance_set_visible(sbox_3d_instance_xray, selection_visible); + RS::get_singleton()->instance_set_visible(sbox_3d_instance_xray_ofs, selection_visible); + + Transform3D xform_to_top_level_parent_space = node_3d->get_global_transform().affine_inverse() * node_3d->get_global_transform(); + bounds = xform_to_top_level_parent_space.xform(bounds); + Transform3D t = node_3d->get_global_transform(); + + if (t == sbox_3d_xform && bounds == sbox_3d_bounds) { + return; // Nothing changed. + } + sbox_3d_xform = t; + sbox_3d_bounds = bounds; + + Transform3D t_offset = t; + + // Apply AABB scaling before item's global transform. + { + const Vector3 offset(0.005, 0.005, 0.005); + Basis aabb_s; + aabb_s.scale(bounds.size + offset); + t.translate_local(bounds.position - offset / 2); + t.basis = t.basis * aabb_s; + } + { + const Vector3 offset(0.01, 0.01, 0.01); + Basis aabb_s; + aabb_s.scale(bounds.size + offset); + t_offset.translate_local(bounds.position - offset / 2); + t_offset.basis = t_offset.basis * aabb_s; + } + + RS::get_singleton()->instance_set_transform(sbox_3d_instance, t); + RS::get_singleton()->instance_set_transform(sbox_3d_instance_ofs, t_offset); + RS::get_singleton()->instance_set_transform(sbox_3d_instance_xray, t); + RS::get_singleton()->instance_set_transform(sbox_3d_instance_xray_ofs, t_offset); +#endif // _3D_DISABLED + } +} + +void RuntimeNodeSelect::_clear_selection() { + selected_node = nullptr; + has_selection = false; + + if (sbox_2d_canvas.is_valid()) { + RS::get_singleton()->canvas_item_clear(sbox_2d_ci); + } + +#ifndef _3D_DISABLED + if (sbox_3d_instance.is_valid()) { + RS::get_singleton()->free(sbox_3d_instance); + RS::get_singleton()->free(sbox_3d_instance_ofs); + RS::get_singleton()->free(sbox_3d_instance_xray); + RS::get_singleton()->free(sbox_3d_instance_xray_ofs); + } +#endif // _3D_DISABLED +} + +void RuntimeNodeSelect::_set_selection_visible(bool p_visible) { + selection_visible = p_visible; + + if (has_selection) { + _update_selection(); + } +} + +// Copied and trimmed from the CanvasItemEditor implementation. +void RuntimeNodeSelect::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<SelectResult> &r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { + if (!p_node || Object::cast_to<Viewport>(p_node)) { + return; + } + + // In the original CanvasItemEditor, this value would be fetched from the "editors/polygon_editor/point_grab_radius" editor property, + // but since this is not accessible from here, we will just use the default value. + const real_t grab_distance = 8; + CanvasItem *ci = Object::cast_to<CanvasItem>(p_node); + + for (int i = p_node->get_child_count() - 1; i >= 0; i--) { + if (ci) { + if (!ci->is_set_as_top_level()) { + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_parent_xform * ci->get_transform(), p_canvas_xform); + } else { + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, ci->get_transform(), p_canvas_xform); + } + } else { + CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); + } + } + + if (ci && ci->is_visible_in_tree()) { + Transform2D xform = p_canvas_xform; + if (!ci->is_set_as_top_level()) { + xform *= p_parent_xform; + } + + Vector2 pos; + // Cameras (overridden or not) don't affect `CanvasLayer`s. + if (!ci->get_canvas_layer_node()) { + Window *root = SceneTree::get_singleton()->get_root(); + pos = (root->is_canvas_transform_override_enabled() ? root->get_canvas_transform_override() : root->get_canvas_transform()).affine_inverse().xform(p_pos); + } else { + pos = p_pos; + } + + xform = (xform * ci->get_transform()).affine_inverse(); + const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length() / view_2d_zoom; + if (ci->_edit_is_selected_on_click(xform.xform(pos), local_grab_distance)) { + SelectResult res; + res.item = ci; + res.order = ci->get_effective_z_index() + ci->get_canvas_layer(); + r_items.push_back(res); + + // If it's a shape, get the collision object it's from. + // FIXME: If the collision object has multiple shapes, only the topmost will be above it in the list. + if (Object::cast_to<CollisionShape2D>(ci) || Object::cast_to<CollisionPolygon2D>(ci)) { + CollisionObject2D *collision_object = Object::cast_to<CollisionObject2D>(ci->get_parent()); + if (collision_object) { + SelectResult res_col; + res_col.item = ci->get_parent(); + res_col.order = collision_object->get_z_index() + ci->get_canvas_layer(); + r_items.push_back(res_col); + } + } + } + } +} + +void RuntimeNodeSelect::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { + view_2d_offset.x -= p_scroll_vec.x / view_2d_zoom; + view_2d_offset.y -= p_scroll_vec.y / view_2d_zoom; + + _update_view_2d(); +} + +// A very shallow copy of the same function inside CanvasItemEditor. +void RuntimeNodeSelect::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + real_t prev_zoom = view_2d_zoom; + view_2d_zoom = CLAMP(view_2d_zoom * p_zoom_factor, VIEW_2D_MIN_ZOOM, VIEW_2D_MAX_ZOOM); + + Vector2 pos = SceneTree::get_singleton()->get_root()->get_screen_transform().affine_inverse().xform(p_origin); + view_2d_offset += pos / prev_zoom - pos / view_2d_zoom; + + // We want to align in-scene pixels to screen pixels, this prevents blurry rendering + // of small details (texts, lines). + // This correction adds a jitter movement when zooming, so we correct only when the + // zoom factor is an integer. (in the other cases, all pixels won't be aligned anyway) + const real_t closest_zoom_factor = Math::round(view_2d_zoom); + if (Math::is_zero_approx(view_2d_zoom - closest_zoom_factor)) { + // Make sure scene pixel at view_offset is aligned on a screen pixel. + Vector2 view_offset_int = view_2d_offset.floor(); + Vector2 view_offset_frac = view_2d_offset - view_offset_int; + view_2d_offset = view_offset_int + (view_offset_frac * closest_zoom_factor).round() / closest_zoom_factor; + } + + _update_view_2d(); +} + +void RuntimeNodeSelect::_reset_camera_2d() { + view_2d_offset = -SceneTree::get_singleton()->get_root()->get_canvas_transform().get_origin(); + view_2d_zoom = 1; + + _update_view_2d(); +} + +void RuntimeNodeSelect::_update_view_2d() { + Transform2D transform = Transform2D(); + transform.scale_basis(Size2(view_2d_zoom, view_2d_zoom)); + transform.columns[2] = -view_2d_offset * view_2d_zoom; + + SceneTree::get_singleton()->get_root()->set_canvas_transform_override(transform); + + _queue_selection_update(); +} + +#ifndef _3D_DISABLED +void RuntimeNodeSelect::_find_3d_items_at_pos(const Point2 &p_pos, Vector<SelectResult> &r_items) { + Window *root = SceneTree::get_singleton()->get_root(); + Camera3D *camera = root->get_viewport()->get_camera_3d(); + if (!camera) { + return; + } + + Vector3 ray, pos, to; + if (root->get_viewport()->is_camera_3d_override_enabled()) { + Viewport *vp = root->get_viewport(); + ray = vp->camera_3d_override_project_ray_normal(p_pos); + pos = vp->camera_3d_override_project_ray_origin(p_pos); + to = pos + ray * vp->get_camera_3d_override_properties()["z_far"]; + } else { + ray = camera->project_ray_normal(p_pos); + pos = camera->project_ray_origin(p_pos); + to = pos + ray * camera->get_far(); + } + + // Start with physical objects. + PhysicsDirectSpaceState3D *ss = root->get_world_3d()->get_direct_space_state(); + PhysicsDirectSpaceState3D::RayResult result; + HashSet<RID> excluded; + PhysicsDirectSpaceState3D::RayParameters ray_params; + ray_params.from = pos; + ray_params.to = to; + ray_params.collide_with_areas = true; + while (true) { + ray_params.exclude = excluded; + if (ss->intersect_ray(ray_params, result)) { + SelectResult res; + res.item = Object::cast_to<Node>(result.collider); + res.order = -pos.distance_to(Object::cast_to<Node3D>(res.item)->get_global_transform().xform(result.position)); + + // Fetch collision shapes. + CollisionObject3D *collision = Object::cast_to<CollisionObject3D>(result.collider); + if (collision) { + List<uint32_t> owners; + collision->get_shape_owners(&owners); + for (const uint32_t &I : owners) { + SelectResult res_shape; + res_shape.item = Object::cast_to<Node>(collision->shape_owner_get_owner(I)); + res_shape.order = res.order; + r_items.push_back(res_shape); + } + } + + r_items.push_back(res); + + excluded.insert(result.rid); + } else { + break; + } + } + + // Then go for the meshes. + Vector<ObjectID> items = RS::get_singleton()->instances_cull_ray(pos, to, root->get_world_3d()->get_scenario()); + for (int i = 0; i < items.size(); i++) { + Object *obj = ObjectDB::get_instance(items[i]); + GeometryInstance3D *geo_instance = nullptr; + Ref<TriangleMesh> mesh_collision; + + MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(obj); + if (mesh_instance) { + if (mesh_instance->get_mesh().is_valid()) { + geo_instance = mesh_instance; + mesh_collision = mesh_instance->get_mesh()->generate_triangle_mesh(); + } + } else { + Label3D *label = Object::cast_to<Label3D>(obj); + if (label) { + geo_instance = label; + mesh_collision = label->generate_triangle_mesh(); + } else { + Sprite3D *sprite = Object::cast_to<Sprite3D>(obj); + if (sprite) { + geo_instance = sprite; + mesh_collision = sprite->generate_triangle_mesh(); + } + } + } + + if (mesh_collision.is_valid()) { + Transform3D gt = geo_instance->get_global_transform(); + Transform3D ai = gt.affine_inverse(); + Vector3 point, normal; + if (mesh_collision->intersect_ray(ai.xform(pos), ai.basis.xform(ray).normalized(), point, normal)) { + SelectResult res; + res.item = Object::cast_to<Node>(obj); + res.order = -pos.distance_to(gt.xform(point)); + r_items.push_back(res); + + continue; + } + } + + items.remove_at(i); + i--; + } +} + +bool RuntimeNodeSelect::_handle_3d_input(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + + if (b.is_valid()) { + const real_t zoom_factor = 1.08 * b->get_factor(); + switch (b->get_button_index()) { + case MouseButton::WHEEL_UP: { + if (!camera_freelook) { + _cursor_scale_distance(1.0 / zoom_factor); + } + + return true; + } break; + case MouseButton::WHEEL_DOWN: { + if (!camera_freelook) { + _cursor_scale_distance(zoom_factor); + } + + return true; + } break; + case MouseButton::RIGHT: { + _set_camera_freelook_enabled(b->is_pressed()); + return true; + } break; + default: { + } + } + } + + Ref<InputEventMouseMotion> m = p_event; + + if (m.is_valid()) { + if (camera_freelook) { + _cursor_look(m); + } else if (m->get_button_mask().has_flag(MouseButtonMask::MIDDLE)) { + if (m->is_shift_pressed()) { + _cursor_pan(m); + } else { + _cursor_orbit(m); + } + } + + return true; + } + + Ref<InputEventKey> k = p_event; + + if (k.is_valid()) { + if (k->get_physical_keycode() == Key::ESCAPE) { + _set_camera_freelook_enabled(false); + return true; + } else if (k->is_ctrl_pressed()) { + switch (k->get_physical_keycode()) { + case Key::EQUAL: { + cursor.fov_scale = CLAMP(cursor.fov_scale - 0.05, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE); + SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR); + + return true; + } break; + case Key::MINUS: { + cursor.fov_scale = CLAMP(cursor.fov_scale + 0.05, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE); + SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR); + + return true; + } break; + case Key::KEY_0: { + cursor.fov_scale = 1; + SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV, CAMERA_ZNEAR, CAMERA_ZFAR); + + return true; + } break; + default: { + } + } + } + } + + // TODO: Handle magnify and pan input gestures. + + return false; +} + +void RuntimeNodeSelect::_set_camera_freelook_enabled(bool p_enabled) { + camera_freelook = p_enabled; + + if (p_enabled) { + // Make sure eye_pos is synced, because freelook referential is eye pos rather than orbit pos + Vector3 forward = _get_cursor_transform().basis.xform(Vector3(0, 0, -1)); + cursor.eye_pos = cursor.pos - cursor.distance * forward; + + previous_mouse_position = SceneTree::get_singleton()->get_root()->get_mouse_position(); + + // Hide mouse like in an FPS (warping doesn't work). + Input::get_singleton()->set_mouse_mode_override(Input::MOUSE_MODE_CAPTURED); + + } else { + // Restore mouse. + Input::get_singleton()->set_mouse_mode_override(Input::MOUSE_MODE_VISIBLE); + + // Restore the previous mouse position when leaving freelook mode. + // This is done because leaving `Input.MOUSE_MODE_CAPTURED` will center the cursor + // due to OS limitations. + Input::get_singleton()->warp_mouse(previous_mouse_position); + } +} + +void RuntimeNodeSelect::_cursor_scale_distance(real_t p_scale) { + real_t min_distance = MAX(CAMERA_ZNEAR * 4, VIEW_3D_MIN_ZOOM); + real_t max_distance = MIN(CAMERA_ZFAR / 4, VIEW_3D_MAX_ZOOM); + cursor.distance = CLAMP(cursor.distance * p_scale, min_distance, max_distance); + + SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform()); +} + +void RuntimeNodeSelect::_cursor_look(Ref<InputEventWithModifiers> p_event) { + Window *root = SceneTree::get_singleton()->get_root(); + const Vector2 relative = Input::get_singleton()->warp_mouse_motion(p_event, Rect2(Vector2(), root->get_size())); + const Transform3D prev_camera_transform = _get_cursor_transform(); + + cursor.x_rot += relative.y * RADS_PER_PIXEL; + // Clamp the Y rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented. + cursor.x_rot = CLAMP(cursor.x_rot, -1.57, 1.57); + + cursor.y_rot += relative.x * RADS_PER_PIXEL; + + // Look is like the opposite of Orbit: the focus point rotates around the camera. + Transform3D camera_transform = _get_cursor_transform(); + Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); + Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); + Vector3 diff = prev_pos - pos; + cursor.pos += diff; + + SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform()); +} + +void RuntimeNodeSelect::_cursor_pan(Ref<InputEventWithModifiers> p_event) { + Window *root = SceneTree::get_singleton()->get_root(); + // Reduce all sides of the area by 1, so warping works when windows are maximized/fullscreen. + const Vector2 relative = Input::get_singleton()->warp_mouse_motion(p_event, Rect2(Vector2(1, 1), root->get_size() - Vector2(2, 2))); + const real_t pan_speed = 1 / 150.0; + + Transform3D camera_transform; + camera_transform.translate_local(cursor.pos); + camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); + camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); + + Vector3 translation(1 * -relative.x * pan_speed, relative.y * pan_speed, 0); + translation *= cursor.distance / 4; + camera_transform.translate_local(translation); + cursor.pos = camera_transform.origin; + + SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform()); +} + +void RuntimeNodeSelect::_cursor_orbit(Ref<InputEventWithModifiers> p_event) { + Window *root = SceneTree::get_singleton()->get_root(); + // Reduce all sides of the area by 1, so warping works when windows are maximized/fullscreen. + const Vector2 relative = Input::get_singleton()->warp_mouse_motion(p_event, Rect2(Vector2(1, 1), root->get_size() - Vector2(2, 2))); + + cursor.x_rot += relative.y * RADS_PER_PIXEL; + // Clamp the Y rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented. + cursor.x_rot = CLAMP(cursor.x_rot, -1.57, 1.57); + + cursor.y_rot += relative.x * RADS_PER_PIXEL; + + SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform()); +} + +Transform3D RuntimeNodeSelect::_get_cursor_transform() { + Transform3D camera_transform; + camera_transform.translate_local(cursor.pos); + camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot); + camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot); + camera_transform.translate_local(0, 0, cursor.distance); + + return camera_transform; +} + +void RuntimeNodeSelect::_reset_camera_3d() { + camera_first_override = true; + + Window *root = SceneTree::get_singleton()->get_root(); + Camera3D *camera = root->get_camera_3d(); + if (!camera) { + return; + } + + cursor = Cursor(); + Transform3D transform = camera->get_global_transform(); + transform.translate_local(0, 0, -cursor.distance); + cursor.pos = transform.origin; + + cursor.x_rot = -camera->get_global_rotation().x; + cursor.y_rot = -camera->get_global_rotation().y; + + cursor.fov_scale = CLAMP(camera->get_fov() / CAMERA_BASE_FOV, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE); + + SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform()); + SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR); +} +#endif // _3D_DISABLED #endif diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index 0c28ca2a0c..90c8000eb4 100644 --- a/scene/debugger/scene_debugger.h +++ b/scene/debugger/scene_debugger.h @@ -31,19 +31,21 @@ #ifndef SCENE_DEBUGGER_H #define SCENE_DEBUGGER_H -#include "core/object/class_db.h" +#include "core/input/shortcut.h" #include "core/object/ref_counted.h" #include "core/string/ustring.h" #include "core/templates/pair.h" #include "core/variant/array.h" +#include "scene/gui/view_panner.h" +#include "scene/resources/mesh.h" +class PopupMenu; class Script; class Node; class SceneDebugger { -public: private: - static SceneDebugger *singleton; + inline static SceneDebugger *singleton = nullptr; SceneDebugger(); @@ -55,15 +57,19 @@ public: #ifdef DEBUG_ENABLED private: + static void _handle_input(const Ref<InputEvent> &p_event, const Ref<Shortcut> &p_shortcut); + static void _save_node(ObjectID id, const String &p_path); static void _set_node_owner_recursive(Node *p_node, Node *p_owner); static void _set_object_property(ObjectID p_id, const String &p_property, const Variant &p_value); static void _send_object_id(ObjectID p_id, int p_max_size = 1 << 20); + static void _next_frame(); public: static Error parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured); static void add_to_cache(const String &p_filename, Node *p_node); static void remove_from_cache(const String &p_filename, Node *p_node); + static void reload_cached_files(const PackedStringArray &p_files); #endif }; @@ -158,13 +164,164 @@ private: LiveEditor() { singleton = this; live_edit_root = NodePath("/root"); - }; + } - static LiveEditor *singleton; + inline static LiveEditor *singleton = nullptr; public: static LiveEditor *get_singleton(); }; + +class RuntimeNodeSelect : public Object { + GDCLASS(RuntimeNodeSelect, Object); + +public: + enum NodeType { + NODE_TYPE_NONE, + NODE_TYPE_2D, + NODE_TYPE_3D, + NODE_TYPE_MAX + }; + + enum SelectMode { + SELECT_MODE_SINGLE, + SELECT_MODE_LIST, + SELECT_MODE_MAX + }; + +private: + friend class SceneDebugger; + + struct SelectResult { + Node *item = nullptr; + real_t order = 0; + _FORCE_INLINE_ bool operator<(const SelectResult &p_rr) const { return p_rr.order < order; } + }; + + bool has_selection = false; + Node *selected_node = nullptr; + PopupMenu *selection_list = nullptr; + bool selection_visible = true; + bool selection_update_queued = false; + bool warped_panning = false; + + bool camera_override = false; + + // Values taken from EditorZoomWidget. + const float VIEW_2D_MIN_ZOOM = 1.0 / 128; + const float VIEW_2D_MAX_ZOOM = 128; + + Ref<ViewPanner> panner; + Vector2 view_2d_offset; + real_t view_2d_zoom = 1.0; + + RID sbox_2d_canvas; + RID sbox_2d_ci; + Transform2D sbox_2d_xform; + Rect2 sbox_2d_rect; + +#ifndef _3D_DISABLED + struct Cursor { + Vector3 pos; + real_t x_rot, y_rot, distance, fov_scale; + Vector3 eye_pos; // Used in freelook mode. + + Cursor() { + // These rotations place the camera in +X +Y +Z, aka south east, facing north west. + x_rot = 0.5; + y_rot = -0.5; + distance = 4; + fov_scale = 1.0; + } + }; + Cursor cursor; + + // Values taken from Node3DEditor. + const float VIEW_3D_MIN_ZOOM = 0.01; +#ifdef REAL_T_IS_DOUBLE + const double VIEW_3D_MAX_ZOOM = 1'000'000'000'000; +#else + const float VIEW_3D_MAX_ZOOM = 10'000; +#endif + const float CAMERA_ZNEAR = 0.05; + const float CAMERA_ZFAR = 4'000; + + const float CAMERA_BASE_FOV = 75; + const float CAMERA_MIN_FOV_SCALE = 0.1; + const float CAMERA_MAX_FOV_SCALE = 2.5; + + const float FREELOOK_BASE_SPEED = 4; + const float RADS_PER_PIXEL = 0.004; + + bool camera_first_override = true; + bool camera_freelook = false; + + Vector2 previous_mouse_position; + + Ref<ArrayMesh> sbox_3d_mesh; + Ref<ArrayMesh> sbox_3d_mesh_xray; + RID sbox_3d_instance; + RID sbox_3d_instance_ofs; + RID sbox_3d_instance_xray; + RID sbox_3d_instance_xray_ofs; + Transform3D sbox_3d_xform; + AABB sbox_3d_bounds; +#endif + + Point2 selection_position = Point2(INFINITY, INFINITY); + bool list_shortcut_pressed = false; + + NodeType node_select_type = NODE_TYPE_2D; + SelectMode node_select_mode = SELECT_MODE_SINGLE; + + void _setup(const Dictionary &p_settings); + + void _node_set_type(NodeType p_type); + void _select_set_mode(SelectMode p_mode); + + void _set_camera_override_enabled(bool p_enabled); + + void _root_window_input(const Ref<InputEvent> &p_event); + void _items_popup_index_pressed(int p_index, PopupMenu *p_popup); + void _update_input_state(); + + void _process_frame(); + void _physics_frame(); + + void _click_point(); + void _select_node(Node *p_node); + void _queue_selection_update(); + void _update_selection(); + void _clear_selection(); + void _set_selection_visible(bool p_visible); + + void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<SelectResult> &r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); + void _reset_camera_2d(); + void _update_view_2d(); + +#ifndef _3D_DISABLED + void _find_3d_items_at_pos(const Point2 &p_pos, Vector<SelectResult> &r_items); + bool _handle_3d_input(const Ref<InputEvent> &p_event); + void _set_camera_freelook_enabled(bool p_enabled); + void _cursor_scale_distance(real_t p_scale); + void _cursor_look(Ref<InputEventWithModifiers> p_event); + void _cursor_pan(Ref<InputEventWithModifiers> p_event); + void _cursor_orbit(Ref<InputEventWithModifiers> p_event); + Transform3D _get_cursor_transform(); + void _reset_camera_3d(); +#endif + + RuntimeNodeSelect() { singleton = this; } + + inline static RuntimeNodeSelect *singleton = nullptr; + +public: + static RuntimeNodeSelect *get_singleton(); + + ~RuntimeNodeSelect(); +}; #endif #endif // SCENE_DEBUGGER_H diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 34f5095493..bd4770bcc3 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -205,6 +205,7 @@ void BaseButton::set_disabled(bool p_disabled) { status.pressing_inside = false; } queue_redraw(); + update_minimum_size(); } bool BaseButton::is_disabled() const { diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 9df4bfde92..3e593a8372 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -561,6 +561,7 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) { } autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; p_paragraph->set_break_flags(autowrap_flags); + p_paragraph->set_line_spacing(theme_cache.line_spacing); if (text_direction == Control::TEXT_DIRECTION_INHERITED) { p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); @@ -642,7 +643,7 @@ String Button::get_language() const { return language; } -void Button::set_icon(const Ref<Texture2D> &p_icon) { +void Button::set_button_icon(const Ref<Texture2D> &p_icon) { if (icon == p_icon) { return; } @@ -666,7 +667,7 @@ void Button::_texture_changed() { update_minimum_size(); } -Ref<Texture2D> Button::get_icon() const { +Ref<Texture2D> Button::get_button_icon() const { return icon; } @@ -762,8 +763,8 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction); ClassDB::bind_method(D_METHOD("set_language", "language"), &Button::set_language); ClassDB::bind_method(D_METHOD("get_language"), &Button::get_language); - ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); - ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon); + ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_button_icon); + ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_button_icon); ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat); ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat); ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text); @@ -833,6 +834,7 @@ void Button::_bind_methods() { BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, icon_max_width); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, align_to_largest_stylebox); + BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, line_spacing); } Button::Button(const String &p_text) { diff --git a/scene/gui/button.h b/scene/gui/button.h index 5f4429bc1d..686d4806db 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -100,6 +100,7 @@ private: int h_separation = 0; int icon_max_width = 0; + int line_spacing = 0; } theme_cache; void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = ""); @@ -137,8 +138,8 @@ public: void set_language(const String &p_language); String get_language() const; - void set_icon(const Ref<Texture2D> &p_icon); - Ref<Texture2D> get_icon() const; + void set_button_icon(const Ref<Texture2D> &p_icon); + Ref<Texture2D> get_button_icon() const; void set_expand_icon(bool p_enabled); bool is_expand_icon() const; diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 7346c9dcd3..9d7e2496a2 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -1481,7 +1481,7 @@ void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 if (E) { text_rid = E->value; } else { - String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding); + String fc = String::num_int64(p_line + 1).lpad(line_number_digits, line_number_padding); if (is_localizing_numeral_system()) { fc = TS->format_number(fc); } diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index ab443e95e1..2cd34ec99f 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -251,13 +251,13 @@ private: Ref<Texture2D> completion_color_bg; Color breakpoint_color = Color(1, 1, 1); - Ref<Texture2D> breakpoint_icon = Ref<Texture2D>(); + Ref<Texture2D> breakpoint_icon; Color bookmark_color = Color(1, 1, 1); - Ref<Texture2D> bookmark_icon = Ref<Texture2D>(); + Ref<Texture2D> bookmark_icon; Color executing_line_color = Color(1, 1, 1); - Ref<Texture2D> executing_line_icon = Ref<Texture2D>(); + Ref<Texture2D> executing_line_icon; Color line_number_color = Color(1, 1, 1); diff --git a/scene/gui/color_mode.h b/scene/gui/color_mode.h index 94193ccf74..0abc90bb44 100644 --- a/scene/gui/color_mode.h +++ b/scene/gui/color_mode.h @@ -41,9 +41,9 @@ public: virtual String get_name() const = 0; - virtual int get_slider_count() const { return 3; }; + virtual int get_slider_count() const { return 3; } virtual float get_slider_step() const = 0; - virtual float get_spinbox_arrow_step() const { return get_slider_step(); }; + virtual float get_spinbox_arrow_step() const { return get_slider_step(); } virtual String get_slider_label(int idx) const = 0; virtual float get_slider_max(int idx) const = 0; virtual float get_slider_value(int idx) const = 0; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 2d88a9bddf..997120ff25 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -80,10 +80,10 @@ void ColorPicker::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - btn_pick->set_icon(theme_cache.screen_picker); + btn_pick->set_button_icon(theme_cache.screen_picker); _update_drop_down_arrow(btn_preset->is_pressed(), btn_preset); _update_drop_down_arrow(btn_recent_preset->is_pressed(), btn_recent_preset); - btn_add_preset->set_icon(theme_cache.add_preset); + btn_add_preset->set_button_icon(theme_cache.add_preset); btn_pick->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0)); btn_shape->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0)); @@ -116,7 +116,7 @@ void ColorPicker::_notification(int p_what) { shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_OKHSL_CIRCLE), theme_cache.shape_circle); if (current_shape != SHAPE_NONE) { - btn_shape->set_icon(shape_popup->get_item_icon(current_shape)); + btn_shape->set_button_icon(shape_popup->get_item_icon(current_shape)); } internal_margin->begin_bulk_theme_override(); @@ -705,14 +705,14 @@ void ColorPicker::_text_type_toggled() { if (text_is_constructor) { text_type->set_text(""); #ifdef TOOLS_ENABLED - text_type->set_icon(get_editor_theme_icon(SNAME("Script"))); + text_type->set_button_icon(get_editor_theme_icon(SNAME("Script"))); #endif c_text->set_editable(false); c_text->set_tooltip_text(RTR("Copy this constructor in a script.")); } else { text_type->set_text("#"); - text_type->set_icon(nullptr); + text_type->set_button_icon(nullptr); c_text->set_editable(true); c_text->set_tooltip_text(ETR("Enter a hex code (\"#ff0000\") or named color (\"red\").")); @@ -738,7 +738,7 @@ void ColorPicker::set_picker_shape(PickerShapeType p_shape) { } if (p_shape != SHAPE_NONE) { shape_popup->set_item_checked(p_shape, true); - btn_shape->set_icon(shape_popup->get_item_icon(p_shape)); + btn_shape->set_button_icon(shape_popup->get_item_icon(p_shape)); } current_shape = p_shape; @@ -794,9 +794,9 @@ void ColorPicker::_show_hide_preset(const bool &p_is_btn_pressed, Button *p_btn_ void ColorPicker::_update_drop_down_arrow(const bool &p_is_btn_pressed, Button *p_btn_preset) { if (p_is_btn_pressed) { - p_btn_preset->set_icon(theme_cache.expanded_arrow); + p_btn_preset->set_button_icon(theme_cache.expanded_arrow); } else { - p_btn_preset->set_icon(theme_cache.folded_arrow); + p_btn_preset->set_button_icon(theme_cache.folded_arrow); } } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 5052deb65a..4a3f7f2414 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -44,7 +44,7 @@ #ifdef TOOLS_ENABLED #include "editor/plugins/control_editor_plugin.h" -#endif +#endif // TOOLS_ENABLED // Editor plugin interoperability. @@ -120,11 +120,11 @@ void Control::_edit_set_state(const Dictionary &p_state) { void Control::_edit_set_position(const Point2 &p_position) { ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins."); set_position(p_position, ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled() && get_parent_control()); -}; +} Point2 Control::_edit_get_position() const { return get_position(); -}; +} void Control::_edit_set_scale(const Size2 &p_scale) { set_scale(p_scale); @@ -140,14 +140,6 @@ void Control::_edit_set_rect(const Rect2 &p_edit_rect) { set_size(p_edit_rect.size.snappedf(1), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled()); } -Rect2 Control::_edit_get_rect() const { - return Rect2(Point2(), get_size()); -} - -bool Control::_edit_use_rect() const { - return true; -} - void Control::_edit_set_rotation(real_t p_rotation) { set_rotation(p_rotation); } @@ -178,7 +170,17 @@ bool Control::_edit_use_pivot() const { Size2 Control::_edit_get_minimum_size() const { return get_combined_minimum_size(); } -#endif +#endif // TOOLS_ENABLED + +#ifdef DEBUG_ENABLED +Rect2 Control::_edit_get_rect() const { + return Rect2(Point2(), get_size()); +} + +bool Control::_edit_use_rect() const { + return true; +} +#endif // DEBUG_ENABLED void Control::reparent(Node *p_parent, bool p_keep_global_transform) { ERR_MAIN_THREAD_GUARD; @@ -239,7 +241,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List } CanvasItem::get_argument_options(p_function, p_idx, r_options); } -#endif +#endif // TOOLS_ENABLED PackedStringArray Control::get_configuration_warnings() const { ERR_READ_THREAD_GUARD_V(PackedStringArray()); @@ -663,7 +665,7 @@ Rect2 Control::get_parent_anchorable_rect() const { #else parent_rect = get_viewport()->get_visible_rect(); -#endif +#endif // TOOLS_ENABLED } return parent_rect; @@ -1396,7 +1398,7 @@ void Control::set_position(const Point2 &p_point, bool p_keep_offsets) { data.pos_cache = p_point; return; } -#endif +#endif // TOOLS_ENABLED if (p_keep_offsets) { _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor); @@ -1440,7 +1442,7 @@ void Control::_set_size(const Size2 &p_size) { if (data.size_warning && (data.anchor[SIDE_LEFT] != data.anchor[SIDE_RIGHT] || data.anchor[SIDE_TOP] != data.anchor[SIDE_BOTTOM])) { WARN_PRINT("Nodes with non-equal opposite anchors will have their size overridden after _ready(). \nIf you want to set size, change the anchors or consider using set_deferred()."); } -#endif +#endif // DEBUG_ENABLED set_size(p_size); } @@ -1462,7 +1464,7 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { data.size_cache = new_size; return; } -#endif +#endif // TOOLS_ENABLED if (p_keep_offsets) { _compute_anchors(Rect2(data.pos_cache, new_size), data.offset, data.anchor); @@ -2745,7 +2747,7 @@ Variant Control::get_theme_item(Theme::DataType p_data_type, const StringName &p Ref<Texture2D> Control::get_editor_theme_icon(const StringName &p_name) const { return get_theme_icon(p_name, SNAME("EditorIcons")); } -#endif +#endif // TOOLS_ENABLED bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(false); @@ -3087,7 +3089,7 @@ bool Control::is_layout_rtl() const { const_cast<Control *>(this)->data.is_rtl = true; return data.is_rtl; } -#endif +#endif // TOOLS_ENABLED Node *parent_node = get_parent(); while (parent_node) { Control *parent_control = Object::cast_to<Control>(parent_node); @@ -3162,7 +3164,7 @@ bool Control::is_auto_translating() const { ERR_READ_THREAD_GUARD_V(false); return can_auto_translate(); } -#endif +#endif // DISABLE_DEPRECATED void Control::set_tooltip_auto_translate_mode(AutoTranslateMode p_mode) { ERR_MAIN_THREAD_GUARD; @@ -3215,7 +3217,7 @@ void Control::_notification(int p_notification) { case NOTIFICATION_EDITOR_POST_SAVE: { saving = false; } break; -#endif +#endif // TOOLS_ENABLED case NOTIFICATION_POSTINITIALIZE: { data.initialized = true; @@ -3261,7 +3263,7 @@ void Control::_notification(int p_notification) { case NOTIFICATION_READY: { #ifdef DEBUG_ENABLED connect(SceneStringName(ready), callable_mp(this, &Control::_clear_size_warning), CONNECT_DEFERRED | CONNECT_ONE_SHOT); -#endif +#endif // DEBUG_ENABLED } break; case NOTIFICATION_ENTER_CANVAS: { @@ -3573,7 +3575,7 @@ void Control::_bind_methods() { #ifndef DISABLE_DEPRECATED 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); -#endif +#endif // DISABLE_DEPRECATED 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); @@ -3628,7 +3630,7 @@ void Control::_bind_methods() { #ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_auto_translate", "is_auto_translating"); -#endif +#endif // DISABLE_DEPRECATED 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 6cabf10971..ac386659ec 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -48,7 +48,7 @@ class Control : public CanvasItem { #ifdef TOOLS_ENABLED bool saving = false; -#endif +#endif //TOOLS_ENABLED public: enum Anchor { @@ -396,8 +396,6 @@ public: virtual Size2 _edit_get_scale() const override; virtual void _edit_set_rect(const Rect2 &p_edit_rect) override; - virtual Rect2 _edit_get_rect() const override; - virtual bool _edit_use_rect() const override; virtual void _edit_set_rotation(real_t p_rotation) override; virtual real_t _edit_get_rotation() const override; @@ -408,7 +406,13 @@ public: virtual bool _edit_use_pivot() const override; virtual Size2 _edit_get_minimum_size() const override; -#endif +#endif //TOOLS_ENABLED + +#ifdef DEBUG_ENABLED + virtual Rect2 _edit_get_rect() const override; + virtual bool _edit_use_rect() const override; +#endif // DEBUG_ENABLED + virtual void reparent(Node *p_parent, bool p_keep_global_transform = true) override; // Editor integration. @@ -418,7 +422,7 @@ public: PackedStringArray get_configuration_warnings() const override; #ifdef TOOLS_ENABLED virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; -#endif +#endif //TOOLS_ENABLED virtual bool is_text_field() const; @@ -605,7 +609,7 @@ public: Variant get_theme_item(Theme::DataType p_data_type, const StringName &p_name, const StringName &p_theme_type = StringName()) const; #ifdef TOOLS_ENABLED Ref<Texture2D> get_editor_theme_icon(const StringName &p_name) const; -#endif +#endif //TOOLS_ENABLED bool has_theme_icon_override(const StringName &p_name) const; bool has_theme_stylebox_override(const StringName &p_name) const; @@ -637,7 +641,7 @@ public: #ifndef DISABLE_DEPRECATED void set_auto_translate(bool p_enable); bool is_auto_translating() const; -#endif +#endif //DISABLE_DEPRECATED void set_tooltip_auto_translate_mode(AutoTranslateMode p_mode); AutoTranslateMode get_tooltip_auto_translate_mode() const; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index d8e9d1bcc0..9eda1a256f 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -67,7 +67,18 @@ void FileDialog::_native_popup() { } else if (access == ACCESS_USERDATA) { root = OS::get_singleton()->get_user_data_dir(); } - DisplayServer::get_singleton()->file_dialog_with_options_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &FileDialog::_native_dialog_cb)); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_EXTRA)) { + DisplayServer::get_singleton()->file_dialog_with_options_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &FileDialog::_native_dialog_cb_with_options)); + } else { + DisplayServer::get_singleton()->file_dialog_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, callable_mp(this, &FileDialog::_native_dialog_cb)); + } +} + +bool FileDialog::_can_use_native_popup() { + if (access == ACCESS_RESOURCES || access == ACCESS_USERDATA || options.size() > 0) { + return DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_EXTRA); + } + return DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE); } void FileDialog::popup(const Rect2i &p_rect) { @@ -80,7 +91,7 @@ void FileDialog::popup(const Rect2i &p_rect) { } #endif - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { + if (_can_use_native_popup() && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { _native_popup(); } else { ConfirmationDialog::popup(p_rect); @@ -99,7 +110,7 @@ void FileDialog::set_visible(bool p_visible) { } #endif - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { + if (_can_use_native_popup() && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { if (p_visible) { _native_popup(); } @@ -108,7 +119,11 @@ void FileDialog::set_visible(bool p_visible) { } } -void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options) { +void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter) { + _native_dialog_cb_with_options(p_ok, p_files, p_filter, Dictionary()); +} + +void FileDialog::_native_dialog_cb_with_options(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options) { if (!p_ok) { file->set_text(""); emit_signal(SNAME("canceled")); @@ -182,7 +197,7 @@ void FileDialog::_notification(int p_what) { #endif // Replace the built-in dialog with the native one if it started visible. - if (is_visible() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { + if (is_visible() && _can_use_native_popup() && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { ConfirmationDialog::set_visible(false); _native_popup(); } @@ -197,18 +212,18 @@ void FileDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - dir_up->set_icon(theme_cache.parent_folder); + dir_up->set_button_icon(theme_cache.parent_folder); if (vbox->is_layout_rtl()) { - dir_prev->set_icon(theme_cache.forward_folder); - dir_next->set_icon(theme_cache.back_folder); + dir_prev->set_button_icon(theme_cache.forward_folder); + dir_next->set_button_icon(theme_cache.back_folder); } else { - dir_prev->set_icon(theme_cache.back_folder); - dir_next->set_icon(theme_cache.forward_folder); + dir_prev->set_button_icon(theme_cache.back_folder); + dir_next->set_button_icon(theme_cache.forward_folder); } - refresh->set_icon(theme_cache.reload); - show_hidden->set_icon(theme_cache.toggle_hidden); - makedir->set_icon(theme_cache.create_folder); - show_filename_filter_button->set_icon(theme_cache.toggle_filename_filter); + refresh->set_button_icon(theme_cache.reload); + show_hidden->set_button_icon(theme_cache.toggle_hidden); + makedir->set_button_icon(theme_cache.create_folder); + show_filename_filter_button->set_button_icon(theme_cache.toggle_filename_filter); dir_up->begin_bulk_theme_override(); dir_up->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); @@ -1487,7 +1502,7 @@ void FileDialog::set_use_native_dialog(bool p_native) { #endif // Replace the built-in dialog with the native one if it's currently visible. - if (is_inside_tree() && is_visible() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { + if (is_inside_tree() && is_visible() && _can_use_native_popup() && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { ConfirmationDialog::set_visible(false); _native_popup(); } @@ -1571,6 +1586,7 @@ FileDialog::FileDialog() { vbox->add_child(hbc); tree = memnew(Tree); + tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); tree->set_hide_root(true); vbox->add_margin_child(ETR("Directories & Files:"), tree, true); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 6ef60a0f4f..28978dbed3 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -188,8 +188,10 @@ private: virtual void shortcut_input(const Ref<InputEvent> &p_event) override; + bool _can_use_native_popup(); void _native_popup(); - void _native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options); + void _native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter); + void _native_dialog_cb_with_options(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options); bool _is_open_should_be_disabled(); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 646757008a..7e07f4a202 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -317,7 +317,7 @@ bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, con void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) { ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing."); - for (const List<Ref<Connection>>::Element *E = connections.front(); E; E = E->next()) { + for (List<Ref<Connection>>::Element *E = connections.front(); E; E = E->next()) { if (E->get()->from_node == p_from && E->get()->from_port == p_from_port && E->get()->to_node == p_to && E->get()->to_port == p_to_port) { connection_map[p_from].erase(E->get()); connection_map[p_to].erase(E->get()); @@ -733,14 +733,14 @@ void GraphEdit::_update_theme_item_cache() { void GraphEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - zoom_minus_button->set_icon(theme_cache.zoom_out); - zoom_reset_button->set_icon(theme_cache.zoom_reset); - zoom_plus_button->set_icon(theme_cache.zoom_in); + zoom_minus_button->set_button_icon(theme_cache.zoom_out); + zoom_reset_button->set_button_icon(theme_cache.zoom_reset); + zoom_plus_button->set_button_icon(theme_cache.zoom_in); - toggle_snapping_button->set_icon(theme_cache.snapping_toggle); - toggle_grid_button->set_icon(theme_cache.grid_toggle); - minimap_button->set_icon(theme_cache.minimap_toggle); - arrange_button->set_icon(theme_cache.layout); + toggle_snapping_button->set_button_icon(theme_cache.snapping_toggle); + toggle_grid_button->set_button_icon(theme_cache.grid_toggle); + minimap_button->set_button_icon(theme_cache.minimap_toggle); + arrange_button->set_button_icon(theme_cache.layout); zoom_label->set_custom_minimum_size(Size2(48, 0) * theme_cache.base_scale); @@ -3001,5 +3001,5 @@ GraphEdit::GraphEdit() { set_clip_contents(true); - arranger = Ref<GraphEditArranger>(memnew(GraphEditArranger(this))); + arranger.instantiate(this); } diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 42b4e56b48..7a0e5b8867 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -335,6 +335,121 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col } } +void Label::_ensure_shaped() const { + if (dirty || font_dirty || lines_dirty) { + const_cast<Label *>(this)->_shape(); + } +} + +RID Label::get_line_rid(int p_line) const { + return lines_rid[p_line]; +} + +Rect2 Label::get_line_rect(int p_line) const { + // Returns a rect providing the line's horizontal offset and total size. To determine the vertical + // offset, use r_offset and r_line_spacing from get_layout_data. + bool rtl = TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL; + bool rtl_layout = is_layout_rtl(); + Ref<StyleBox> style = theme_cache.normal_style; + Size2 size = get_size(); + Size2 line_size = TS->shaped_text_get_size(lines_rid[p_line]); + Vector2 offset; + + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) { + offset.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + offset.x = style->get_offset().x; + } + break; + case HORIZONTAL_ALIGNMENT_LEFT: { + if (rtl_layout) { + offset.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + offset.x = style->get_offset().x; + } + } break; + case HORIZONTAL_ALIGNMENT_CENTER: { + offset.x = int(size.width - line_size.width) / 2; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (rtl_layout) { + offset.x = style->get_offset().x; + } else { + offset.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } + } break; + } + + return Rect2(offset, line_size); +} + +void Label::get_layout_data(Vector2 &r_offset, int &r_line_limit, int &r_line_spacing) const { + // Computes several common parameters involved in laying out and rendering text set to this label. + // Only vertical margin is considered in r_offset: use get_line_rect to get the horizontal offset + // for a given line of text. + Size2 size = get_size(); + Ref<StyleBox> style = theme_cache.normal_style; + int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; + + float total_h = 0.0; + int lines_visible = 0; + + // Get number of lines to fit to the height. + for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { + break; + } + lines_visible++; + } + + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { + lines_visible = max_lines_visible; + } + + r_line_limit = MIN(lines_rid.size(), lines_visible + lines_skipped); + + // Get real total height. + total_h = 0; + for (int64_t i = lines_skipped; i < r_line_limit; i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + } + total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); + + int vbegin = 0, vsep = 0; + if (lines_visible > 0) { + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. + } break; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (size.y - (total_h - line_spacing)) / 2; + vsep = 0; + + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = size.y - (total_h - line_spacing); + vsep = 0; + + } break; + case VERTICAL_ALIGNMENT_FILL: { + vbegin = 0; + if (lines_visible > 1) { + vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); + } else { + vsep = 0; + } + + } break; + } + } + + r_offset = { 0, style->get_offset().y + vbegin }; + r_line_spacing = line_spacing + vsep; +} + PackedStringArray Label::get_configuration_warnings() const { PackedStringArray warnings = Control::get_configuration_warnings(); @@ -361,10 +476,7 @@ PackedStringArray Label::get_configuration_warnings() const { } if (font.is_valid()) { - if (dirty || font_dirty || lines_dirty) { - const_cast<Label *>(this)->_shape(); - } - + _ensure_shaped(); const Glyph *glyph = TS->shaped_text_get_glyphs(text_rid); int64_t glyph_count = TS->shaped_text_get_glyph_count(text_rid); for (int64_t i = 0; i < glyph_count; i++) { @@ -416,22 +528,17 @@ void Label::_notification(int p_what) { } } - if (dirty || font_dirty || lines_dirty) { - _shape(); - } + _ensure_shaped(); RID ci = get_canvas_item(); bool has_settings = settings.is_valid(); Size2 string_size; - Size2 size = get_size(); Ref<StyleBox> style = theme_cache.normal_style; - Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; Color font_color = has_settings ? settings->get_font_color() : theme_cache.font_color; Color font_shadow_color = has_settings ? settings->get_shadow_color() : theme_cache.font_shadow_color; Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : theme_cache.font_shadow_offset; - int line_spacing = has_settings ? settings->get_line_spacing() : theme_cache.line_spacing; Color font_outline_color = has_settings ? settings->get_outline_color() : theme_cache.font_outline_color; int outline_size = has_settings ? settings->get_outline_size() : theme_cache.font_outline_size; int shadow_outline_size = has_settings ? settings->get_shadow_size() : theme_cache.font_shadow_outline_size; @@ -440,98 +547,28 @@ void Label::_notification(int p_what) { style->draw(ci, Rect2(Point2(0, 0), get_size())); - float total_h = 0.0; - int lines_visible = 0; - - // Get number of lines to fit to the height. - for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; - if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { - break; - } - lines_visible++; - } - - if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { - lines_visible = max_lines_visible; - } - - int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == TextServer::VC_CHARS_AFTER_SHAPING); bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_LTR) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && !rtl_layout)); bool trim_glyphs_rtl = (visible_chars >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_RTL) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && rtl_layout)); - // Get real total height. + Vector2 ofs; + int line_limit; + int line_spacing; + get_layout_data(ofs, line_limit, line_spacing); + + int processed_glyphs = 0; int total_glyphs = 0; - total_h = 0; - for (int64_t i = lines_skipped; i < last_line; i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + + for (int64_t i = lines_skipped; i < line_limit; i++) { total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); } - int visible_glyphs = total_glyphs * visible_ratio; - int processed_glyphs = 0; - total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); - - int vbegin = 0, vsep = 0; - if (lines_visible > 0) { - switch (vertical_alignment) { - case VERTICAL_ALIGNMENT_TOP: { - // Nothing. - } break; - case VERTICAL_ALIGNMENT_CENTER: { - vbegin = (size.y - (total_h - line_spacing)) / 2; - vsep = 0; - - } break; - case VERTICAL_ALIGNMENT_BOTTOM: { - vbegin = size.y - (total_h - line_spacing); - vsep = 0; - - } break; - case VERTICAL_ALIGNMENT_FILL: { - vbegin = 0; - if (lines_visible > 1) { - vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); - } else { - vsep = 0; - } - } break; - } - } + int visible_glyphs = total_glyphs * visible_ratio; - Vector2 ofs; - ofs.y = style->get_offset().y + vbegin; - for (int i = lines_skipped; i < last_line; i++) { - Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); - ofs.x = 0; + for (int i = lines_skipped; i < line_limit; i++) { + Vector2 line_offset = get_line_rect(i).position; + ofs.x = line_offset.x; ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); - switch (horizontal_alignment) { - case HORIZONTAL_ALIGNMENT_FILL: - if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } else { - ofs.x = style->get_offset().x; - } - break; - case HORIZONTAL_ALIGNMENT_LEFT: { - if (rtl_layout) { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } else { - ofs.x = style->get_offset().x; - } - } break; - case HORIZONTAL_ALIGNMENT_CENTER: { - ofs.x = int(size.width - line_size.width) / 2; - } break; - case HORIZONTAL_ALIGNMENT_RIGHT: { - if (rtl_layout) { - ofs.x = style->get_offset().x; - } else { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } - } break; - } const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); @@ -621,7 +658,7 @@ void Label::_notification(int p_what) { } } } - ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing; + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; } } break; @@ -637,102 +674,16 @@ void Label::_notification(int p_what) { } Rect2 Label::get_character_bounds(int p_pos) const { - if (dirty || font_dirty || lines_dirty) { - const_cast<Label *>(this)->_shape(); - } - - bool has_settings = settings.is_valid(); - Size2 size = get_size(); - Ref<StyleBox> style = theme_cache.normal_style; - int line_spacing = has_settings ? settings->get_line_spacing() : theme_cache.line_spacing; - bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL); - bool rtl_layout = is_layout_rtl(); - - float total_h = 0.0; - int lines_visible = 0; - - // Get number of lines to fit to the height. - for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; - if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { - break; - } - lines_visible++; - } - - if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { - lines_visible = max_lines_visible; - } - - int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); - - // Get real total height. - total_h = 0; - for (int64_t i = lines_skipped; i < last_line; i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; - } - - total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); - - int vbegin = 0, vsep = 0; - if (lines_visible > 0) { - switch (vertical_alignment) { - case VERTICAL_ALIGNMENT_TOP: { - // Nothing. - } break; - case VERTICAL_ALIGNMENT_CENTER: { - vbegin = (size.y - (total_h - line_spacing)) / 2; - vsep = 0; - - } break; - case VERTICAL_ALIGNMENT_BOTTOM: { - vbegin = size.y - (total_h - line_spacing); - vsep = 0; - - } break; - case VERTICAL_ALIGNMENT_FILL: { - vbegin = 0; - if (lines_visible > 1) { - vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); - } else { - vsep = 0; - } - - } break; - } - } + _ensure_shaped(); Vector2 ofs; - ofs.y = style->get_offset().y + vbegin; - for (int i = lines_skipped; i < last_line; i++) { - Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); - ofs.x = 0; - switch (horizontal_alignment) { - case HORIZONTAL_ALIGNMENT_FILL: - if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } else { - ofs.x = style->get_offset().x; - } - break; - case HORIZONTAL_ALIGNMENT_LEFT: { - if (rtl_layout) { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } else { - ofs.x = style->get_offset().x; - } - } break; - case HORIZONTAL_ALIGNMENT_CENTER: { - ofs.x = int(size.width - line_size.width) / 2; - } break; - case HORIZONTAL_ALIGNMENT_RIGHT: { - if (rtl_layout) { - ofs.x = style->get_offset().x; - } else { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } - } break; - } + int line_limit; + int line_spacing; + get_layout_data(ofs, line_limit, line_spacing); + + for (int i = lines_skipped; i < line_limit; i++) { + Rect2 line_rect = get_line_rect(i); + ofs.x = line_rect.position.x; int v_size = TS->shaped_text_get_glyph_count(lines_rid[i]); const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); @@ -746,22 +697,19 @@ Rect2 Label::get_character_bounds(int p_pos) const { } Rect2 rect; rect.position = ofs + Vector2(gl_off, 0); - rect.size = Vector2(advance, TS->shaped_text_get_size(lines_rid[i]).y); + rect.size = Vector2(advance, line_rect.size.y); return rect; } } gl_off += glyphs[j].advance * glyphs[j].repeat; } - ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; } return Rect2(); } Size2 Label::get_minimum_size() const { - // don't want to mutable everything - if (dirty || font_dirty || lines_dirty) { - const_cast<Label *>(this)->_shape(); - } + _ensure_shaped(); Size2 min_size = minsize; @@ -798,10 +746,7 @@ int Label::get_line_count() const { if (!is_inside_tree()) { return 1; } - if (dirty || font_dirty || lines_dirty) { - const_cast<Label *>(this)->_shape(); - } - + _ensure_shaped(); return lines_rid.size(); } @@ -1104,10 +1049,7 @@ int Label::get_max_lines_visible() const { } int Label::get_total_character_count() const { - if (dirty || font_dirty || lines_dirty) { - const_cast<Label *>(this)->_shape(); - } - + _ensure_shaped(); return xl_text.length(); } diff --git a/scene/gui/label.h b/scene/gui/label.h index e0ebca944a..2576d21c33 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -91,11 +91,16 @@ private: int font_shadow_outline_size; } theme_cache; + void _ensure_shaped() const; void _update_visible(); void _shape(); void _invalidate(); protected: + RID get_line_rid(int p_line) const; + Rect2 get_line_rect(int p_line) const; + void get_layout_data(Vector2 &r_offset, int &r_line_limit, int &r_line_spacing) const; + void _notification(int p_what); static void _bind_methods(); #ifndef DISABLE_DEPRECATED diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 9967805134..3f979f7c20 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -128,6 +128,7 @@ bool LineEdit::has_ime_text() const { void LineEdit::cancel_ime() { if (!has_ime_text()) { + _close_ime_window(); return; } ime_text = String(); @@ -140,6 +141,7 @@ void LineEdit::cancel_ime() { void LineEdit::apply_ime() { if (!has_ime_text()) { + _close_ime_window(); return; } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 5432058f7b..79018c40f5 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -225,7 +225,7 @@ void OptionButton::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { popup->set_item_icon(p_idx, p_icon); if (current == p_idx) { - set_icon(p_icon); + set_button_icon(p_icon); } _queue_update_size_cache(); } @@ -381,7 +381,7 @@ void OptionButton::_select(int p_which, bool p_emit) { current = NONE_SELECTED; set_text(""); - set_icon(nullptr); + set_button_icon(nullptr); } else { ERR_FAIL_INDEX(p_which, popup->get_item_count()); @@ -391,7 +391,7 @@ void OptionButton::_select(int p_which, bool p_emit) { current = p_which; set_text(popup->get_item_text(current)); - set_icon(popup->get_item_icon(current)); + set_button_icon(popup->get_item_icon(current)); } if (is_inside_tree() && p_emit) { diff --git a/scene/gui/range.h b/scene/gui/range.h index b1c2446ded..710fed8645 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -64,7 +64,7 @@ class Range : public Control { protected: virtual void _value_changed(double p_value); - void _notify_shared_value_changed() { shared->emit_value_changed(); }; + void _notify_shared_value_changed() { shared->emit_value_changed(); } static void _bind_methods(); diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h index 681f068fb2..94e1bd4f04 100644 --- a/scene/gui/rich_text_effect.h +++ b/scene/gui/rich_text_effect.h @@ -79,20 +79,20 @@ public: Color get_color() { return color; } void set_color(Color p_color) { color = p_color; } - uint32_t get_glyph_index() const { return glyph_index; }; - void set_glyph_index(uint32_t p_glyph_index) { glyph_index = p_glyph_index; }; + uint32_t get_glyph_index() const { return glyph_index; } + void set_glyph_index(uint32_t p_glyph_index) { glyph_index = p_glyph_index; } - uint16_t get_glyph_flags() const { return glyph_flags; }; - void set_glyph_flags(uint16_t p_glyph_flags) { glyph_flags = p_glyph_flags; }; + uint16_t get_glyph_flags() const { return glyph_flags; } + void set_glyph_flags(uint16_t p_glyph_flags) { glyph_flags = p_glyph_flags; } - uint8_t get_glyph_count() const { return glyph_count; }; - void set_glyph_count(uint8_t p_glyph_count) { glyph_count = p_glyph_count; }; + uint8_t get_glyph_count() const { return glyph_count; } + void set_glyph_count(uint8_t p_glyph_count) { glyph_count = p_glyph_count; } - int32_t get_relative_index() const { return relative_index; }; - void set_relative_index(int32_t p_relative_index) { relative_index = p_relative_index; }; + int32_t get_relative_index() const { return relative_index; } + void set_relative_index(int32_t p_relative_index) { relative_index = p_relative_index; } - RID get_font() const { return font; }; - void set_font(RID p_font) { font = p_font; }; + RID get_font() const { return font; } + void set_font(RID p_font) { font = p_font; } Dictionary get_environment() { return environment; } void set_environment(Dictionary p_environment) { environment = p_environment; } diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 1ac0e8b59f..312b538a99 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -461,7 +461,7 @@ void ScrollContainer::update_scrollbars() { void ScrollContainer::_scroll_moved(float) { queue_sort(); -}; +} void ScrollContainer::set_h_scroll(int p_pos) { h_scroll->set_value(p_pos); @@ -625,7 +625,7 @@ void ScrollContainer::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, panel_style, "panel"); GLOBAL_DEF("gui/common/default_scroll_deadzone", 0); -}; +} ScrollContainer::ScrollContainer() { h_scroll = memnew(HScrollBar); @@ -641,4 +641,4 @@ ScrollContainer::ScrollContainer() { deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone"); set_clip_contents(true); -}; +} diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 6b5ff23436..b1918ff23f 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -3169,6 +3169,7 @@ bool TextEdit::has_ime_text() const { void TextEdit::cancel_ime() { if (!has_ime_text()) { + _close_ime_window(); return; } ime_text = String(); @@ -3181,6 +3182,7 @@ void TextEdit::cancel_ime() { void TextEdit::apply_ime() { if (!has_ime_text()) { + _close_ime_window(); return; } @@ -5069,7 +5071,7 @@ bool TextEdit::multicaret_edit_ignore_caret(int p_caret) const { } bool TextEdit::is_caret_visible(int p_caret) const { - ERR_FAIL_INDEX_V(p_caret, carets.size(), 0); + ERR_FAIL_INDEX_V(p_caret, carets.size(), false); return carets[p_caret].visible; } @@ -5736,7 +5738,7 @@ TextServer::AutowrapMode TextEdit::get_autowrap_mode() const { } bool TextEdit::is_line_wrapped(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), 0); + ERR_FAIL_INDEX_V(p_line, text.size(), false); if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) { return false; } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 94b105d486..6b137581f2 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -139,7 +139,7 @@ private: Variant metadata; bool clickable = false; - Ref<Texture2D> icon = Ref<Texture2D>(); + Ref<Texture2D> icon; String text = ""; Color color = Color(1, 1, 1); }; @@ -693,9 +693,9 @@ protected: void _set_symbol_lookup_word(const String &p_symbol); // Theme items. - virtual Color _get_brace_mismatch_color() const { return Color(); }; - virtual Color _get_code_folding_color() const { return Color(); }; - virtual Ref<Texture2D> _get_folded_eol_icon() const { return Ref<Texture2D>(); }; + virtual Color _get_brace_mismatch_color() const { return Color(); } + virtual Color _get_code_folding_color() const { return Color(); } + virtual Ref<Texture2D> _get_folded_eol_icon() const { return Ref<Texture2D>(); } /* Text manipulation */ diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index c267ff93c6..47bb0643b3 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -340,11 +340,11 @@ Ref<BitMap> TextureButton::get_click_mask() const { Ref<Texture2D> TextureButton::get_texture_focused() const { return focused; -}; +} void TextureButton::set_texture_focused(const Ref<Texture2D> &p_focused) { focused = p_focused; -}; +} void TextureButton::_set_texture(Ref<Texture2D> *p_destination, const Ref<Texture2D> &p_texture) { DEV_ASSERT(p_destination); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 40917ee8f1..f6e05f5796 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1099,7 +1099,7 @@ void TreeItem::clear_children() { first_child = nullptr; last_child = nullptr; children_cache.clear(); -}; +} int TreeItem::get_index() { int idx = 0; @@ -1574,7 +1574,7 @@ Size2 TreeItem::get_minimum_size(int p_column) { const TreeItem::Cell &cell = cells[p_column]; - if (cell.cached_minimum_size_dirty) { + if (cell.cached_minimum_size_dirty || cell.text_buf->is_dirty() || cell.dirty) { Size2 size = Size2( parent_tree->theme_cache.inner_item_margin_left + parent_tree->theme_cache.inner_item_margin_right, parent_tree->theme_cache.inner_item_margin_top + parent_tree->theme_cache.inner_item_margin_bottom); @@ -1593,7 +1593,9 @@ Size2 TreeItem::get_minimum_size(int p_column) { // Icon. if (cell.mode == CELL_MODE_CHECK) { - size.width += parent_tree->theme_cache.checked->get_width() + parent_tree->theme_cache.h_separation; + Size2i check_size = parent_tree->theme_cache.checked->get_size(); + size.width += check_size.width + parent_tree->theme_cache.h_separation; + size.height = MAX(size.height, check_size.height); } if (cell.icon.is_valid()) { Size2i icon_size = parent_tree->_get_cell_icon_size(cell); @@ -1605,7 +1607,8 @@ Size2 TreeItem::get_minimum_size(int p_column) { for (int i = 0; i < cell.buttons.size(); i++) { Ref<Texture2D> texture = cell.buttons[i].texture; if (texture.is_valid()) { - Size2 button_size = texture->get_size() + parent_tree->theme_cache.button_pressed->get_minimum_size(); + Size2 button_size = texture->get_size(); + button_size.width += parent_tree->theme_cache.button_pressed->get_minimum_size().width; size.width += button_size.width + parent_tree->theme_cache.button_margin; size.height = MAX(size.height, button_size.height); } @@ -1889,44 +1892,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { int height = 0; for (int i = 0; i < columns.size(); i++) { - if (p_item->cells[i].dirty) { - const_cast<Tree *>(this)->update_item_cell(p_item, i); - } - height = MAX(height, p_item->cells[i].text_buf->get_size().y); - for (int j = 0; j < p_item->cells[i].buttons.size(); j++) { - Size2i s; // = cache.button_pressed->get_minimum_size(); - s += p_item->cells[i].buttons[j].texture->get_size(); - if (s.height > height) { - height = s.height; - } - } - - switch (p_item->cells[i].mode) { - case TreeItem::CELL_MODE_CHECK: { - int check_icon_h = theme_cache.checked->get_height(); - if (height < check_icon_h) { - height = check_icon_h; - } - [[fallthrough]]; - } - case TreeItem::CELL_MODE_STRING: - case TreeItem::CELL_MODE_CUSTOM: - case TreeItem::CELL_MODE_ICON: { - Ref<Texture2D> icon = p_item->cells[i].icon; - if (!icon.is_null()) { - Size2i s = _get_cell_icon_size(p_item->cells[i]); - if (s.height > height) { - height = s.height; - } - } - if (p_item->cells[i].mode == TreeItem::CELL_MODE_CUSTOM && p_item->cells[i].custom_button) { - height += theme_cache.custom_button->get_minimum_size().height; - } - - } break; - default: { - } - } + height = MAX(height, p_item->get_minimum_size(i).y); } int item_min_height = MAX(theme_cache.font->get_height(theme_cache.font_size), p_item->get_custom_minimum_height()); if (height < item_min_height) { @@ -4702,6 +4668,7 @@ void Tree::item_edited(int p_column, TreeItem *p_item, MouseButton p_custom_mous edited_col = p_column; if (p_item != nullptr && p_column >= 0 && p_column < p_item->cells.size()) { edited_item->cells.write[p_column].dirty = true; + edited_item->cells.write[p_column].cached_minimum_size_dirty = true; } emit_signal(SNAME("item_edited")); if (p_custom_mouse_index != MouseButton::NONE) { @@ -4827,7 +4794,7 @@ void Tree::clear() { _determine_hovered_item(); queue_redraw(); -}; +} void Tree::set_hide_root(bool p_enabled) { if (hide_root == p_enabled) { @@ -5362,14 +5329,16 @@ void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) { int y_offset = get_item_offset(p_item); if (y_offset != -1) { - const int tbh = _get_title_button_height(); - y_offset -= tbh; + const int title_button_height = _get_title_button_height(); + y_offset -= title_button_height; const int cell_h = compute_item_height(p_item) + theme_cache.v_separation; - int screen_h = area_size.height - tbh; + int screen_h = area_size.height - title_button_height; if (p_center_on_item) { - v_scroll->set_value(y_offset - (screen_h - cell_h) / 2.0f); + // This makes sure that centering the offset doesn't overflow. + const double v_scroll_value = y_offset - MAX((screen_h - cell_h) / 2.0, 0.0); + v_scroll->set_value(v_scroll_value); } else { if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet. v_scroll->set_value(y_offset); diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index 0b521f926d..878bceccbc 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -178,6 +178,7 @@ void VideoStreamPlayer::_notification(int p_notification) { draw_texture_rect(texture, Rect2(Point2(), s), false); } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (is_playing() && !is_paused()) { paused_from_tree = true; @@ -189,6 +190,13 @@ void VideoStreamPlayer::_notification(int p_notification) { } } break; + case NOTIFICATION_UNSUSPENDED: { + if (get_tree()->is_paused()) { + break; + } + [[fallthrough]]; + } + case NOTIFICATION_UNPAUSED: { if (paused_from_tree) { paused_from_tree = false; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 7c8bf9c809..f87dad1889 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -44,7 +44,7 @@ #define ERR_DRAW_GUARD \ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside this node's `_draw()`, functions connected to its `draw` signal, or when it receives NOTIFICATION_DRAW.") -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { if (_edit_use_rect()) { return _edit_get_rect().has_point(p_point); @@ -52,11 +52,13 @@ bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tole return p_point.length() < p_tolerance; } } +#endif // DEBUG_ENABLED +#ifdef TOOLS_ENABLED Transform2D CanvasItem::_edit_get_transform() const { return Transform2D(_edit_get_rotation(), _edit_get_position() + _edit_get_pivot()); } -#endif +#endif //TOOLS_ENABLED bool CanvasItem::is_visible_in_tree() const { ERR_READ_THREAD_GUARD_V(false); @@ -1158,7 +1160,7 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("_edit_get_pivot"), &CanvasItem::_edit_get_pivot); ClassDB::bind_method(D_METHOD("_edit_use_pivot"), &CanvasItem::_edit_use_pivot); ClassDB::bind_method(D_METHOD("_edit_get_transform"), &CanvasItem::_edit_get_transform); -#endif +#endif //TOOLS_ENABLED ClassDB::bind_method(D_METHOD("get_canvas_item"), &CanvasItem::get_canvas_item); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 028c2cb2cf..c74f8238e3 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -174,7 +174,7 @@ protected: void _draw_multiline_bind_compat_84523(const Vector<Point2> &p_points, const Color &p_color, real_t p_width); void _draw_multiline_colors_bind_compat_84523(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width); static void _bind_compatibility_methods(); -#endif +#endif // DISABLE_DEPRECATED void _validate_property(PropertyInfo &p_property) const; @@ -193,11 +193,9 @@ public: NOTIFICATION_WORLD_2D_CHANGED = 36, }; - /* EDITOR */ -#ifdef TOOLS_ENABLED - // Select the node - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + /* EDITOR AND DEBUGGING */ +#ifdef TOOLS_ENABLED // Save and restore a CanvasItem state virtual void _edit_set_state(const Dictionary &p_state) {} virtual Dictionary _edit_get_state() const { return Dictionary(); } @@ -211,23 +209,32 @@ public: virtual Size2 _edit_get_scale() const = 0; // Used to rotate the node - virtual bool _edit_use_rotation() const { return false; }; + virtual bool _edit_use_rotation() const { return false; } virtual void _edit_set_rotation(real_t p_rotation) {} - virtual real_t _edit_get_rotation() const { return 0.0; }; + virtual real_t _edit_get_rotation() const { return 0.0; } // Used to resize/move the node - virtual bool _edit_use_rect() const { return false; }; // MAYBE REPLACE BY A _edit_get_editmode() virtual void _edit_set_rect(const Rect2 &p_rect) {} - virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); }; - virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); }; // LOOKS WEIRD + virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); } // LOOKS WEIRD // Used to set a pivot - virtual bool _edit_use_pivot() const { return false; }; + virtual bool _edit_use_pivot() const { return false; } virtual void _edit_set_pivot(const Point2 &p_pivot) {} - virtual Point2 _edit_get_pivot() const { return Point2(); }; + virtual Point2 _edit_get_pivot() const { return Point2(); } virtual Transform2D _edit_get_transform() const; -#endif +#endif // TOOLS_ENABLED + +#ifdef DEBUG_ENABLED + // Those need to be available in debug runtime, to allow for node selection. + + // Select the node. + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + + // Used to resize/move the node. + virtual bool _edit_use_rect() const { return false; } // Maybe replace with _edit_get_editmode(). + virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); } +#endif // DEBUG_ENABLED void update_draw_order(); @@ -375,7 +382,7 @@ public: TextureRepeat get_texture_repeat_in_tree() const; // Used by control nodes to retrieve the parent's anchorable area - virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); }; + virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); } int get_canvas_layer() const; CanvasLayer *get_canvas_layer_node() const; diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index 29166f3d92..9222ce6df3 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -245,7 +245,7 @@ Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) { } return ret; -}; +} void InstancePlaceholder::_bind_methods() { ClassDB::bind_method(D_METHOD("get_stored_values", "with_order"), &InstancePlaceholder::get_stored_values, DEFVAL(false)); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index d921cc5b67..8dc7b4a87c 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -184,6 +184,7 @@ void Node::_notification(int p_notification) { } } break; + case NOTIFICATION_SUSPENDED: case NOTIFICATION_PAUSED: { if (is_physics_interpolated_and_enabled() && is_inside_tree()) { reset_physics_interpolation(); @@ -695,6 +696,16 @@ void Node::_propagate_pause_notification(bool p_enable) { data.blocked--; } +void Node::_propagate_suspend_notification(bool p_enable) { + notification(p_enable ? NOTIFICATION_SUSPENDED : NOTIFICATION_UNSUSPENDED); + + data.blocked++; + for (KeyValue<StringName, Node *> &KV : data.children) { + KV.value->_propagate_suspend_notification(p_enable); + } + data.blocked--; +} + Node::ProcessMode Node::get_process_mode() const { return data.process_mode; } @@ -850,7 +861,7 @@ bool Node::can_process_notification(int p_what) const { bool Node::can_process() const { ERR_FAIL_COND_V(!is_inside_tree(), false); - return _can_process(get_tree()->is_paused()); + return !get_tree()->is_suspended() && _can_process(get_tree()->is_paused()); } bool Node::_can_process(bool p_paused) const { diff --git a/scene/main/node.h b/scene/main/node.h index 799478fa35..e2f3ce9b78 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -300,6 +300,7 @@ private: void _set_tree(SceneTree *p_tree); void _propagate_pause_notification(bool p_enable); + void _propagate_suspend_notification(bool p_enable); _FORCE_INLINE_ bool _can_process(bool p_paused) const; _FORCE_INLINE_ bool _is_enabled() const; @@ -439,6 +440,8 @@ public: // Editor specific node notifications NOTIFICATION_EDITOR_PRE_SAVE = 9001, NOTIFICATION_EDITOR_POST_SAVE = 9002, + NOTIFICATION_SUSPENDED = 9003, + NOTIFICATION_UNSUSPENDED = 9004 }; /* NODE/TREE */ diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 71d91b970e..16b41c8f9b 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -899,7 +899,7 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() { return debug_contact_mesh; } - debug_contact_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_contact_mesh.instantiate(); Ref<StandardMaterial3D> mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); @@ -954,11 +954,14 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() { void SceneTree::set_pause(bool p_enabled) { ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Pause can only be set from the main thread."); + ERR_FAIL_COND_MSG(suspended, "Pause state cannot be modified while suspended."); if (p_enabled == paused) { return; } + paused = p_enabled; + #ifndef _3D_DISABLED PhysicsServer3D::get_singleton()->set_active(!p_enabled); #endif // _3D_DISABLED @@ -972,6 +975,30 @@ bool SceneTree::is_paused() const { return paused; } +void SceneTree::set_suspend(bool p_enabled) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Suspend can only be set from the main thread."); + + if (p_enabled == suspended) { + return; + } + + suspended = p_enabled; + + Engine::get_singleton()->set_freeze_time_scale(p_enabled); + +#ifndef _3D_DISABLED + PhysicsServer3D::get_singleton()->set_active(!p_enabled && !paused); +#endif // _3D_DISABLED + PhysicsServer2D::get_singleton()->set_active(!p_enabled && !paused); + if (get_root()) { + get_root()->_propagate_suspend_notification(p_enabled); + } +} + +bool SceneTree::is_suspended() const { + return suspended; +} + void SceneTree::_process_group(ProcessGroup *p_group, bool p_physics) { // When reading this function, keep in mind that this code must work in a way where // if any node is removed, this needs to continue working. diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 7e44541105..291e4a5a0c 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -143,6 +143,7 @@ private: bool debug_navigation_hint = false; #endif bool paused = false; + bool suspended = false; HashMap<StringName, Group> group_map; bool _quit = false; @@ -343,6 +344,8 @@ public: void set_pause(bool p_enabled); bool is_paused() const; + void set_suspend(bool p_enabled); + bool is_suspended() const; #ifdef DEBUG_ENABLED void set_debug_collisions_hint(bool p_enabled); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8755d5f51e..1ee99099ec 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1213,7 +1213,7 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) { } } else { WARN_PRINT("Invalid world_2d"); - world_2d = Ref<World2D>(memnew(World2D)); + world_2d.instantiate(); } world_2d->register_viewport(this); @@ -1478,12 +1478,12 @@ void Viewport::_gui_show_tooltip() { gui.tooltip_label = memnew(Label); gui.tooltip_label->set_theme_type_variation(SNAME("TooltipLabel")); gui.tooltip_label->set_text(gui.tooltip_text); + gui.tooltip_label->set_auto_translate_mode(tooltip_owner->get_tooltip_auto_translate_mode()); base_tooltip = gui.tooltip_label; panel->connect(SceneStringName(mouse_entered), callable_mp(this, &Viewport::_gui_cancel_tooltip)); } base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - base_tooltip->set_auto_translate_mode(tooltip_owner->get_tooltip_auto_translate_mode()); panel->set_transient(true); panel->set_flag(Window::FLAG_NO_FOCUS, true); @@ -3123,7 +3123,7 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(p_event.is_null()); - if (disable_input) { + if (disable_input || disable_input_override) { return; } @@ -3195,7 +3195,7 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local local_input_handled = false; - if (disable_input || !_can_consume_input_events()) { + if (disable_input || disable_input_override || !_can_consume_input_events()) { return; } @@ -3298,7 +3298,7 @@ void Viewport::set_disable_input(bool p_disable) { if (p_disable == disable_input) { return; } - if (p_disable) { + if (p_disable && !disable_input_override) { _drop_mouse_focus(); _mouse_leave_viewport(); _gui_cancel_tooltip(); @@ -3311,6 +3311,19 @@ bool Viewport::is_input_disabled() const { return disable_input; } +void Viewport::set_disable_input_override(bool p_disable) { + ERR_MAIN_THREAD_GUARD; + if (p_disable == disable_input_override) { + return; + } + if (p_disable && !disable_input) { + _drop_mouse_focus(); + _mouse_leave_viewport(); + _gui_cancel_tooltip(); + } + disable_input_override = p_disable; +} + Variant Viewport::gui_get_drag_data() const { ERR_READ_THREAD_GUARD_V(Variant()); return get_section_root_viewport()->gui.drag_data; @@ -4237,6 +4250,22 @@ void Viewport::set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, } } +HashMap<StringName, real_t> Viewport::get_camera_3d_override_properties() const { + HashMap<StringName, real_t> props; + + props["size"] = 0; + props["fov"] = 0; + props["z_near"] = 0; + props["z_far"] = 0; + ERR_READ_THREAD_GUARD_V(props); + + props["size"] = camera_3d_override.size; + props["fov"] = camera_3d_override.fov; + props["z_near"] = camera_3d_override.z_near; + props["z_far"] = camera_3d_override.z_far; + return props; +} + void Viewport::set_disable_3d(bool p_disable) { ERR_MAIN_THREAD_GUARD; disable_3d = p_disable; @@ -4270,6 +4299,54 @@ Transform3D Viewport::get_camera_3d_override_transform() const { return Transform3D(); } +Vector3 Viewport::camera_3d_override_project_ray_normal(const Point2 &p_pos) const { + ERR_READ_THREAD_GUARD_V(Vector3()); + Vector3 ray = camera_3d_override_project_local_ray_normal(p_pos); + return camera_3d_override.transform.basis.xform(ray).normalized(); +} + +Vector3 Viewport::camera_3d_override_project_local_ray_normal(const Point2 &p_pos) const { + ERR_READ_THREAD_GUARD_V(Vector3()); + Size2 viewport_size = get_camera_rect_size(); + Vector2 cpos = get_camera_coords(p_pos); + Vector3 ray; + + if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) { + ray = Vector3(0, 0, -1); + } else { + Projection cm; + cm.set_perspective(camera_3d_override.fov, get_visible_rect().size.aspect(), camera_3d_override.z_near, camera_3d_override.z_far, false); + + Vector2 screen_he = cm.get_viewport_half_extents(); + ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -camera_3d_override.z_near).normalized(); + } + + return ray; +} + +Vector3 Viewport::camera_3d_override_project_ray_origin(const Point2 &p_pos) const { + ERR_READ_THREAD_GUARD_V(Vector3()); + Size2 viewport_size = get_camera_rect_size(); + Vector2 cpos = get_camera_coords(p_pos); + ERR_FAIL_COND_V(viewport_size.y == 0, Vector3()); + + if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) { + Vector2 pos = cpos / viewport_size; + real_t vsize, hsize; + hsize = camera_3d_override.size * viewport_size.aspect(); + vsize = camera_3d_override.size; + + Vector3 ray; + ray.x = pos.x * (hsize)-hsize / 2; + ray.y = (1.0 - pos.y) * (vsize)-vsize / 2; + ray.z = -camera_3d_override.z_near; + ray = camera_3d_override.transform.xform(ray); + return ray; + } else { + return camera_3d_override.transform.origin; + }; +} + Ref<World3D> Viewport::get_world_3d() const { ERR_READ_THREAD_GUARD_V(Ref<World3D>()); return world_3d; @@ -4309,7 +4386,7 @@ void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) { own_world_3d = world_3d->duplicate(); world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed)); } else { - own_world_3d = Ref<World3D>(memnew(World3D)); + own_world_3d.instantiate(); } } @@ -4360,7 +4437,7 @@ void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) { own_world_3d = world_3d->duplicate(); world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed)); } else { - own_world_3d = Ref<World3D>(memnew(World3D)); + own_world_3d.instantiate(); } } else { own_world_3d = Ref<World3D>(); @@ -4886,7 +4963,7 @@ void Viewport::_validate_property(PropertyInfo &p_property) const { } Viewport::Viewport() { - world_2d = Ref<World2D>(memnew(World2D)); + world_2d.instantiate(); world_2d->register_viewport(this); viewport = RenderingServer::get_singleton()->viewport_create(); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index a18dc1f6f0..92691ccbec 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -401,6 +401,7 @@ private: DefaultCanvasItemTextureRepeat default_canvas_item_texture_repeat = DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; bool disable_input = false; + bool disable_input_override = false; void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input); void _gui_call_notification(Control *p_control, int p_what); @@ -580,6 +581,8 @@ public: void set_disable_input(bool p_disable); bool is_input_disabled() const; + void set_disable_input_override(bool p_disable); + Vector2 get_mouse_position() const; void warp_mouse(const Vector2 &p_position); virtual void update_mouse_cursor_state(); @@ -770,6 +773,11 @@ public: void set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far); void set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far); + HashMap<StringName, real_t> get_camera_3d_override_properties() const; + + Vector3 camera_3d_override_project_ray_normal(const Point2 &p_pos) const; + Vector3 camera_3d_override_project_ray_origin(const Point2 &p_pos) const; + Vector3 camera_3d_override_project_local_ray_normal(const Point2 &p_pos) const; void set_disable_3d(bool p_disable); bool is_3d_disabled() const; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 045c3ae02d..fc2fe4320b 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -31,10 +31,8 @@ #include "window.h" #include "core/config/project_settings.h" -#include "core/debugger/engine_debugger.h" #include "core/input/shortcut.h" #include "core/string/translation_server.h" -#include "core/variant/variant_parser.h" #include "scene/gui/control.h" #include "scene/theme/theme_db.h" #include "scene/theme/theme_owner.h" @@ -1631,35 +1629,6 @@ bool Window::_can_consume_input_events() const { void Window::_window_input(const Ref<InputEvent> &p_ev) { ERR_MAIN_THREAD_GUARD; - if (EngineDebugger::is_active()) { - // Quit from game window using the stop shortcut (F8 by default). - // The custom shortcut is provided via environment variable when running from the editor. - if (debugger_stop_shortcut.is_null()) { - String shortcut_str = OS::get_singleton()->get_environment("__GODOT_EDITOR_STOP_SHORTCUT__"); - if (!shortcut_str.is_empty()) { - Variant shortcut_var; - - VariantParser::StreamString ss; - ss.s = shortcut_str; - - String errs; - int line; - VariantParser::parse(&ss, shortcut_var, errs, line); - debugger_stop_shortcut = shortcut_var; - } - - if (debugger_stop_shortcut.is_null()) { - // Define a default shortcut if it wasn't provided or is invalid. - debugger_stop_shortcut.instantiate(); - debugger_stop_shortcut->set_events({ (Variant)InputEventKey::create_reference(Key::F8) }); - } - } - - Ref<InputEventKey> k = p_ev; - if (k.is_valid() && k->is_pressed() && !k->is_echo() && debugger_stop_shortcut->matches_event(k)) { - EngineDebugger::get_singleton()->send_message("request_quit", Array()); - } - } if (exclusive_child != nullptr) { if (!is_embedding_subwindows()) { // Not embedding, no need for event. diff --git a/scene/main/window.h b/scene/main/window.h index 6517350b78..0994fc6012 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -374,7 +374,7 @@ public: bool is_wrapping_controls() const; void child_controls_changed(); - Window *get_exclusive_child() const { return exclusive_child; }; + Window *get_exclusive_child() const { return exclusive_child; } Window *get_parent_visible_window() const; Viewport *get_parent_viewport() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 6b1ce2b4ca..315ccaeb8c 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -512,6 +512,9 @@ void register_scene_types() { GDREGISTER_CLASS(AnimationNodeStateMachine); GDREGISTER_CLASS(AnimationNodeStateMachinePlayback); + GDREGISTER_INTERNAL_CLASS(AnimationNodeStartState); + GDREGISTER_INTERNAL_CLASS(AnimationNodeEndState); + GDREGISTER_CLASS(AnimationNodeSync); GDREGISTER_CLASS(AnimationNodeStateMachineTransition); GDREGISTER_CLASS(AnimationNodeOutput); diff --git a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp index e3f14539a8..07e9caa713 100644 --- a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp +++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp @@ -43,7 +43,7 @@ void NavigationMeshSourceGeometryData2D::clear() { bool NavigationMeshSourceGeometryData2D::has_data() { RWLockRead read_lock(geometry_rwlock); return traversable_outlines.size(); -}; +} void NavigationMeshSourceGeometryData2D::clear_projected_obstructions() { RWLockWrite write_lock(geometry_rwlock); diff --git a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h index b29c106fb5..2812925770 100644 --- a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h +++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h @@ -36,6 +36,8 @@ #include "scene/resources/2d/navigation_polygon.h" class NavigationMeshSourceGeometryData2D : public Resource { + friend class NavMeshGenerator2D; + GDCLASS(NavigationMeshSourceGeometryData2D, Resource); RWLock geometry_rwlock; diff --git a/scene/resources/2d/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp index 3dfa906e3b..37240e8038 100644 --- a/scene/resources/2d/navigation_polygon.cpp +++ b/scene/resources/2d/navigation_polygon.cpp @@ -36,7 +36,7 @@ #include "thirdparty/misc/polypartition.h" -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 NavigationPolygon::_edit_get_rect() const { RWLockRead read_lock(rwlock); if (rect_cache_dirty) { @@ -79,7 +79,7 @@ bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double } return false; } -#endif +#endif // DEBUG_ENABLED void NavigationPolygon::set_vertices(const Vector<Vector2> &p_vertices) { RWLockWrite write_lock(rwlock); diff --git a/scene/resources/2d/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h index ed2c606c55..59e5eeed68 100644 --- a/scene/resources/2d/navigation_polygon.h +++ b/scene/resources/2d/navigation_polygon.h @@ -68,10 +68,11 @@ protected: TypedArray<Vector<Vector2>> _get_outlines() const; public: -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED Rect2 _edit_get_rect() const; bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; -#endif +#endif // DEBUG_ENABLED + enum SamplePartitionType { SAMPLE_PARTITION_CONVEX_PARTITION = 0, SAMPLE_PARTITION_TRIANGULATE, diff --git a/scene/resources/2d/skeleton/skeleton_modification_stack_2d.h b/scene/resources/2d/skeleton/skeleton_modification_stack_2d.h index 0732153997..d1e50cb702 100644 --- a/scene/resources/2d/skeleton/skeleton_modification_stack_2d.h +++ b/scene/resources/2d/skeleton/skeleton_modification_stack_2d.h @@ -64,7 +64,7 @@ public: execution_mode_physics_process }; - Vector<Ref<SkeletonModification2D>> modifications = Vector<Ref<SkeletonModification2D>>(); + Vector<Ref<SkeletonModification2D>> modifications; void setup(); void execute(float p_delta, int p_execution_mode); diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp index e624bdb32f..5ecfc32622 100644 --- a/scene/resources/2d/tile_set.cpp +++ b/scene/resources/2d/tile_set.cpp @@ -174,13 +174,13 @@ void TileMapPattern::set_size(const Size2i &p_size) { bool TileMapPattern::is_empty() const { return pattern.is_empty(); -}; +} void TileMapPattern::clear() { size = Size2i(); pattern.clear(); emit_changed(); -}; +} bool TileMapPattern::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "tile_data") { @@ -571,11 +571,11 @@ void TileSet::set_uv_clipping(bool p_uv_clipping) { bool TileSet::is_uv_clipping() const { return uv_clipping; -}; +} int TileSet::get_occlusion_layers_count() const { return occlusion_layers.size(); -}; +} void TileSet::add_occlusion_layer(int p_index) { if (p_index < 0) { @@ -699,6 +699,17 @@ uint32_t TileSet::get_physics_layer_collision_mask(int p_layer_index) const { return physics_layers[p_layer_index].collision_mask; } +void TileSet::set_physics_layer_collision_priority(int p_layer_index, real_t p_priority) { + ERR_FAIL_INDEX(p_layer_index, physics_layers.size()); + physics_layers.write[p_layer_index].collision_priority = p_priority; + emit_changed(); +} + +real_t TileSet::get_physics_layer_collision_priority(int p_layer_index) const { + ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), 0); + return physics_layers[p_layer_index].collision_priority; +} + void TileSet::set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material) { ERR_FAIL_INDEX(p_layer_index, physics_layers.size()); physics_layers.write[p_layer_index].physics_material = p_physics_material; @@ -3691,7 +3702,7 @@ Array TileSet::compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool return cannot_convert_array; break; } -}; +} #endif // DISABLE_DEPRECATED @@ -3900,6 +3911,13 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { } set_physics_layer_collision_mask(index, p_value); return true; + } else if (components[1] == "collision_priority") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::FLOAT, false); + while (index >= physics_layers.size()) { + add_physics_layer(); + } + set_physics_layer_collision_priority(index, p_value); + return true; } else if (components[1] == "physics_material") { Ref<PhysicsMaterial> physics_material = p_value; while (index >= physics_layers.size()) { @@ -4051,6 +4069,9 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "collision_mask") { r_ret = get_physics_layer_collision_mask(index); return true; + } else if (components[1] == "collision_priority") { + r_ret = get_physics_layer_collision_priority(index); + return true; } else if (components[1] == "physics_material") { r_ret = get_physics_layer_physics_material(index); return true; @@ -4176,6 +4197,13 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { } p_list->push_back(property_info); + // physics_layer_%d/collision_priority + property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/collision_priority", i)); + if (physics_layers[i].collision_priority == 1.0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + // physics_layer_%d/physics_material property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/physics_material", i), PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"); if (!physics_layers[i].physics_material.is_valid()) { @@ -4220,10 +4248,10 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { // Tile Proxies. // Note: proxies need to be set after sources are set. - p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Tile Proxies", ""), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/source_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/coords_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/alternative_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); // Patterns. for (unsigned int pattern_index = 0; pattern_index < patterns.size(); pattern_index++) { @@ -4287,6 +4315,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer); ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask); ClassDB::bind_method(D_METHOD("get_physics_layer_collision_mask", "layer_index"), &TileSet::get_physics_layer_collision_mask); + ClassDB::bind_method(D_METHOD("set_physics_layer_collision_priority", "layer_index", "priority"), &TileSet::set_physics_layer_collision_priority); + ClassDB::bind_method(D_METHOD("get_physics_layer_collision_priority", "layer_index"), &TileSet::get_physics_layer_collision_priority); ClassDB::bind_method(D_METHOD("set_physics_layer_physics_material", "layer_index", "physics_material"), &TileSet::set_physics_layer_physics_material); ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material); @@ -4432,7 +4462,7 @@ TileSet *TileSetSource::get_tile_set() const { void TileSetSource::reset_state() { tile_set = nullptr; -}; +} void TileSetSource::_bind_methods() { // Base tiles @@ -4931,10 +4961,13 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { } for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + const String formatted_key = itos(E_alternative.key); + // Add a dummy property to show the alternative exists. - tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + tile_property_list.push_back(PropertyInfo(Variant::INT, formatted_key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); // Get the alternative tile's properties and append them to the list of properties. + const String alternative_property_info_prefix = formatted_key + '/'; List<PropertyInfo> alternative_property_list; E_alternative.value->get_property_list(&alternative_property_list); for (PropertyInfo &alternative_property_info : alternative_property_list) { @@ -4943,14 +4976,15 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE; } - alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative.key), alternative_property_info.name); + alternative_property_info.name = alternative_property_info_prefix + alternative_property_info.name; tile_property_list.push_back(alternative_property_info); } } // Add all alternative. + const String property_info_prefix = vformat("%d:%d/", E_tile.key.x, E_tile.key.y); for (PropertyInfo &tile_property_info : tile_property_list) { - tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile.key.x, E_tile.key.y), tile_property_info.name); + tile_property_info.name = property_info_prefix + tile_property_info.name; p_list->push_back(tile_property_info); } } diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h index 15e1a16359..6d3ccd1d2d 100644 --- a/scene/resources/2d/tile_set.h +++ b/scene/resources/2d/tile_set.h @@ -278,7 +278,7 @@ public: bool operator==(const TerrainsPattern &p_terrains_pattern) const; bool operator!=(const TerrainsPattern &p_terrains_pattern) const { return !operator==(p_terrains_pattern); - }; + } void set_terrain(int p_terrain); int get_terrain() const; @@ -327,6 +327,7 @@ private: struct PhysicsLayer { uint32_t collision_layer = 1; uint32_t collision_mask = 1; + real_t collision_priority = 1.0; Ref<PhysicsMaterial> physics_material; }; Vector<PhysicsLayer> physics_layers; @@ -448,6 +449,8 @@ public: uint32_t get_physics_layer_collision_layer(int p_layer_index) const; void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask); uint32_t get_physics_layer_collision_mask(int p_layer_index) const; + void set_physics_layer_collision_priority(int p_layer_index, real_t p_priority); + real_t get_physics_layer_collision_priority(int p_layer_index) const; void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material); Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const; @@ -812,8 +815,8 @@ public: // Scenes accessors. Lot are similar to "Alternative tiles". int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); } - int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); }; - bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); }; + int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); } + bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); } int create_scene_tile(Ref<PackedScene> p_packed_scene = Ref<PackedScene>(), int p_id_override = -1); void set_scene_tile_id(int p_id, int p_new_id); void set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene); @@ -836,7 +839,7 @@ private: bool flip_v = false; bool transpose = false; Vector2i texture_origin; - Ref<Material> material = Ref<Material>(); + Ref<Material> material; Color modulate = Color(1.0, 1.0, 1.0, 1.0); int z_index = 0; int y_sort_origin = 0; diff --git a/scene/resources/3d/convex_polygon_shape_3d.cpp b/scene/resources/3d/convex_polygon_shape_3d.cpp index 3bfeeca461..586d5f4678 100644 --- a/scene/resources/3d/convex_polygon_shape_3d.cpp +++ b/scene/resources/3d/convex_polygon_shape_3d.cpp @@ -35,7 +35,7 @@ Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const { Vector<Vector3> poly_points = get_points(); - if (poly_points.size() > 3) { + if (poly_points.size() > 1) { // Need at least 2 points for a line. Vector<Vector3> varr = Variant(poly_points); Geometry3D::MeshData md; Error err = ConvexHullComputer::convex_hull(varr, md); diff --git a/scene/resources/3d/importer_mesh.cpp b/scene/resources/3d/importer_mesh.cpp index 47cd64f19a..f040f04cd8 100644 --- a/scene/resources/3d/importer_mesh.cpp +++ b/scene/resources/3d/importer_mesh.cpp @@ -33,108 +33,10 @@ #include "core/io/marshalls.h" #include "core/math/convex_hull.h" #include "core/math/random_pcg.h" -#include "core/math/static_raycaster.h" -#include "scene/resources/animation_library.h" #include "scene/resources/surface_tool.h" #include <cstdint> -void ImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) { - _split_normals(arrays, p_indices, p_normals); - - for (BlendShape &blend_shape : blend_shape_data) { - _split_normals(blend_shape.arrays, p_indices, p_normals); - } -} - -void ImporterMesh::Surface::_split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) { - ERR_FAIL_COND(r_arrays.size() != RS::ARRAY_MAX); - - const PackedVector3Array &vertices = r_arrays[RS::ARRAY_VERTEX]; - int current_vertex_count = vertices.size(); - int new_vertex_count = p_indices.size(); - int final_vertex_count = current_vertex_count + new_vertex_count; - const int *indices_ptr = p_indices.ptr(); - - for (int i = 0; i < r_arrays.size(); i++) { - if (i == RS::ARRAY_INDEX) { - continue; - } - - if (r_arrays[i].get_type() == Variant::NIL) { - continue; - } - - switch (r_arrays[i].get_type()) { - case Variant::PACKED_VECTOR3_ARRAY: { - PackedVector3Array data = r_arrays[i]; - data.resize(final_vertex_count); - Vector3 *data_ptr = data.ptrw(); - if (i == RS::ARRAY_NORMAL) { - const Vector3 *normals_ptr = p_normals.ptr(); - memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count); - } else { - for (int j = 0; j < new_vertex_count; j++) { - data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; - } - } - r_arrays[i] = data; - } break; - case Variant::PACKED_VECTOR2_ARRAY: { - PackedVector2Array data = r_arrays[i]; - data.resize(final_vertex_count); - Vector2 *data_ptr = data.ptrw(); - for (int j = 0; j < new_vertex_count; j++) { - data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; - } - r_arrays[i] = data; - } break; - case Variant::PACKED_FLOAT32_ARRAY: { - PackedFloat32Array data = r_arrays[i]; - int elements = data.size() / current_vertex_count; - data.resize(final_vertex_count * elements); - float *data_ptr = data.ptrw(); - for (int j = 0; j < new_vertex_count; j++) { - memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements); - } - r_arrays[i] = data; - } break; - case Variant::PACKED_INT32_ARRAY: { - PackedInt32Array data = r_arrays[i]; - int elements = data.size() / current_vertex_count; - data.resize(final_vertex_count * elements); - int32_t *data_ptr = data.ptrw(); - for (int j = 0; j < new_vertex_count; j++) { - memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements); - } - r_arrays[i] = data; - } break; - case Variant::PACKED_BYTE_ARRAY: { - PackedByteArray data = r_arrays[i]; - int elements = data.size() / current_vertex_count; - data.resize(final_vertex_count * elements); - uint8_t *data_ptr = data.ptrw(); - for (int j = 0; j < new_vertex_count; j++) { - memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements); - } - r_arrays[i] = data; - } break; - case Variant::PACKED_COLOR_ARRAY: { - PackedColorArray data = r_arrays[i]; - data.resize(final_vertex_count); - Color *data_ptr = data.ptrw(); - for (int j = 0; j < new_vertex_count; j++) { - data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; - } - r_arrays[i] = data; - } break; - default: { - ERR_FAIL_MSG("Unhandled array type."); - } break; - } - } -} - String ImporterMesh::validate_blend_shape_name(const String &p_name) { String name = p_name; const char *characters = ":"; @@ -266,10 +168,56 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma mesh.unref(); } -void ImporterMesh::optimize_indices_for_cache() { +template <typename T> +static Vector<T> _remap_array(Vector<T> p_array, const Vector<uint32_t> &p_remap, uint32_t p_vertex_count) { + ERR_FAIL_COND_V(p_array.size() % p_remap.size() != 0, p_array); + int num_elements = p_array.size() / p_remap.size(); + T *data = p_array.ptrw(); + SurfaceTool::remap_vertex_func(data, data, p_remap.size(), sizeof(T) * num_elements, p_remap.ptr()); + p_array.resize(p_vertex_count * num_elements); + return p_array; +} + +static void _remap_arrays(Array &r_arrays, const Vector<uint32_t> &p_remap, uint32_t p_vertex_count) { + for (int i = 0; i < r_arrays.size(); i++) { + if (i == RS::ARRAY_INDEX) { + continue; + } + + switch (r_arrays[i].get_type()) { + case Variant::NIL: + break; + case Variant::PACKED_VECTOR3_ARRAY: + r_arrays[i] = _remap_array<Vector3>(r_arrays[i], p_remap, p_vertex_count); + break; + case Variant::PACKED_VECTOR2_ARRAY: + r_arrays[i] = _remap_array<Vector2>(r_arrays[i], p_remap, p_vertex_count); + break; + case Variant::PACKED_FLOAT32_ARRAY: + r_arrays[i] = _remap_array<float>(r_arrays[i], p_remap, p_vertex_count); + break; + case Variant::PACKED_INT32_ARRAY: + r_arrays[i] = _remap_array<int32_t>(r_arrays[i], p_remap, p_vertex_count); + break; + case Variant::PACKED_BYTE_ARRAY: + r_arrays[i] = _remap_array<uint8_t>(r_arrays[i], p_remap, p_vertex_count); + break; + case Variant::PACKED_COLOR_ARRAY: + r_arrays[i] = _remap_array<Color>(r_arrays[i], p_remap, p_vertex_count); + break; + default: + ERR_FAIL_MSG("Unhandled array type."); + } + } +} + +void ImporterMesh::optimize_indices() { if (!SurfaceTool::optimize_vertex_cache_func) { return; } + if (!SurfaceTool::optimize_vertex_fetch_remap_func || !SurfaceTool::remap_vertex_func || !SurfaceTool::remap_index_func) { + return; + } for (int i = 0; i < surfaces.size(); i++) { if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) { @@ -286,10 +234,48 @@ void ImporterMesh::optimize_indices_for_cache() { continue; } + // Optimize indices for vertex cache to establish final triangle order. int *indices_ptr = indices.ptrw(); SurfaceTool::optimize_vertex_cache_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, vertex_count); + surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices; + + for (int j = 0; j < surfaces[i].lods.size(); ++j) { + Surface::LOD &lod = surfaces.write[i].lods.write[j]; + int *lod_indices_ptr = lod.indices.ptrw(); + SurfaceTool::optimize_vertex_cache_func((unsigned int *)lod_indices_ptr, (const unsigned int *)lod_indices_ptr, lod.indices.size(), vertex_count); + } + // Concatenate indices for all LODs in the order of coarse->fine; this establishes the effective order of vertices, + // and is important to optimize for vertex fetch (all GPUs) and shading (Mali GPUs) + PackedInt32Array merged_indices; + for (int j = surfaces[i].lods.size() - 1; j >= 0; --j) { + merged_indices.append_array(surfaces[i].lods[j].indices); + } + merged_indices.append_array(indices); + + // Generate remap array that establishes optimal vertex order according to the order of indices above. + Vector<uint32_t> remap; + remap.resize(vertex_count); + unsigned int new_vertex_count = SurfaceTool::optimize_vertex_fetch_remap_func(remap.ptrw(), (const unsigned int *)merged_indices.ptr(), merged_indices.size(), vertex_count); + + // We need to remap all vertex and index arrays in lockstep according to the remap. + SurfaceTool::remap_index_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, remap.ptr()); surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices; + + for (int j = 0; j < surfaces[i].lods.size(); ++j) { + Surface::LOD &lod = surfaces.write[i].lods.write[j]; + int *lod_indices_ptr = lod.indices.ptrw(); + SurfaceTool::remap_index_func((unsigned int *)lod_indices_ptr, (const unsigned int *)lod_indices_ptr, lod.indices.size(), remap.ptr()); + } + + _remap_arrays(surfaces.write[i].arrays, remap, new_vertex_count); + for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) { + _remap_arrays(surfaces.write[i].blend_shape_data.write[j].arrays, remap, new_vertex_count); + } + } + + if (shadow_mesh.is_valid()) { + shadow_mesh->optimize_indices(); } } @@ -306,16 +292,13 @@ void ImporterMesh::optimize_indices_for_cache() { } \ write_array[vert_idx] = transformed_vert; -void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_bone_transform_array, bool p_raycast_normals) { +void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transform_array) { if (!SurfaceTool::simplify_scale_func) { return; } if (!SurfaceTool::simplify_with_attrib_func) { return; } - if (!SurfaceTool::optimize_vertex_cache_func) { - return; - } LocalVector<Transform3D> bone_transform_vector; for (int i = 0; i < p_bone_transform_array.size(); i++) { @@ -379,8 +362,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } float normal_merge_threshold = Math::cos(Math::deg_to_rad(p_normal_merge_angle)); - float normal_pre_split_threshold = Math::cos(Math::deg_to_rad(MIN(180.0f, p_normal_split_angle * 2.0f))); - float normal_split_threshold = Math::cos(Math::deg_to_rad(p_normal_split_angle)); const Vector3 *normals_ptr = normals.ptr(); HashMap<Vector3, LocalVector<Pair<int, int>>> unique_vertices; @@ -469,22 +450,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli unsigned int index_target = 12; // Start with the smallest target, 4 triangles unsigned int last_index_count = 0; - // Only used for normal raycasting - int split_vertex_count = vertex_count; - LocalVector<Vector3> split_vertex_normals; - LocalVector<int> split_vertex_indices; - split_vertex_normals.reserve(index_count / 3); - split_vertex_indices.reserve(index_count / 3); - - RandomPCG pcg; - pcg.seed(123456789); // Keep seed constant across imports - - Ref<StaticRaycaster> raycaster = p_raycast_normals ? StaticRaycaster::create() : Ref<StaticRaycaster>(); - if (raycaster.is_valid()) { - raycaster->add_mesh(vertices, indices, 0); - raycaster->commit(); - } - const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target float mesh_error = 0.0f; @@ -503,6 +468,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli merged_normals_f32.ptr(), sizeof(float) * 3, // Attribute stride normal_weights, 3, + nullptr, // Vertex lock index_target, max_mesh_error, simplify_options, @@ -533,173 +499,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } } - if (raycaster.is_valid()) { - LocalVector<LocalVector<int>> vertex_corners; - vertex_corners.resize(vertex_count); - - int *ptrw = new_indices.ptrw(); - for (unsigned int j = 0; j < new_index_count; j++) { - vertex_corners[ptrw[j]].push_back(j); - } - - float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15)); - const float ray_bias = 0.05; - float ray_length = ray_bias + mesh_error * scale * 3.0f; - - Vector<StaticRaycaster::Ray> rays; - LocalVector<Vector2> ray_uvs; - - int32_t *new_indices_ptr = new_indices.ptrw(); - - int current_ray_count = 0; - for (unsigned int j = 0; j < new_index_count; j += 3) { - const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]]; - const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]]; - const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]]; - Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1); - float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care - if (!Math::is_finite(face_area) || face_area == 0) { - WARN_PRINT_ONCE("Ignoring face with non-finite normal in LOD generation."); - continue; - } - - Vector3 dir = face_normal / face_area; - int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64); - - rays.resize(current_ray_count + ray_count); - StaticRaycaster::Ray *rays_ptr = rays.ptrw(); - - ray_uvs.resize(current_ray_count + ray_count); - Vector2 *ray_uvs_ptr = ray_uvs.ptr(); - - for (int k = 0; k < ray_count; k++) { - float u = pcg.randf(); - float v = pcg.randf(); - - if (u + v >= 1.0f) { - u = 1.0f - u; - v = 1.0f - v; - } - - u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge - v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third; - float w = 1.0f - u - v; - - Vector3 org = v0 * w + v1 * u + v2 * v; - org -= dir * ray_bias; - rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length); - rays_ptr[current_ray_count + k].id = j / 3; - ray_uvs_ptr[current_ray_count + k] = Vector2(u, v); - } - - current_ray_count += ray_count; - } - - raycaster->intersect(rays); - - LocalVector<Vector3> ray_normals; - LocalVector<real_t> ray_normal_weights; - - ray_normals.resize(new_index_count); - ray_normal_weights.resize(new_index_count); - - for (unsigned int j = 0; j < new_index_count; j++) { - ray_normal_weights[j] = 0.0f; - } - - const StaticRaycaster::Ray *rp = rays.ptr(); - for (int j = 0; j < rays.size(); j++) { - if (rp[j].geomID != 0) { // Ray missed - continue; - } - - if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face. - continue; - } - - const float &u = rp[j].u; - const float &v = rp[j].v; - const float w = 1.0f - u - v; - - const unsigned int &hit_tri_id = rp[j].primID; - const unsigned int &orig_tri_id = rp[j].id; - - const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]]; - const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]]; - const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]]; - Vector3 normal = n0 * w + n1 * u + n2 * v; - - Vector2 orig_uv = ray_uvs[j]; - const real_t orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y }; - for (int k = 0; k < 3; k++) { - int idx = orig_tri_id * 3 + k; - real_t weight = orig_bary[k]; - ray_normals[idx] += normal * weight; - ray_normal_weights[idx] += weight; - } - } - - for (unsigned int j = 0; j < new_index_count; j++) { - if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess - ray_normals[j] = Vector3(); - } else { - ray_normals[j] /= ray_normal_weights[j]; - } - } - - LocalVector<LocalVector<int>> normal_group_indices; - LocalVector<Vector3> normal_group_averages; - normal_group_indices.reserve(24); - normal_group_averages.reserve(24); - - for (unsigned int j = 0; j < vertex_count; j++) { - const LocalVector<int> &corners = vertex_corners[j]; - const Vector3 &vertex_normal = normals_ptr[j]; - - for (const int &corner_idx : corners) { - const Vector3 &ray_normal = ray_normals[corner_idx]; - - if (ray_normal.length_squared() < CMP_EPSILON2) { - continue; - } - - bool found = false; - for (unsigned int l = 0; l < normal_group_indices.size(); l++) { - LocalVector<int> &group_indices = normal_group_indices[l]; - Vector3 n = normal_group_averages[l] / group_indices.size(); - if (n.dot(ray_normal) > normal_pre_split_threshold) { - found = true; - group_indices.push_back(corner_idx); - normal_group_averages[l] += ray_normal; - break; - } - } - - if (!found) { - normal_group_indices.push_back({ corner_idx }); - normal_group_averages.push_back(ray_normal); - } - } - - for (unsigned int k = 0; k < normal_group_indices.size(); k++) { - LocalVector<int> &group_indices = normal_group_indices[k]; - Vector3 n = normal_group_averages[k] / group_indices.size(); - - if (vertex_normal.dot(n) < normal_split_threshold) { - split_vertex_indices.push_back(j); - split_vertex_normals.push_back(n); - int new_idx = split_vertex_count++; - for (const int &index : group_indices) { - new_indices_ptr[index] = new_idx; - } - } - } - - normal_group_indices.clear(); - normal_group_averages.clear(); - } - } - Surface::LOD lod; lod.distance = MAX(mesh_error * scale, CMP_EPSILON2); lod.indices = new_indices; @@ -712,22 +511,13 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli } } - if (raycaster.is_valid()) { - surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals); - } - surfaces.write[i].lods.sort_custom<Surface::LODComparator>(); - - for (int j = 0; j < surfaces.write[i].lods.size(); j++) { - Surface::LOD &lod = surfaces.write[i].lods.write[j]; - unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw(); - SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count); - } } } void ImporterMesh::_generate_lods_bind(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array) { - generate_lods(p_normal_merge_angle, p_normal_split_angle, p_skin_pose_transform_array); + // p_normal_split_angle is unused, but kept for compatibility + generate_lods(p_normal_merge_angle, p_skin_pose_transform_array); } bool ImporterMesh::has_mesh() const { @@ -859,10 +649,6 @@ void ImporterMesh::create_shadow_mesh() { index_wptr[j] = vertex_remap[index]; } - if (SurfaceTool::optimize_vertex_cache_func && surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) { - SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size()); - } - new_surface[RS::ARRAY_INDEX] = new_indices; // Make sure the same LODs as the full version are used. @@ -881,10 +667,6 @@ void ImporterMesh::create_shadow_mesh() { index_wptr[k] = vertex_remap[index]; } - if (SurfaceTool::optimize_vertex_cache_func && surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) { - SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size()); - } - lods[surfaces[i].lods[j].distance] = new_indices; } } diff --git a/scene/resources/3d/importer_mesh.h b/scene/resources/3d/importer_mesh.h index c7e3a059d6..2bdf759da6 100644 --- a/scene/resources/3d/importer_mesh.h +++ b/scene/resources/3d/importer_mesh.h @@ -68,9 +68,6 @@ class ImporterMesh : public Resource { return l.distance < r.distance; } }; - - void split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals); - static void _split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals); }; Vector<Surface> surfaces; Vector<String> blend_shapes; @@ -116,9 +113,9 @@ public: void set_surface_material(int p_surface, const Ref<Material> &p_material); - void optimize_indices_for_cache(); + void optimize_indices(); - void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array, bool p_raycast_normals = false); + void generate_lods(float p_normal_merge_angle, Array p_skin_pose_transform_array); void create_shadow_mesh(); Ref<ImporterMesh> get_shadow_mesh() const; diff --git a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp index 59366592ce..74dca88423 100644 --- a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp +++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp @@ -71,7 +71,7 @@ void NavigationMeshSourceGeometryData3D::append_arrays(const Vector<float> &p_ve bool NavigationMeshSourceGeometryData3D::has_data() { RWLockRead read_lock(geometry_rwlock); return vertices.size() && indices.size(); -}; +} void NavigationMeshSourceGeometryData3D::clear() { RWLockWrite write_lock(geometry_rwlock); diff --git a/scene/resources/3d/primitive_meshes.h b/scene/resources/3d/primitive_meshes.h index 85f46a482a..e68ac7fb26 100644 --- a/scene/resources/3d/primitive_meshes.h +++ b/scene/resources/3d/primitive_meshes.h @@ -545,7 +545,7 @@ private: ContourPoint(const Vector2 &p_pt, bool p_sharp) { point = p_pt; sharp = p_sharp; - }; + } }; struct ContourInfo { diff --git a/scene/resources/3d/shape_3d.cpp b/scene/resources/3d/shape_3d.cpp index 5a79392ba5..259d82b7a0 100644 --- a/scene/resources/3d/shape_3d.cpp +++ b/scene/resources/3d/shape_3d.cpp @@ -73,7 +73,7 @@ Ref<ArrayMesh> Shape3D::get_debug_mesh() { Vector<Vector3> lines = get_debug_mesh_lines(); - debug_mesh_cache = Ref<ArrayMesh>(memnew(ArrayMesh)); + debug_mesh_cache.instantiate(); if (!lines.is_empty()) { //make mesh diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 57a4e35f7a..f0b182503a 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1048,7 +1048,7 @@ int Animation::find_track(const NodePath &p_path, const TrackType p_type) const } }; return -1; -}; +} Animation::TrackType Animation::get_cache_type(TrackType p_type) { if (p_type == Animation::TYPE_BEZIER) { diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp index f9787dde2e..539001bf25 100644 --- a/scene/resources/audio_stream_wav.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -624,7 +624,7 @@ Error AudioStreamWAV::save_to_wav(const String &p_path) { } String file_path = p_path; - if (!(file_path.substr(file_path.length() - 4, 4) == ".wav")) { + if (file_path.substr(file_path.length() - 4, 4).to_lower() != ".wav") { file_path += ".wav"; } diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index 3a021720c6..3a0c207a5d 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -487,7 +487,7 @@ void CameraAttributesPhysical::_bind_methods() { ADD_GROUP("Auto Exposure", "auto_exposure_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_min_exposure_value", "get_auto_exposure_min_exposure_value"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_max_exposure_value", "get_auto_exposure_max_exposure_value"); -}; +} CameraAttributesPhysical::CameraAttributesPhysical() { _update_exposure(); diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index f8c70c3002..b5e23e9832 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -130,10 +130,7 @@ int Environment::get_canvas_max_layer() const { void Environment::set_camera_feed_id(int p_id) { bg_camera_feed_id = p_id; -// FIXME: Disabled during Vulkan refactoring, should be ported. -#if 0 - RS::get_singleton()->environment_set_camera_feed_id(environment, camera_feed_id); -#endif + RS::get_singleton()->environment_set_camera_feed_id(environment, bg_camera_feed_id); } int Environment::get_camera_feed_id() const { diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 5e4136f449..a4677d917d 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -647,13 +647,13 @@ void FontFile::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) wa[ofs_dst + 1] = r[ofs_src + 3]; } } - Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r)); + Ref<Image> img_r = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_r)); set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r); - Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g)); + Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g)); set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g); - Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b)); + Ref<Image> img_b = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_b)); set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b); - Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a)); + Ref<Image> img_a = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_a)); set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a); } @@ -738,22 +738,22 @@ void FontFile::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) } } } - Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r)); + Ref<Image> img_r = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_r)); set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r); - Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g)); + Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g)); set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g); - Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b)); + Ref<Image> img_b = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_b)); set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b); - Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a)); + Ref<Image> img_a = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_a)); set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a); - Ref<Image> img_ro = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ro)); + Ref<Image> img_ro = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_ro)); set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 0, img_ro); - Ref<Image> img_go = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_go)); + Ref<Image> img_go = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_go)); set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 1, img_go); - Ref<Image> img_bo = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_bo)); + Ref<Image> img_bo = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_bo)); set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 2, img_bo); - Ref<Image> img_ao = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ao)); + Ref<Image> img_ao = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_ao)); set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao); } @@ -806,10 +806,10 @@ void FontFile::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { } } } - Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_g)); + Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_RGBA8, imgdata_g)); set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g); - Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_o)); + Ref<Image> img_o = memnew(Image(w, h, false, Image::FORMAT_RGBA8, imgdata_o)); set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o); } @@ -838,7 +838,7 @@ void FontFile::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, in wg[ofs_dst + 1] = r[ofs_src + p_ch]; } } - Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g)); + Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g)); set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g); } @@ -878,10 +878,10 @@ void FontFile::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, in } } } - Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g)); + Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g)); set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g); - Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_o)); + Ref<Image> img_o = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_o)); set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o); } diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 4d1d733f8b..b0353b4f2c 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -45,23 +45,23 @@ void MeshConvexDecompositionSettings::set_max_concavity(real_t p_max_concavity) real_t MeshConvexDecompositionSettings::get_max_concavity() const { return max_concavity; -}; +} void MeshConvexDecompositionSettings::set_symmetry_planes_clipping_bias(real_t p_symmetry_planes_clipping_bias) { symmetry_planes_clipping_bias = CLAMP(p_symmetry_planes_clipping_bias, 0.0, 1.0); -}; +} real_t MeshConvexDecompositionSettings::get_symmetry_planes_clipping_bias() const { return symmetry_planes_clipping_bias; -}; +} void MeshConvexDecompositionSettings::set_revolution_axes_clipping_bias(real_t p_revolution_axes_clipping_bias) { revolution_axes_clipping_bias = CLAMP(p_revolution_axes_clipping_bias, 0.0, 1.0); -}; +} real_t MeshConvexDecompositionSettings::get_revolution_axes_clipping_bias() const { return revolution_axes_clipping_bias; -}; +} void MeshConvexDecompositionSettings::set_min_volume_per_convex_hull(real_t p_min_volume_per_convex_hull) { min_volume_per_convex_hull = CLAMP(p_min_volume_per_convex_hull, 0.0001, 0.01); @@ -385,7 +385,7 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { } } - triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh.instantiate(); triangle_mesh->create(faces); return triangle_mesh; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 67ed65df0d..034d4d6996 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -392,8 +392,8 @@ Ref<ArrayMesh> NavigationMesh::get_debug_mesh() { return debug_mesh; } - if (!debug_mesh.is_valid()) { - debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (debug_mesh.is_null()) { + debug_mesh.instantiate(); } else { debug_mesh->clear_surfaces(); } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index d6fe4385c4..809a77a487 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -786,7 +786,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has Dictionary missing_resource_properties = p_node->get_meta(META_MISSING_RESOURCES, Dictionary()); for (const PropertyInfo &E : plist) { - if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + if (!(E.usage & PROPERTY_USAGE_STORAGE) && !missing_resource_properties.has(E.name)) { continue; } @@ -2195,7 +2195,7 @@ void PackedScene::replace_state(Ref<SceneState> p_by) { } void PackedScene::recreate_state() { - state = Ref<SceneState>(memnew(SceneState)); + state.instantiate(); state->set_path(get_path()); #ifdef TOOLS_ENABLED state->set_last_modified_time(get_last_modified_time()); @@ -2286,5 +2286,5 @@ void PackedScene::_bind_methods() { } PackedScene::PackedScene() { - state = Ref<SceneState>(memnew(SceneState)); + state.instantiate(); } diff --git a/scene/resources/portable_compressed_texture.cpp b/scene/resources/portable_compressed_texture.cpp index 06b5ec6d5a..55bbed7c47 100644 --- a/scene/resources/portable_compressed_texture.cpp +++ b/scene/resources/portable_compressed_texture.cpp @@ -89,7 +89,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { data_size -= mipsize; } - image = Ref<Image>(memnew(Image(size.width, size.height, mipmaps, format, image_data))); + image.instantiate(size.width, size.height, mipmaps, format, image_data); } break; case COMPRESSION_MODE_BASIS_UNIVERSAL: { @@ -100,7 +100,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { case COMPRESSION_MODE_S3TC: case COMPRESSION_MODE_ETC2: case COMPRESSION_MODE_BPTC: { - image = Ref<Image>(memnew(Image(size.width, size.height, mipmaps, format, p_data.slice(20)))); + image.instantiate(size.width, size.height, mipmaps, format, p_data.slice(20)); } break; } ERR_FAIL_COND(image.is_null()); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 4a318a10f0..6e03fe25c9 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -600,7 +600,7 @@ Error ResourceLoaderText::load() { if (do_assign) { bool set_valid = true; - if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + if (value.get_type() == Variant::OBJECT && missing_resource == nullptr && ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { // If the property being set is a missing resource (and the parent is not), // then setting it will most likely not work. // Instead, save it as metadata. @@ -723,24 +723,25 @@ Error ResourceLoaderText::load() { if (error) { if (error != ERR_FILE_EOF) { _printerr(); - } else { - error = OK; - if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - if (!ResourceCache::has(res_path)) { - resource->set_path(res_path); - } - resource->set_as_translation_remapped(translation_remapped); - } else { - resource->set_path_cache(res_path); + return error; + } + // EOF, Done parsing. + error = OK; + if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { + if (!ResourceCache::has(res_path)) { + resource->set_path(res_path); } + resource->set_as_translation_remapped(translation_remapped); + } else { + resource->set_path_cache(res_path); } - return error; + break; } if (!assign.is_empty()) { bool set_valid = true; - if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + if (value.get_type() == Variant::OBJECT && missing_resource == nullptr && ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { // If the property being set is a missing resource (and the parent is not), // then setting it will most likely not work. // Instead, save it as metadata. @@ -1525,6 +1526,10 @@ ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) return loader.get_uid(f); } +bool ResourceFormatLoaderText::has_custom_uid_support() const { + return true; +} + void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); if (f.is_null()) { @@ -1900,7 +1905,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso #endif } - Dictionary missing_resource_properties = p_resource->get_meta(META_MISSING_RESOURCES, Dictionary()); + Dictionary missing_resource_properties = res->get_meta(META_MISSING_RESOURCES, Dictionary()); List<PropertyInfo> property_list; res->get_property_list(&property_list); @@ -1912,7 +1917,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso continue; } - if (PE->get().usage & PROPERTY_USAGE_STORAGE) { + if (PE->get().usage & PROPERTY_USAGE_STORAGE || missing_resource_properties.has(PE->get().name)) { String name = PE->get().name; Variant value; if (PE->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 8397bc985f..4c0bf3d917 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -155,6 +155,7 @@ public: virtual String get_resource_type(const String &p_path) const override; virtual String get_resource_script_class(const String &p_path) const override; virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; + virtual bool has_custom_uid_support() const override; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override; virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) override; diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 6921885ee0..c230cf1b70 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -33,10 +33,10 @@ #define EQ_VERTEX_DIST 0.00001 SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr; +SurfaceTool::OptimizeVertexFetchRemapFunc SurfaceTool::optimize_vertex_fetch_remap_func = nullptr; SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr; SurfaceTool::SimplifyWithAttribFunc SurfaceTool::simplify_with_attrib_func = nullptr; SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr; -SurfaceTool::SimplifySloppyFunc SurfaceTool::simplify_sloppy_func = nullptr; SurfaceTool::GenerateRemapFunc SurfaceTool::generate_remap_func = nullptr; SurfaceTool::RemapVertexFunc SurfaceTool::remap_vertex_func = nullptr; SurfaceTool::RemapIndexFunc SurfaceTool::remap_index_func = nullptr; diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index a072df5bee..68dc9e7198 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -80,18 +80,24 @@ public: enum { /* Do not move vertices that are located on the topological border (vertices on triangle edges that don't have a paired triangle). Useful for simplifying portions of the larger mesh. */ SIMPLIFY_LOCK_BORDER = 1 << 0, // From meshopt_SimplifyLockBorder + /* Improve simplification performance assuming input indices are a sparse subset of the mesh. Note that error becomes relative to subset extents. */ + SIMPLIFY_SPARSE = 1 << 1, // From meshopt_SimplifySparse + /* Treat error limit and resulting error as absolute instead of relative to mesh extents. */ + SIMPLIFY_ERROR_ABSOLUTE = 1 << 2, // From meshopt_SimplifyErrorAbsolute + /* Remove disconnected parts of the mesh during simplification incrementally, regardless of the topological restrictions inside components. */ + SIMPLIFY_PRUNE = 1 << 3, // From meshopt_SimplifyPrune }; typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count); static OptimizeVertexCacheFunc optimize_vertex_cache_func; + typedef size_t (*OptimizeVertexFetchRemapFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count); + static OptimizeVertexFetchRemapFunc optimize_vertex_fetch_remap_func; typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float *r_error); static SimplifyFunc simplify_func; - typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, const float *attributes, size_t attribute_stride, const float *attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float *result_error); + typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, const float *attributes, size_t attribute_stride, const float *attribute_weights, size_t attribute_count, const unsigned char *vertex_lock, size_t target_index_count, float target_error, unsigned int options, float *result_error); static SimplifyWithAttribFunc simplify_with_attrib_func; typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride); static SimplifyScaleFunc simplify_scale_func; - typedef size_t (*SimplifySloppyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *out_result_error); - static SimplifySloppyFunc simplify_sloppy_func; typedef size_t (*GenerateRemapFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const void *vertices, size_t vertex_count, size_t vertex_size); static GenerateRemapFunc generate_remap_func; typedef void (*RemapVertexFunc)(void *destination, const void *vertices, size_t vertex_count, size_t vertex_size, const unsigned int *remap); @@ -113,7 +119,7 @@ private: SmoothGroupVertex(const Vertex &p_vertex) { vertex = p_vertex.vertex; smooth_group = p_vertex.smooth_group; - }; + } }; struct SmoothGroupVertexHasher { @@ -216,7 +222,9 @@ public: void clear(); - LocalVector<Vertex> &get_vertex_array() { return vertex_array; } + LocalVector<Vertex> &get_vertex_array() { + return vertex_array; + } void create_from_triangle_arrays(const Array &p_arrays); void create_from_arrays(const Array &p_arrays, Mesh::PrimitiveType p_primitive_type = Mesh::PRIMITIVE_TRIANGLES); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 29a8541cb0..65c6e40241 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -112,6 +112,11 @@ void TextParagraph::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible"), "set_max_lines_visible", "get_max_lines_visible"); + ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &TextParagraph::set_line_spacing); + ClassDB::bind_method(D_METHOD("get_line_spacing"), &TextParagraph::get_line_spacing); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing"), "set_line_spacing", "get_line_spacing"); + ClassDB::bind_method(D_METHOD("get_line_objects", "line"), &TextParagraph::get_line_objects); ClassDB::bind_method(D_METHOD("get_line_object_rect", "line", "key"), &TextParagraph::get_line_object_rect); ClassDB::bind_method(D_METHOD("get_line_size", "line"), &TextParagraph::get_line_size); @@ -180,6 +185,7 @@ void TextParagraph::_shape_lines() { for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x; + h += line_spacing; if (!tab_stops.is_empty()) { TS->shaped_text_tab_align(line, tab_stops); } @@ -574,12 +580,18 @@ Size2 TextParagraph::get_size() const { } size.x = MAX(size.x, lsize.x); size.y += lsize.y; + if (i != visible_lines - 1) { + size.y += line_spacing; + } } else { if (h_offset > 0 && i <= dropcap_lines) { lsize.y += h_offset; } size.x += lsize.x; size.y = MAX(size.y, lsize.y); + if (i != visible_lines - 1) { + size.x += line_spacing; + } } } if (h_offset > 0) { @@ -612,6 +624,19 @@ int TextParagraph::get_max_lines_visible() const { return max_lines_visible; } +void TextParagraph::set_line_spacing(float p_spacing) { + _THREAD_SAFE_METHOD_ + + if (line_spacing != p_spacing) { + line_spacing = p_spacing; + lines_dirty = true; + } +} + +float TextParagraph::get_line_spacing() const { + return line_spacing; +} + Array TextParagraph::get_line_objects(int p_line) const { _THREAD_SAFE_METHOD_ @@ -697,10 +722,10 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const { if (i != p_line) { if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = 0.f; - ofs.y += TS->shaped_text_get_descent(lines_rid[i]); + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; } else { ofs.y = 0.f; - ofs.x += TS->shaped_text_get_descent(lines_rid[i]); + ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; } } } @@ -872,10 +897,10 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo TS->shaped_text_draw(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_descent(lines_rid[i]); + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_descent(lines_rid[i]); + ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; } } } @@ -974,10 +999,10 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli TS->shaped_text_draw_outline(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_descent(lines_rid[i]); + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_descent(lines_rid[i]); + ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing; } } } @@ -1001,17 +1026,21 @@ int TextParagraph::hit_test(const Point2 &p_coords) const { if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(line_rid).y)) { return TS->shaped_text_hit_test_position(line_rid, p_coords.x); } - ofs.y += TS->shaped_text_get_size(line_rid).y; + ofs.y += TS->shaped_text_get_size(line_rid).y + line_spacing; } else { if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(line_rid).x)) { return TS->shaped_text_hit_test_position(line_rid, p_coords.y); } - ofs.y += TS->shaped_text_get_size(line_rid).x; + ofs.y += TS->shaped_text_get_size(line_rid).x + line_spacing; } } return TS->shaped_text_get_range(rid).y; } +bool TextParagraph::is_dirty() { + return lines_dirty; +} + void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const { _THREAD_SAFE_METHOD_ diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 7512955fb3..966ce556d5 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -51,6 +51,7 @@ private: bool lines_dirty = true; + float line_spacing = 0.0; float width = -1.0; int max_lines_visible = -1; @@ -122,6 +123,9 @@ public: void set_max_lines_visible(int p_lines); int get_max_lines_visible() const; + void set_line_spacing(float p_spacing); + float get_line_spacing() const; + Size2 get_non_wrapped_size() const; Size2 get_size() const; @@ -152,7 +156,9 @@ public: int hit_test(const Point2 &p_coords) const; - Mutex &get_mutex() const { return _thread_safe_; }; + bool is_dirty(); + + Mutex &get_mutex() const { return _thread_safe_; } TextParagraph(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextParagraph(); diff --git a/scene/resources/texture_rd.cpp b/scene/resources/texture_rd.cpp index 531dbcbe7e..8e7eeb0ff4 100644 --- a/scene/resources/texture_rd.cpp +++ b/scene/resources/texture_rd.cpp @@ -197,7 +197,7 @@ void TextureLayeredRD::_set_texture_rd_rid(RID p_texture_rd_rid) { RS::TextureLayeredType rs_layer_type; RD::TextureFormat tf = RD::get_singleton()->texture_get_format(p_texture_rd_rid); - ERR_FAIL_COND(tf.texture_type != RD::TEXTURE_TYPE_2D_ARRAY); + ERR_FAIL_COND(tf.texture_type != RD::TEXTURE_TYPE_2D_ARRAY && tf.texture_type != RD::TEXTURE_TYPE_CUBE && tf.texture_type != RD::TEXTURE_TYPE_CUBE_ARRAY); ERR_FAIL_COND(tf.depth > 1); switch (layer_type) { case LAYERED_TYPE_2D_ARRAY: { diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index d0e55f4065..847867fa4d 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1368,7 +1368,7 @@ void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_por ERR_FAIL_INDEX(p_type, TYPE_MAX); Graph *g = &graph[p_type]; - for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { g->connections.erase(E); g->nodes[p_from_node].next_connected_nodes.erase(p_to_node); @@ -1560,7 +1560,7 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port shader_code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_4D: { - shader_code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ".xyz;\n"; + shader_code += " COLOR = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; } break; default: { shader_code += " COLOR.rgb = vec3(0.0);\n"; @@ -1921,13 +1921,13 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { } for (const KeyValue<String, Varying> &E : varyings) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("varyings"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", "varyings", E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { for (const KeyValue<String, Variant> &E : preview_params) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("preview_params"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", "preview_params", E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } } #endif // TOOLS_ENABLED |