summaryrefslogtreecommitdiffstats
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite_2d.cpp6
-rw-r--r--scene/2d/animated_sprite_2d.h9
-rw-r--r--scene/2d/back_buffer_copy.cpp4
-rw-r--r--scene/2d/back_buffer_copy.h4
-rw-r--r--scene/2d/camera_2d.cpp5
-rw-r--r--scene/2d/gpu_particles_2d.cpp2
-rw-r--r--scene/2d/light_2d.cpp9
-rw-r--r--scene/2d/light_2d.h5
-rw-r--r--scene/2d/light_occluder_2d.cpp15
-rw-r--r--scene/2d/light_occluder_2d.h9
-rw-r--r--scene/2d/line_2d.cpp2
-rw-r--r--scene/2d/line_2d.h2
-rw-r--r--scene/2d/marker_2d.cpp4
-rw-r--r--scene/2d/marker_2d.h4
-rw-r--r--scene/2d/mesh_instance_2d.cpp4
-rw-r--r--scene/2d/mesh_instance_2d.h4
-rw-r--r--scene/2d/multimesh_instance_2d.cpp4
-rw-r--r--scene/2d/multimesh_instance_2d.h4
-rw-r--r--scene/2d/navigation_agent_2d.cpp8
-rw-r--r--scene/2d/navigation_link_2d.cpp4
-rw-r--r--scene/2d/navigation_link_2d.h4
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp40
-rw-r--r--scene/2d/navigation_obstacle_2d.h6
-rw-r--r--scene/2d/navigation_region_2d.cpp4
-rw-r--r--scene/2d/navigation_region_2d.h4
-rw-r--r--scene/2d/path_2d.cpp2
-rw-r--r--scene/2d/path_2d.h2
-rw-r--r--scene/2d/physics/collision_polygon_2d.cpp2
-rw-r--r--scene/2d/physics/collision_polygon_2d.h2
-rw-r--r--scene/2d/physics/collision_shape_2d.h4
-rw-r--r--scene/2d/physics/rigid_body_2d.cpp2
-rw-r--r--scene/2d/polygon_2d.cpp4
-rw-r--r--scene/2d/polygon_2d.h5
-rw-r--r--scene/2d/sprite_2d.cpp4
-rw-r--r--scene/2d/sprite_2d.h5
-rw-r--r--scene/2d/tile_map.cpp19
-rw-r--r--scene/2d/tile_map.h8
-rw-r--r--scene/2d/tile_map_layer.cpp27
-rw-r--r--scene/2d/tile_map_layer.h2
-rw-r--r--scene/2d/touch_screen_button.cpp7
-rw-r--r--scene/2d/touch_screen_button.h4
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.cpp4
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.h4
-rw-r--r--scene/3d/camera_3d.cpp9
-rw-r--r--scene/3d/gpu_particles_3d.cpp2
-rw-r--r--scene/3d/label_3d.cpp2
-rw-r--r--scene/3d/light_3d.cpp15
-rw-r--r--scene/3d/light_3d.h4
-rw-r--r--scene/3d/lightmap_gi.cpp164
-rw-r--r--scene/3d/lightmap_gi.h10
-rw-r--r--scene/3d/look_at_modifier_3d.cpp781
-rw-r--r--scene/3d/look_at_modifier_3d.h199
-rw-r--r--scene/3d/navigation_agent_3d.cpp12
-rw-r--r--scene/3d/navigation_link_3d.cpp4
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp350
-rw-r--r--scene/3d/navigation_obstacle_3d.h17
-rw-r--r--scene/3d/navigation_region_3d.cpp8
-rw-r--r--scene/3d/node_3d.h4
-rw-r--r--scene/3d/path_3d.cpp4
-rw-r--r--scene/3d/physics/ray_cast_3d.cpp2
-rw-r--r--scene/3d/physics/rigid_body_3d.cpp2
-rw-r--r--scene/3d/physics/shape_cast_3d.cpp2
-rw-r--r--scene/3d/skeleton_3d.cpp22
-rw-r--r--scene/3d/skeleton_3d.h6
-rw-r--r--scene/3d/skeleton_ik_3d.h2
-rw-r--r--scene/3d/skeleton_modifier_3d.cpp16
-rw-r--r--scene/3d/skeleton_modifier_3d.h1
-rw-r--r--scene/3d/sprite_3d.cpp2
-rw-r--r--scene/3d/xr_nodes.cpp14
-rw-r--r--scene/animation/animation_mixer.cpp66
-rw-r--r--scene/animation/animation_mixer.h21
-rw-r--r--scene/animation/animation_node_state_machine.cpp2
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/animation/animation_player.h4
-rw-r--r--scene/animation/animation_tree.cpp32
-rw-r--r--scene/animation/animation_tree.h49
-rw-r--r--scene/audio/audio_stream_player_internal.cpp8
-rw-r--r--scene/debugger/scene_debugger.cpp1227
-rw-r--r--scene/debugger/scene_debugger.h167
-rw-r--r--scene/gui/base_button.cpp1
-rw-r--r--scene/gui/button.cpp27
-rw-r--r--scene/gui/button.h5
-rw-r--r--scene/gui/code_edit.cpp2
-rw-r--r--scene/gui/code_edit.h6
-rw-r--r--scene/gui/color_mode.h4
-rw-r--r--scene/gui/color_picker.cpp36
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/control.cpp50
-rw-r--r--scene/gui/control.h18
-rw-r--r--scene/gui/file_dialog.cpp46
-rw-r--r--scene/gui/file_dialog.h4
-rw-r--r--scene/gui/graph_edit.cpp18
-rw-r--r--scene/gui/label.cpp344
-rw-r--r--scene/gui/label.h5
-rw-r--r--scene/gui/line_edit.cpp2
-rw-r--r--scene/gui/option_button.cpp6
-rw-r--r--scene/gui/range.h2
-rw-r--r--scene/gui/rich_text_effect.h20
-rw-r--r--scene/gui/scroll_container.cpp6
-rw-r--r--scene/gui/spin_box.cpp38
-rw-r--r--scene/gui/spin_box.h2
-rw-r--r--scene/gui/text_edit.cpp6
-rw-r--r--scene/gui/text_edit.h8
-rw-r--r--scene/gui/texture_button.cpp4
-rw-r--r--scene/gui/tree.cpp83
-rw-r--r--scene/gui/video_stream_player.cpp8
-rw-r--r--scene/main/canvas_item.cpp10
-rw-r--r--scene/main/canvas_item.h35
-rw-r--r--scene/main/instance_placeholder.cpp2
-rw-r--r--scene/main/missing_node.cpp14
-rw-r--r--scene/main/node.cpp13
-rw-r--r--scene/main/node.h3
-rw-r--r--scene/main/scene_tree.cpp29
-rw-r--r--scene/main/scene_tree.h3
-rw-r--r--scene/main/viewport.cpp95
-rw-r--r--scene/main/viewport.h8
-rw-r--r--scene/main/window.cpp31
-rw-r--r--scene/main/window.h2
-rw-r--r--scene/property_utils.cpp10
-rw-r--r--scene/register_scene_types.cpp5
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp2
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.h2
-rw-r--r--scene/resources/2d/navigation_polygon.cpp4
-rw-r--r--scene/resources/2d/navigation_polygon.h5
-rw-r--r--scene/resources/2d/skeleton/skeleton_modification_stack_2d.h2
-rw-r--r--scene/resources/2d/tile_set.cpp68
-rw-r--r--scene/resources/2d/tile_set.h11
-rw-r--r--scene/resources/3d/convex_polygon_shape_3d.cpp2
-rw-r--r--scene/resources/3d/importer_mesh.cpp396
-rw-r--r--scene/resources/3d/importer_mesh.h7
-rw-r--r--scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp2
-rw-r--r--scene/resources/3d/primitive_meshes.h2
-rw-r--r--scene/resources/3d/shape_3d.cpp2
-rw-r--r--scene/resources/animation.cpp2
-rw-r--r--scene/resources/audio_stream_wav.cpp2
-rw-r--r--scene/resources/camera_attributes.cpp2
-rw-r--r--scene/resources/curve.cpp21
-rw-r--r--scene/resources/environment.cpp5
-rw-r--r--scene/resources/external_texture.cpp23
-rw-r--r--scene/resources/external_texture.h5
-rw-r--r--scene/resources/font.cpp34
-rw-r--r--scene/resources/mesh.cpp12
-rw-r--r--scene/resources/navigation_mesh.cpp4
-rw-r--r--scene/resources/packed_scene.cpp6
-rw-r--r--scene/resources/portable_compressed_texture.cpp4
-rw-r--r--scene/resources/resource_format_text.cpp33
-rw-r--r--scene/resources/resource_format_text.h1
-rw-r--r--scene/resources/style_box_flat.cpp24
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/surface_tool.h18
-rw-r--r--scene/resources/text_paragraph.cpp45
-rw-r--r--scene/resources/text_paragraph.h8
-rw-r--r--scene/resources/texture_rd.cpp2
-rw-r--r--scene/resources/visual_shader.cpp8
-rw-r--r--scene/scene_string_names.cpp2
-rw-r--r--scene/scene_string_names.h2
156 files changed, 3888 insertions, 1401 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..0b54d1e604 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()) {
@@ -417,7 +418,7 @@ Vector2 PointLight2D::get_texture_offset() const {
}
PackedStringArray PointLight2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node2D::get_configuration_warnings();
+ PackedStringArray warnings = Light2D::get_configuration_warnings();
if (!texture.is_valid()) {
warnings.push_back(RTR("A texture with the shape of the light must be supplied to the \"Texture\" property."));
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..c6d1447ad8 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;
@@ -159,8 +167,7 @@ void NavigationObstacle2D::_notification(int p_what) {
if (is_debug_enabled) {
RS::get_singleton()->canvas_item_clear(debug_canvas_item);
- Transform2D debug_transform = Transform2D(0.0, get_global_position());
- RS::get_singleton()->canvas_item_set_transform(debug_canvas_item, debug_transform);
+ RS::get_singleton()->canvas_item_set_transform(debug_canvas_item, Transform2D());
_update_fake_agent_radius_debug();
_update_static_obstacle_debug();
}
@@ -303,6 +310,25 @@ bool NavigationObstacle2D::get_carve_navigation_mesh() const {
return carve_navigation_mesh;
}
+PackedStringArray NavigationObstacle2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
+
+ const Vector2 global_scale = get_global_scale();
+ if (global_scale.x < 0.001 || global_scale.y < 0.001) {
+ warnings.push_back(RTR("NavigationObstacle2D does not support negative or zero scaling."));
+ }
+
+ if (radius > 0.0 && !get_global_transform().is_conformal()) {
+ warnings.push_back(RTR("The agent radius can only be scaled uniformly. The largest value along the two axes of the global scale will be used to scale the radius. This value may change in unexpected ways when the node is rotated."));
+ }
+
+ if (radius > 0.0 && get_global_skew() != 0.0) {
+ warnings.push_back(RTR("Skew has no effect on the agent radius."));
+ }
+
+ return warnings;
+}
+
void NavigationObstacle2D::_update_map(RID p_map) {
map_current = p_map;
NavigationServer2D::get_singleton()->obstacle_set_map(obstacle, p_map);
@@ -319,8 +345,11 @@ void NavigationObstacle2D::_update_position(const Vector2 p_position) {
void NavigationObstacle2D::_update_fake_agent_radius_debug() {
if (radius > 0.0 && NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
Color debug_radius_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
-
- RS::get_singleton()->canvas_item_add_circle(debug_canvas_item, Vector2(), radius, debug_radius_color);
+ // Prevent non-positive scaling.
+ const Vector2 safe_scale = get_global_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()];
+ RS::get_singleton()->canvas_item_add_circle(debug_canvas_item, get_global_position(), scaling_max_value * radius, debug_radius_color);
}
}
#endif // DEBUG_ENABLED
@@ -362,7 +391,8 @@ void NavigationObstacle2D::_update_static_obstacle_debug() {
debug_obstacle_line_colors.resize(debug_obstacle_line_vertices.size());
debug_obstacle_line_colors.fill(debug_static_obstacle_edge_color);
- RS::get_singleton()->canvas_item_add_polyline(debug_canvas_item, debug_obstacle_line_vertices, debug_obstacle_line_colors, 4.0);
+ // Transforming the vertices directly instead of the canvas item in order to not affect the circle shape by non-uniform scales.
+ RS::get_singleton()->canvas_item_add_polyline(debug_canvas_item, get_global_transform().xform(debug_obstacle_line_vertices), debug_obstacle_line_colors, 4.0);
}
}
#endif // DEBUG_ENABLED
diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h
index 30328f7086..34e585142d 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
@@ -106,6 +106,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 Vector2 p_position);
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/physics/rigid_body_2d.cpp b/scene/2d/physics/rigid_body_2d.cpp
index 402e5c8b95..4b1cde6b7a 100644
--- a/scene/2d/physics/rigid_body_2d.cpp
+++ b/scene/2d/physics/rigid_body_2d.cpp
@@ -641,7 +641,7 @@ void RigidBody2D::_notification(int p_what) {
PackedStringArray RigidBody2D::get_configuration_warnings() const {
Transform2D t = get_transform();
- PackedStringArray warnings = CollisionObject2D::get_configuration_warnings();
+ PackedStringArray warnings = PhysicsBody2D::get_configuration_warnings();
if (ABS(t.columns[0].length() - 1.0) > 0.05 || ABS(t.columns[1].length() - 1.0) > 0.05) {
warnings.push_back(RTR("Size changes to RigidBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
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/light_3d.cpp b/scene/3d/light_3d.cpp
index 7b70986adc..2d18e62b10 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -146,6 +146,15 @@ bool Light3D::get_shadow_reverse_cull_face() const {
return reverse_cull;
}
+void Light3D::set_shadow_caster_mask(uint32_t p_caster_mask) {
+ shadow_caster_mask = p_caster_mask;
+ RS::get_singleton()->light_set_shadow_caster_mask(light, shadow_caster_mask);
+}
+
+uint32_t Light3D::get_shadow_caster_mask() const {
+ return shadow_caster_mask;
+}
+
AABB Light3D::get_aabb() const {
if (type == RenderingServer::LIGHT_DIRECTIONAL) {
return AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
@@ -300,7 +309,7 @@ bool Light3D::is_editor_only() const {
}
void Light3D::_validate_property(PropertyInfo &p_property) const {
- if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow")) {
+ if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow" || p_property.name == "shadow_caster_mask")) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
@@ -354,6 +363,9 @@ void Light3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_reverse_cull_face", "enable"), &Light3D::set_shadow_reverse_cull_face);
ClassDB::bind_method(D_METHOD("get_shadow_reverse_cull_face"), &Light3D::get_shadow_reverse_cull_face);
+ ClassDB::bind_method(D_METHOD("set_shadow_caster_mask", "caster_mask"), &Light3D::set_shadow_caster_mask);
+ ClassDB::bind_method(D_METHOD("get_shadow_caster_mask"), &Light3D::get_shadow_caster_mask);
+
ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode);
ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode);
@@ -388,6 +400,7 @@ void Light3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_OPACITY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BLUR);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_caster_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_shadow_caster_mask", "get_shadow_caster_mask");
ADD_GROUP("Distance Fade", "distance_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled");
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index d6eca8d8b6..5f549469c6 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -75,6 +75,7 @@ private:
bool negative = false;
bool reverse_cull = false;
uint32_t cull_mask = 0;
+ uint32_t shadow_caster_mask = 0xFFFFFFFF;
bool distance_fade_enabled = false;
real_t distance_fade_begin = 40.0;
real_t distance_fade_shadow = 50.0;
@@ -136,6 +137,9 @@ public:
void set_shadow_reverse_cull_face(bool p_enable);
bool get_shadow_reverse_cull_face() const;
+ void set_shadow_caster_mask(uint32_t p_caster_mask);
+ uint32_t get_shadow_caster_mask() const;
+
void set_bake_mode(BakeMode p_mode);
BakeMode get_bake_mode() const;
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/look_at_modifier_3d.cpp b/scene/3d/look_at_modifier_3d.cpp
new file mode 100644
index 0000000000..ad33cd420a
--- /dev/null
+++ b/scene/3d/look_at_modifier_3d.cpp
@@ -0,0 +1,781 @@
+/**************************************************************************/
+/* look_at_modifier_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "look_at_modifier_3d.h"
+
+void LookAtModifier3D::_validate_property(PropertyInfo &p_property) const {
+ SkeletonModifier3D::_validate_property(p_property);
+
+ if (p_property.name == "bone" || p_property.name == "origin_bone") {
+ Skeleton3D *skeleton = get_skeleton();
+ if (skeleton) {
+ p_property.hint = PROPERTY_HINT_ENUM;
+ p_property.hint_string = skeleton->get_concatenated_bone_names();
+ } else {
+ p_property.hint = PROPERTY_HINT_NONE;
+ p_property.hint_string = "";
+ }
+ }
+
+ if (origin_from == ORIGIN_FROM_SPECIFIC_BONE) {
+ if (p_property.name == "origin_external_node") {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ }
+ } else if (origin_from == ORIGIN_FROM_EXTERNAL_NODE) {
+ if (p_property.name == "origin_bone") {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ }
+ } else {
+ if (p_property.name == "origin_external_node" || p_property.name == "origin_bone") {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ }
+ }
+
+ if ((!use_angle_limitation &&
+ (p_property.name == "symmetry_limitation" || p_property.name.ends_with("limit_angle") || p_property.name.ends_with("damp_threshold"))) ||
+ (!use_secondary_rotation && p_property.name.begins_with("secondary_")) ||
+ (!symmetry_limitation && (p_property.name == "primary_limit_angle" || p_property.name == "primary_damp_threshold" || p_property.name == "secondary_limit_angle" || p_property.name == "secondary_damp_threshold")) ||
+ (symmetry_limitation && (p_property.name.begins_with("primary_positive") || p_property.name.begins_with("primary_negative") || p_property.name.begins_with("secondary_positive") || (p_property.name.begins_with("secondary_negative"))))) {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ }
+}
+
+PackedStringArray LookAtModifier3D::get_configuration_warnings() const {
+ PackedStringArray warnings = SkeletonModifier3D::get_configuration_warnings();
+ if (get_axis_from_bone_axis(forward_axis) == primary_rotation_axis) {
+ warnings.push_back(RTR("Forward axis and primary rotation axis must not be parallel."));
+ }
+ return warnings;
+}
+
+void LookAtModifier3D::set_bone(int p_bone) {
+ bone = p_bone;
+}
+
+int LookAtModifier3D::get_bone() const {
+ return bone;
+}
+
+void LookAtModifier3D::set_forward_axis(BoneAxis p_axis) {
+ forward_axis = p_axis;
+ update_configuration_warnings();
+}
+
+LookAtModifier3D::BoneAxis LookAtModifier3D::get_forward_axis() const {
+ return forward_axis;
+}
+
+void LookAtModifier3D::set_primary_rotation_axis(Vector3::Axis p_axis) {
+ primary_rotation_axis = p_axis;
+ update_configuration_warnings();
+}
+
+Vector3::Axis LookAtModifier3D::get_primary_rotation_axis() const {
+ return primary_rotation_axis;
+}
+
+void LookAtModifier3D::set_use_secondary_rotation(bool p_enabled) {
+ use_secondary_rotation = p_enabled;
+ notify_property_list_changed();
+}
+
+bool LookAtModifier3D::is_using_secondary_rotation() const {
+ return use_secondary_rotation;
+}
+
+void LookAtModifier3D::set_target_node(const NodePath &p_target_node) {
+ if (target_node != p_target_node) {
+ init_transition();
+ }
+ target_node = p_target_node;
+}
+
+NodePath LookAtModifier3D::get_target_node() const {
+ return target_node;
+}
+
+// For origin settings.
+
+void LookAtModifier3D::set_origin_from(OriginFrom p_origin_from) {
+ origin_from = p_origin_from;
+ notify_property_list_changed();
+}
+
+LookAtModifier3D::OriginFrom LookAtModifier3D::get_origin_from() const {
+ return origin_from;
+}
+
+void LookAtModifier3D::set_origin_bone(int p_bone) {
+ origin_bone = p_bone;
+}
+
+int LookAtModifier3D::get_origin_bone() const {
+ return origin_bone;
+}
+
+void LookAtModifier3D::set_origin_external_node(const NodePath &p_external_node) {
+ origin_external_node = p_external_node;
+}
+
+NodePath LookAtModifier3D::get_origin_external_node() const {
+ return origin_external_node;
+}
+
+void LookAtModifier3D::set_origin_offset(const Vector3 &p_offset) {
+ origin_offset = p_offset;
+}
+
+Vector3 LookAtModifier3D::get_origin_offset() const {
+ return origin_offset;
+}
+
+void LookAtModifier3D::set_origin_safe_margin(float p_margin) {
+ origin_safe_margin = p_margin;
+}
+
+float LookAtModifier3D::get_origin_safe_margin() const {
+ return origin_safe_margin;
+}
+
+// For time-based interpolation.
+
+void LookAtModifier3D::set_duration(float p_duration) {
+ duration = p_duration;
+ if (Math::is_zero_approx(p_duration)) {
+ time_step = 0;
+ remaining = 0;
+ } else {
+ time_step = 1.0 / p_duration; // Cache to avoid division.
+ }
+}
+
+float LookAtModifier3D::get_duration() const {
+ return duration;
+}
+
+void LookAtModifier3D::set_transition_type(Tween::TransitionType p_transition_type) {
+ transition_type = p_transition_type;
+}
+
+Tween::TransitionType LookAtModifier3D::get_transition_type() const {
+ return transition_type;
+}
+
+void LookAtModifier3D::set_ease_type(Tween::EaseType p_ease_type) {
+ ease_type = p_ease_type;
+}
+
+Tween::EaseType LookAtModifier3D::get_ease_type() const {
+ return ease_type;
+}
+
+// For angle limitation.
+
+void LookAtModifier3D::set_use_angle_limitation(bool p_enabled) {
+ use_angle_limitation = p_enabled;
+ notify_property_list_changed();
+}
+
+bool LookAtModifier3D::is_using_angle_limitation() const {
+ return use_angle_limitation;
+}
+
+void LookAtModifier3D::set_symmetry_limitation(bool p_enabled) {
+ symmetry_limitation = p_enabled;
+ notify_property_list_changed();
+}
+
+bool LookAtModifier3D::is_limitation_symmetry() const {
+ return symmetry_limitation;
+}
+
+void LookAtModifier3D::set_primary_limit_angle(float p_angle) {
+ primary_limit_angle = p_angle;
+}
+
+float LookAtModifier3D::get_primary_limit_angle() const {
+ return primary_limit_angle;
+}
+
+void LookAtModifier3D::set_primary_damp_threshold(float p_power) {
+ primary_damp_threshold = p_power;
+}
+
+float LookAtModifier3D::get_primary_damp_threshold() const {
+ return primary_damp_threshold;
+}
+
+void LookAtModifier3D::set_primary_positive_limit_angle(float p_angle) {
+ primary_positive_limit_angle = p_angle;
+}
+
+float LookAtModifier3D::get_primary_positive_limit_angle() const {
+ return primary_positive_limit_angle;
+}
+
+void LookAtModifier3D::set_primary_positive_damp_threshold(float p_power) {
+ primary_positive_damp_threshold = p_power;
+}
+
+float LookAtModifier3D::get_primary_positive_damp_threshold() const {
+ return primary_positive_damp_threshold;
+}
+
+void LookAtModifier3D::set_primary_negative_limit_angle(float p_angle) {
+ primary_negative_limit_angle = p_angle;
+}
+
+float LookAtModifier3D::get_primary_negative_limit_angle() const {
+ return primary_negative_limit_angle;
+}
+
+void LookAtModifier3D::set_primary_negative_damp_threshold(float p_power) {
+ primary_negative_damp_threshold = p_power;
+}
+
+float LookAtModifier3D::get_primary_negative_damp_threshold() const {
+ return primary_negative_damp_threshold;
+}
+
+void LookAtModifier3D::set_secondary_limit_angle(float p_angle) {
+ secondary_limit_angle = p_angle;
+}
+
+float LookAtModifier3D::get_secondary_limit_angle() const {
+ return secondary_limit_angle;
+}
+
+void LookAtModifier3D::set_secondary_damp_threshold(float p_power) {
+ secondary_damp_threshold = p_power;
+}
+
+float LookAtModifier3D::get_secondary_damp_threshold() const {
+ return secondary_damp_threshold;
+}
+
+void LookAtModifier3D::set_secondary_positive_limit_angle(float p_angle) {
+ secondary_positive_limit_angle = p_angle;
+}
+
+float LookAtModifier3D::get_secondary_positive_limit_angle() const {
+ return secondary_positive_limit_angle;
+}
+
+void LookAtModifier3D::set_secondary_positive_damp_threshold(float p_power) {
+ secondary_positive_damp_threshold = p_power;
+}
+
+float LookAtModifier3D::get_secondary_positive_damp_threshold() const {
+ return secondary_positive_damp_threshold;
+}
+
+void LookAtModifier3D::set_secondary_negative_limit_angle(float p_angle) {
+ secondary_negative_limit_angle = p_angle;
+}
+
+float LookAtModifier3D::get_secondary_negative_limit_angle() const {
+ return secondary_negative_limit_angle;
+}
+
+void LookAtModifier3D::set_secondary_negative_damp_threshold(float p_power) {
+ secondary_negative_damp_threshold = p_power;
+}
+
+float LookAtModifier3D::get_secondary_negative_damp_threshold() const {
+ return secondary_negative_damp_threshold;
+}
+
+bool LookAtModifier3D::is_target_within_limitation() const {
+ return is_within_limitations;
+}
+
+float LookAtModifier3D::get_interpolation_remaining() const {
+ return remaining * duration;
+}
+
+bool LookAtModifier3D::is_interpolating() const {
+ return Math::is_zero_approx(remaining);
+}
+
+// General API.
+
+void LookAtModifier3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_node"), &LookAtModifier3D::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &LookAtModifier3D::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_bone", "bone"), &LookAtModifier3D::set_bone);
+ ClassDB::bind_method(D_METHOD("get_bone"), &LookAtModifier3D::get_bone);
+ ClassDB::bind_method(D_METHOD("set_forward_axis", "forward_axis"), &LookAtModifier3D::set_forward_axis);
+ ClassDB::bind_method(D_METHOD("get_forward_axis"), &LookAtModifier3D::get_forward_axis);
+ ClassDB::bind_method(D_METHOD("set_primary_rotation_axis", "axis"), &LookAtModifier3D::set_primary_rotation_axis);
+ ClassDB::bind_method(D_METHOD("get_primary_rotation_axis"), &LookAtModifier3D::get_primary_rotation_axis);
+ ClassDB::bind_method(D_METHOD("set_use_secondary_rotation", "enabled"), &LookAtModifier3D::set_use_secondary_rotation);
+ ClassDB::bind_method(D_METHOD("is_using_secondary_rotation"), &LookAtModifier3D::is_using_secondary_rotation);
+ ClassDB::bind_method(D_METHOD("set_origin_safe_margin", "margin"), &LookAtModifier3D::set_origin_safe_margin);
+ ClassDB::bind_method(D_METHOD("get_origin_safe_margin"), &LookAtModifier3D::get_origin_safe_margin);
+
+ ClassDB::bind_method(D_METHOD("set_origin_from", "origin_from"), &LookAtModifier3D::set_origin_from);
+ ClassDB::bind_method(D_METHOD("get_origin_from"), &LookAtModifier3D::get_origin_from);
+ ClassDB::bind_method(D_METHOD("set_origin_bone", "bone"), &LookAtModifier3D::set_origin_bone);
+ ClassDB::bind_method(D_METHOD("get_origin_bone"), &LookAtModifier3D::get_origin_bone);
+ ClassDB::bind_method(D_METHOD("set_origin_external_node", "external_node"), &LookAtModifier3D::set_origin_external_node);
+ ClassDB::bind_method(D_METHOD("get_origin_external_node"), &LookAtModifier3D::get_origin_external_node);
+
+ ClassDB::bind_method(D_METHOD("set_origin_offset", "offset"), &LookAtModifier3D::set_origin_offset);
+ ClassDB::bind_method(D_METHOD("get_origin_offset"), &LookAtModifier3D::get_origin_offset);
+
+ ClassDB::bind_method(D_METHOD("set_duration", "duration"), &LookAtModifier3D::set_duration);
+ ClassDB::bind_method(D_METHOD("get_duration"), &LookAtModifier3D::get_duration);
+ ClassDB::bind_method(D_METHOD("set_transition_type", "transition_type"), &LookAtModifier3D::set_transition_type);
+ ClassDB::bind_method(D_METHOD("get_transition_type"), &LookAtModifier3D::get_transition_type);
+ ClassDB::bind_method(D_METHOD("set_ease_type", "ease_type"), &LookAtModifier3D::set_ease_type);
+ ClassDB::bind_method(D_METHOD("get_ease_type"), &LookAtModifier3D::get_ease_type);
+
+ ClassDB::bind_method(D_METHOD("set_use_angle_limitation", "enabled"), &LookAtModifier3D::set_use_angle_limitation);
+ ClassDB::bind_method(D_METHOD("is_using_angle_limitation"), &LookAtModifier3D::is_using_angle_limitation);
+ ClassDB::bind_method(D_METHOD("set_symmetry_limitation", "enabled"), &LookAtModifier3D::set_symmetry_limitation);
+ ClassDB::bind_method(D_METHOD("is_limitation_symmetry"), &LookAtModifier3D::is_limitation_symmetry);
+
+ ClassDB::bind_method(D_METHOD("set_primary_limit_angle", "angle"), &LookAtModifier3D::set_primary_limit_angle);
+ ClassDB::bind_method(D_METHOD("get_primary_limit_angle"), &LookAtModifier3D::get_primary_limit_angle);
+ ClassDB::bind_method(D_METHOD("set_primary_damp_threshold", "power"), &LookAtModifier3D::set_primary_damp_threshold);
+ ClassDB::bind_method(D_METHOD("get_primary_damp_threshold"), &LookAtModifier3D::get_primary_damp_threshold);
+
+ ClassDB::bind_method(D_METHOD("set_primary_positive_limit_angle", "angle"), &LookAtModifier3D::set_primary_positive_limit_angle);
+ ClassDB::bind_method(D_METHOD("get_primary_positive_limit_angle"), &LookAtModifier3D::get_primary_positive_limit_angle);
+ ClassDB::bind_method(D_METHOD("set_primary_positive_damp_threshold", "power"), &LookAtModifier3D::set_primary_positive_damp_threshold);
+ ClassDB::bind_method(D_METHOD("get_primary_positive_damp_threshold"), &LookAtModifier3D::get_primary_positive_damp_threshold);
+ ClassDB::bind_method(D_METHOD("set_primary_negative_limit_angle", "angle"), &LookAtModifier3D::set_primary_negative_limit_angle);
+ ClassDB::bind_method(D_METHOD("get_primary_negative_limit_angle"), &LookAtModifier3D::get_primary_negative_limit_angle);
+ ClassDB::bind_method(D_METHOD("set_primary_negative_damp_threshold", "power"), &LookAtModifier3D::set_primary_negative_damp_threshold);
+ ClassDB::bind_method(D_METHOD("get_primary_negative_damp_threshold"), &LookAtModifier3D::get_primary_negative_damp_threshold);
+
+ ClassDB::bind_method(D_METHOD("set_secondary_limit_angle", "angle"), &LookAtModifier3D::set_secondary_limit_angle);
+ ClassDB::bind_method(D_METHOD("get_secondary_limit_angle"), &LookAtModifier3D::get_secondary_limit_angle);
+ ClassDB::bind_method(D_METHOD("set_secondary_damp_threshold", "power"), &LookAtModifier3D::set_secondary_damp_threshold);
+ ClassDB::bind_method(D_METHOD("get_secondary_damp_threshold"), &LookAtModifier3D::get_secondary_damp_threshold);
+
+ ClassDB::bind_method(D_METHOD("set_secondary_positive_limit_angle", "angle"), &LookAtModifier3D::set_secondary_positive_limit_angle);
+ ClassDB::bind_method(D_METHOD("get_secondary_positive_limit_angle"), &LookAtModifier3D::get_secondary_positive_limit_angle);
+ ClassDB::bind_method(D_METHOD("set_secondary_positive_damp_threshold", "power"), &LookAtModifier3D::set_secondary_positive_damp_threshold);
+ ClassDB::bind_method(D_METHOD("get_secondary_positive_damp_threshold"), &LookAtModifier3D::get_secondary_positive_damp_threshold);
+ ClassDB::bind_method(D_METHOD("set_secondary_negative_limit_angle", "angle"), &LookAtModifier3D::set_secondary_negative_limit_angle);
+ ClassDB::bind_method(D_METHOD("get_secondary_negative_limit_angle"), &LookAtModifier3D::get_secondary_negative_limit_angle);
+ ClassDB::bind_method(D_METHOD("set_secondary_negative_damp_threshold", "power"), &LookAtModifier3D::set_secondary_negative_damp_threshold);
+ ClassDB::bind_method(D_METHOD("get_secondary_negative_damp_threshold"), &LookAtModifier3D::get_secondary_negative_damp_threshold);
+
+ ClassDB::bind_method(D_METHOD("get_interpolation_remaining"), &LookAtModifier3D::get_interpolation_remaining);
+ ClassDB::bind_method(D_METHOD("is_interpolating"), &LookAtModifier3D::is_interpolating);
+ ClassDB::bind_method(D_METHOD("is_target_within_limitation"), &LookAtModifier3D::is_target_within_limitation);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_target_node", "get_target_node");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone", PROPERTY_HINT_ENUM, ""), "set_bone", "get_bone");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "forward_axis", PROPERTY_HINT_ENUM, "+X,-X,+Y,-Y,+Z,-Z"), "set_forward_axis", "get_forward_axis");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "primary_rotation_axis", PROPERTY_HINT_ENUM, "X,Y,Z"), "set_primary_rotation_axis", "get_primary_rotation_axis");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_secondary_rotation"), "set_use_secondary_rotation", "is_using_secondary_rotation");
+
+ ADD_GROUP("Origin Settings", "origin_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_from", PROPERTY_HINT_ENUM, "Self,SpecificBone,ExternalNode"), "set_origin_from", "get_origin_from");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_bone", PROPERTY_HINT_ENUM, ""), "set_origin_bone", "get_origin_bone");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "origin_external_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_origin_external_node", "get_origin_external_node");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "origin_safe_margin", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_origin_safe_margin", "get_origin_safe_margin");
+
+ ADD_GROUP("Time Based Interpolation", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "duration", PROPERTY_HINT_RANGE, "0,10,0.001,or_greater,suffix:s"), "set_duration", "get_duration");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transition_type", PROPERTY_HINT_ENUM, "Linear,Sine,Quint,Quart,Quad,Expo,Elastic,Cubic,Circ,Bounce,Back,Spring"), "set_transition_type", "get_transition_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "ease_type", PROPERTY_HINT_ENUM, "In,Out,InOut,OutIn"), "set_ease_type", "get_ease_type");
+
+ ADD_GROUP("Angle Limitation", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_angle_limitation"), "set_use_angle_limitation", "is_using_angle_limitation");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "symmetry_limitation"), "set_symmetry_limitation", "is_limitation_symmetry");
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_limit_angle", PROPERTY_HINT_RANGE, "0,360,0.01,radians_as_degrees"), "set_primary_limit_angle", "get_primary_limit_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_primary_damp_threshold", "get_primary_damp_threshold");
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_positive_limit_angle", PROPERTY_HINT_RANGE, "0,180,0.01,radians_as_degrees"), "set_primary_positive_limit_angle", "get_primary_positive_limit_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_positive_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_primary_positive_damp_threshold", "get_primary_positive_damp_threshold");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_negative_limit_angle", PROPERTY_HINT_RANGE, "0,180,0.01,radians_as_degrees"), "set_primary_negative_limit_angle", "get_primary_negative_limit_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_negative_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_primary_negative_damp_threshold", "get_primary_negative_damp_threshold");
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_limit_angle", PROPERTY_HINT_RANGE, "0,360,0.01,radians_as_degrees"), "set_secondary_limit_angle", "get_secondary_limit_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_secondary_damp_threshold", "get_secondary_damp_threshold");
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_positive_limit_angle", PROPERTY_HINT_RANGE, "0,180,0.01,radians_as_degrees"), "set_secondary_positive_limit_angle", "get_secondary_positive_limit_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_positive_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_secondary_positive_damp_threshold", "get_secondary_positive_damp_threshold");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_negative_limit_angle", PROPERTY_HINT_RANGE, "0,180,0.01,radians_as_degrees"), "set_secondary_negative_limit_angle", "get_secondary_negative_limit_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_negative_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_secondary_negative_damp_threshold", "get_secondary_negative_damp_threshold");
+
+ BIND_ENUM_CONSTANT(BONE_AXIS_PLUS_X);
+ BIND_ENUM_CONSTANT(BONE_AXIS_MINUS_X);
+ BIND_ENUM_CONSTANT(BONE_AXIS_PLUS_Y);
+ BIND_ENUM_CONSTANT(BONE_AXIS_MINUS_Y);
+ BIND_ENUM_CONSTANT(BONE_AXIS_PLUS_Z);
+ BIND_ENUM_CONSTANT(BONE_AXIS_MINUS_Z);
+
+ BIND_ENUM_CONSTANT(ORIGIN_FROM_SELF);
+ BIND_ENUM_CONSTANT(ORIGIN_FROM_SPECIFIC_BONE);
+ BIND_ENUM_CONSTANT(ORIGIN_FROM_EXTERNAL_NODE);
+}
+
+void LookAtModifier3D::_process_modification() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton || bone < 0 || bone >= skeleton->get_bone_count()) {
+ return;
+ }
+
+ // Calculate bone rest space in the world.
+ Transform3D bone_rest_space;
+ int parent_bone = skeleton->get_bone_parent(bone);
+ if (parent_bone < 0) {
+ bone_rest_space = skeleton->get_global_transform() * skeleton->get_bone_rest(bone);
+ } else {
+ bone_rest_space = skeleton->get_global_transform() * skeleton->get_bone_global_pose(parent_bone) * skeleton->get_bone_rest(bone);
+ }
+
+ // Calculate forward_vector and destination.
+ is_within_limitations = true;
+ Vector3 prev_forward_vector = forward_vector;
+ Quaternion destination;
+ Node3D *target = Object::cast_to<Node3D>(get_node_or_null(target_node));
+ if (!target) {
+ destination = skeleton->get_bone_pose_rotation(bone);
+ } else {
+ Transform3D origin_tr;
+ if (origin_from == ORIGIN_FROM_SPECIFIC_BONE && origin_bone < skeleton->get_bone_count()) {
+ origin_tr = skeleton->get_global_transform() * skeleton->get_bone_global_pose(origin_bone);
+ } else if (origin_from == ORIGIN_FROM_EXTERNAL_NODE) {
+ Node3D *origin_src = Object::cast_to<Node3D>(get_node_or_null(origin_external_node));
+ if (origin_src) {
+ origin_tr = origin_src->get_global_transform();
+ } else {
+ origin_tr = bone_rest_space;
+ }
+ } else {
+ origin_tr = bone_rest_space;
+ }
+ forward_vector = bone_rest_space.basis.xform_inv((target->get_global_position() - origin_tr.translated_local(origin_offset).origin));
+ forward_vector_nrm = forward_vector.normalized();
+ if (forward_vector_nrm.abs().is_equal_approx(get_vector_from_axis(primary_rotation_axis))) {
+ destination = skeleton->get_bone_pose_rotation(bone);
+ forward_vector = Vector3(0, 0, 0); // The zero-vector to be used for checking in the line immediately below to avoid animation glitch.
+ } else {
+ destination = look_at_with_axes(skeleton->get_bone_rest(bone)).basis.get_rotation_quaternion();
+ }
+ }
+
+ // Detect flipping.
+ Vector3::Axis current_forward_axis = get_axis_from_bone_axis(forward_axis);
+ if (is_intersecting_axis(prev_forward_vector, forward_vector, current_forward_axis, secondary_rotation_axis) ||
+ is_intersecting_axis(prev_forward_vector, forward_vector, primary_rotation_axis, primary_rotation_axis, true) ||
+ is_intersecting_axis(prev_forward_vector, forward_vector, secondary_rotation_axis, current_forward_axis) ||
+ (prev_forward_vector != Vector3(0, 0, 0) && forward_vector == Vector3(0, 0, 0)) ||
+ (prev_forward_vector == Vector3(0, 0, 0) && forward_vector != Vector3(0, 0, 0))) {
+ init_transition();
+ } else if (use_angle_limitation && signbit(prev_forward_vector[secondary_rotation_axis]) != signbit(forward_vector[secondary_rotation_axis])) {
+ // Flipping by angle_limitation can be detected by sign of secondary rotation axes during forward_vector is rotated more than 90 degree from forward_axis (means dot production is negative).
+ Vector3 prev_forward_vector_nrm = forward_vector.normalized();
+ Vector3 rest_forward_vector = get_vector_from_bone_axis(forward_axis);
+ if (symmetry_limitation) {
+ if (!Math::is_equal_approx(primary_limit_angle, (float)Math_TAU) && prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && forward_vector_nrm.dot(rest_forward_vector) < 0) {
+ init_transition();
+ }
+ } else {
+ if (!Math::is_equal_approx(primary_positive_limit_angle + primary_negative_limit_angle, (float)Math_TAU) && prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && forward_vector_nrm.dot(rest_forward_vector) < 0) {
+ init_transition();
+ }
+ }
+ }
+
+ // Do time-based interpolation.
+ if (remaining > 0) {
+ double delta = 0.0;
+ if (skeleton->get_modifier_callback_mode_process() == Skeleton3D::MODIFIER_CALLBACK_MODE_PROCESS_IDLE) {
+ delta = get_process_delta_time();
+ } else {
+ delta = get_physics_process_delta_time();
+ }
+ remaining = MAX(0, remaining - time_step * delta);
+ if (use_angle_limitation) {
+ // Interpolate through the rest same as AnimationTree blending for preventing to penetrate the bone into the body.
+ Quaternion rest = skeleton->get_bone_rest(bone).basis.get_rotation_quaternion();
+ float weight = Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0);
+ destination = rest * Quaternion().slerp(rest.inverse() * from_q, 1 - weight) * Quaternion().slerp(rest.inverse() * destination, weight);
+ } else {
+ destination = from_q.slerp(destination, Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0));
+ }
+ }
+
+ skeleton->set_bone_pose_rotation(bone, destination);
+ prev_q = destination;
+}
+
+bool LookAtModifier3D::is_intersecting_axis(const Vector3 &p_prev, const Vector3 &p_current, Vector3::Axis p_flipping_axis, Vector3::Axis p_check_axis, bool p_check_plane) const {
+ // Prevent that the angular velocity does not become too large.
+ // Check that is p_flipping_axis flipped nearby p_check_axis (close than origin_safe_margin) or not. If p_check_plane is true, check two axes of crossed plane.
+ if (p_check_plane) {
+ if (get_projection_vector(p_prev, p_check_axis).length() > origin_safe_margin && get_projection_vector(p_current, p_check_axis).length() > origin_safe_margin) {
+ return false;
+ }
+ } else if (Math::abs(p_prev[p_check_axis]) > origin_safe_margin && Math::abs(p_current[p_check_axis]) > origin_safe_margin) {
+ return false;
+ }
+
+ return signbit(p_prev[p_flipping_axis]) != signbit(p_current[p_flipping_axis]);
+}
+
+Vector3 LookAtModifier3D::get_basis_vector_from_bone_axis(const Basis &p_basis, LookAtModifier3D::BoneAxis p_axis) const {
+ Vector3 ret;
+ switch (p_axis) {
+ case BONE_AXIS_PLUS_X: {
+ ret = p_basis.get_column(0);
+ } break;
+ case BONE_AXIS_MINUS_X: {
+ ret = -p_basis.get_column(0);
+ } break;
+ case BONE_AXIS_PLUS_Y: {
+ ret = p_basis.get_column(1);
+ } break;
+ case BONE_AXIS_MINUS_Y: {
+ ret = -p_basis.get_column(1);
+ } break;
+ case BONE_AXIS_PLUS_Z: {
+ ret = p_basis.get_column(2);
+ } break;
+ case BONE_AXIS_MINUS_Z: {
+ ret = -p_basis.get_column(2);
+ } break;
+ }
+ return ret;
+}
+
+Vector3 LookAtModifier3D::get_vector_from_bone_axis(const LookAtModifier3D::BoneAxis &p_axis) const {
+ Vector3 ret;
+ switch (p_axis) {
+ case BONE_AXIS_PLUS_X: {
+ ret = Vector3(1, 0, 0);
+ } break;
+ case BONE_AXIS_MINUS_X: {
+ ret = Vector3(-1, 0, 0);
+ } break;
+ case BONE_AXIS_PLUS_Y: {
+ ret = Vector3(0, 1, 0);
+ } break;
+ case BONE_AXIS_MINUS_Y: {
+ ret = Vector3(0, -1, 0);
+ } break;
+ case BONE_AXIS_PLUS_Z: {
+ ret = Vector3(0, 0, 1);
+ } break;
+ case BONE_AXIS_MINUS_Z: {
+ ret = Vector3(0, 0, -1);
+ } break;
+ }
+ return ret;
+}
+
+Vector3 LookAtModifier3D::get_vector_from_axis(const Vector3::Axis &p_axis) const {
+ Vector3 ret;
+ switch (p_axis) {
+ case Vector3::AXIS_X: {
+ ret = Vector3(1, 0, 0);
+ } break;
+ case Vector3::AXIS_Y: {
+ ret = Vector3(0, 1, 0);
+ } break;
+ case Vector3::AXIS_Z: {
+ ret = Vector3(0, 0, 1);
+ } break;
+ }
+ return ret;
+}
+
+Vector3::Axis LookAtModifier3D::get_axis_from_bone_axis(BoneAxis p_axis) const {
+ Vector3::Axis ret = Vector3::AXIS_X;
+ switch (p_axis) {
+ case BONE_AXIS_PLUS_X:
+ case BONE_AXIS_MINUS_X: {
+ ret = Vector3::AXIS_X;
+ } break;
+ case BONE_AXIS_PLUS_Y:
+ case BONE_AXIS_MINUS_Y: {
+ ret = Vector3::AXIS_Y;
+ } break;
+ case BONE_AXIS_PLUS_Z:
+ case BONE_AXIS_MINUS_Z: {
+ ret = Vector3::AXIS_Z;
+ } break;
+ }
+ return ret;
+}
+
+Vector2 LookAtModifier3D::get_projection_vector(const Vector3 &p_vector, Vector3::Axis p_axis) const {
+ // NOTE: axis is swapped between 2D and 3D.
+ Vector2 ret;
+ switch (p_axis) {
+ case Vector3::AXIS_X: {
+ ret = Vector2(p_vector.z, p_vector.y);
+ } break;
+ case Vector3::AXIS_Y: {
+ ret = Vector2(p_vector.x, p_vector.z);
+ } break;
+ case Vector3::AXIS_Z: {
+ ret = Vector2(p_vector.y, p_vector.x);
+ } break;
+ }
+ return ret;
+}
+
+float LookAtModifier3D::remap_damped(float p_from, float p_to, float p_damp_threshold, float p_value) const {
+ float sign = signbit(p_value) ? -1.0f : 1.0f;
+ float abs_value = Math::abs(p_value);
+
+ if (Math::is_equal_approx(p_damp_threshold, 1.0f) || Math::is_zero_approx(p_to)) {
+ return sign * CLAMP(abs_value, p_from, p_to); // Avoid division by zero.
+ }
+
+ float value = Math::inverse_lerp(p_from, p_to, abs_value);
+
+ if (value <= p_damp_threshold) {
+ return sign * CLAMP(abs_value, p_from, p_to);
+ }
+
+ double limit = Math_PI;
+ double inv_to = 1.0 / p_to;
+ double end_x = limit * inv_to;
+ double position = abs_value * inv_to;
+ Vector2 start = Vector2(p_damp_threshold, p_damp_threshold);
+ Vector2 mid = Vector2(1.0, 1.0);
+ Vector2 end = Vector2(end_x, 1.0);
+ value = get_bspline_y(start, mid, end, position);
+
+ return sign * Math::lerp(p_from, p_to, value);
+}
+
+double LookAtModifier3D::get_bspline_y(const Vector2 &p_from, const Vector2 &p_control, const Vector2 &p_to, double p_x) const {
+ double a = p_from.x - 2.0 * p_control.x + p_to.x;
+ double b = -2.0 * p_from.x + 2.0 * p_control.x;
+ double c = p_from.x - p_x;
+ double t = 0.0;
+ if (Math::is_zero_approx(a)) {
+ t = -c / b; // Almost linear.
+ } else {
+ double discriminant = b * b - 4.0 * a * c;
+ double sqrt_discriminant = Math::sqrt(discriminant);
+ double e = 1.0 / (2.0 * a);
+ double t1 = (-b + sqrt_discriminant) * e;
+ t = (0.0 <= t1 && t1 <= 1.0) ? t1 : (-b - sqrt_discriminant) * e;
+ }
+ double u = 1.0 - t;
+ double y = u * u * p_from.y + 2.0 * u * t * p_control.y + t * t * p_to.y;
+ return y;
+}
+
+Transform3D LookAtModifier3D::look_at_with_axes(const Transform3D &p_rest) {
+ // Primary rotation by projection to 2D plane by xform_inv and picking elements.
+ Vector3 current_vector = get_basis_vector_from_bone_axis(p_rest.basis, forward_axis).normalized();
+ Vector2 src_vec2 = get_projection_vector(p_rest.basis.xform_inv(forward_vector_nrm), primary_rotation_axis).normalized();
+ Vector2 dst_vec2 = get_projection_vector(p_rest.basis.xform_inv(current_vector), primary_rotation_axis).normalized();
+ real_t calculated_angle = src_vec2.angle_to(dst_vec2);
+ Transform3D primary_result = p_rest.rotated_local(get_vector_from_axis(primary_rotation_axis), calculated_angle);
+ Transform3D current_result = primary_result; // primary_result will be used by calculation of secondary rotation, current_result is rotated by that.
+ float limit_angle = 0.0;
+ float damp_threshold = 0.0;
+
+ if (use_angle_limitation) {
+ if (symmetry_limitation) {
+ limit_angle = primary_limit_angle * 0.5f;
+ damp_threshold = primary_damp_threshold;
+ } else {
+ if (signbit(calculated_angle)) {
+ limit_angle = primary_negative_limit_angle;
+ damp_threshold = primary_negative_damp_threshold;
+ } else {
+ limit_angle = primary_positive_limit_angle;
+ damp_threshold = primary_positive_damp_threshold;
+ }
+ }
+ if (Math::abs(calculated_angle) > limit_angle) {
+ is_within_limitations = false;
+ }
+ calculated_angle = remap_damped(0, limit_angle, damp_threshold, calculated_angle);
+ current_result = p_rest.rotated_local(get_vector_from_axis(primary_rotation_axis), calculated_angle);
+ }
+
+ // Needs for detecting flipping even if use_secondary_rotation is false.
+ Vector3 secondary_plane = get_vector_from_bone_axis(forward_axis) + get_vector_from_axis(primary_rotation_axis);
+ secondary_rotation_axis = Math::is_zero_approx(secondary_plane.x) ? Vector3::AXIS_X : (Math::is_zero_approx(secondary_plane.y) ? Vector3::AXIS_Y : Vector3::AXIS_Z);
+
+ if (!use_secondary_rotation) {
+ return current_result;
+ }
+
+ // Secondary rotation by projection to 2D plane by xform_inv and picking elements.
+ current_vector = get_basis_vector_from_bone_axis(primary_result.basis, forward_axis).normalized();
+ src_vec2 = get_projection_vector(primary_result.basis.xform_inv(forward_vector_nrm), secondary_rotation_axis).normalized();
+ dst_vec2 = get_projection_vector(primary_result.basis.xform_inv(current_vector), secondary_rotation_axis).normalized();
+ calculated_angle = src_vec2.angle_to(dst_vec2);
+
+ if (use_angle_limitation) {
+ if (symmetry_limitation) {
+ limit_angle = secondary_limit_angle * 0.5f;
+ damp_threshold = secondary_damp_threshold;
+ } else {
+ if (signbit(calculated_angle)) {
+ limit_angle = secondary_negative_limit_angle;
+ damp_threshold = secondary_negative_damp_threshold;
+ } else {
+ limit_angle = secondary_positive_limit_angle;
+ damp_threshold = secondary_positive_damp_threshold;
+ }
+ }
+ if (Math::abs(calculated_angle) > limit_angle) {
+ is_within_limitations = false;
+ }
+ calculated_angle = remap_damped(0, limit_angle, damp_threshold, calculated_angle);
+ }
+
+ current_result = current_result.rotated_local(get_vector_from_axis(secondary_rotation_axis), calculated_angle);
+
+ return current_result;
+}
+
+void LookAtModifier3D::init_transition() {
+ if (Math::is_zero_approx(duration)) {
+ return;
+ }
+ from_q = prev_q;
+ remaining = 1.0;
+}
diff --git a/scene/3d/look_at_modifier_3d.h b/scene/3d/look_at_modifier_3d.h
new file mode 100644
index 0000000000..5f3c4e8b1c
--- /dev/null
+++ b/scene/3d/look_at_modifier_3d.h
@@ -0,0 +1,199 @@
+/**************************************************************************/
+/* look_at_modifier_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef LOOK_AT_MODIFIER_3D_H
+#define LOOK_AT_MODIFIER_3D_H
+
+#include "scene/3d/skeleton_modifier_3d.h"
+#include "scene/animation/tween.h"
+
+class LookAtModifier3D : public SkeletonModifier3D {
+ GDCLASS(LookAtModifier3D, SkeletonModifier3D);
+
+public:
+ enum BoneAxis {
+ BONE_AXIS_PLUS_X,
+ BONE_AXIS_MINUS_X,
+ BONE_AXIS_PLUS_Y,
+ BONE_AXIS_MINUS_Y,
+ BONE_AXIS_PLUS_Z,
+ BONE_AXIS_MINUS_Z,
+ };
+
+ enum OriginFrom {
+ ORIGIN_FROM_SELF,
+ ORIGIN_FROM_SPECIFIC_BONE,
+ ORIGIN_FROM_EXTERNAL_NODE,
+ };
+
+private:
+ int bone = 0;
+
+ Vector3 forward_vector;
+ Vector3 forward_vector_nrm;
+ BoneAxis forward_axis = BONE_AXIS_PLUS_Z;
+ Vector3::Axis primary_rotation_axis = Vector3::AXIS_Y;
+ Vector3::Axis secondary_rotation_axis = Vector3::AXIS_X;
+ bool use_secondary_rotation = true;
+
+ OriginFrom origin_from = ORIGIN_FROM_SELF;
+ int origin_bone = -1;
+ NodePath origin_external_node;
+
+ Vector3 origin_offset;
+ float origin_safe_margin = 0.1;
+
+ NodePath target_node;
+
+ float duration = 0;
+ Tween::TransitionType transition_type = Tween::TRANS_LINEAR;
+ Tween::EaseType ease_type = Tween::EASE_IN;
+
+ bool use_angle_limitation = false;
+ bool symmetry_limitation = true;
+
+ float primary_limit_angle = Math_TAU;
+ float primary_damp_threshold = 1.0f;
+ float primary_positive_limit_angle = Math_PI;
+ float primary_positive_damp_threshold = 1.0f;
+ float primary_negative_limit_angle = Math_PI;
+ float primary_negative_damp_threshold = 1.0f;
+
+ float secondary_limit_angle = Math_TAU;
+ float secondary_damp_threshold = 1.0f;
+ float secondary_positive_limit_angle = Math_PI;
+ float secondary_positive_damp_threshold = 1.0f;
+ float secondary_negative_limit_angle = Math_PI;
+ float secondary_negative_damp_threshold = 1.0f;
+
+ bool is_within_limitations = false;
+
+ // For time-based interpolation.
+ Quaternion from_q;
+ Quaternion prev_q;
+
+ float remaining = 0;
+ float time_step = 1.0;
+
+ Vector3 get_basis_vector_from_bone_axis(const Basis &p_basis, BoneAxis p_axis) const;
+ Vector3 get_vector_from_bone_axis(const BoneAxis &p_axis) const;
+ Vector3 get_vector_from_axis(const Vector3::Axis &p_axis) const;
+ Vector3::Axis get_axis_from_bone_axis(BoneAxis p_axis) const;
+ Vector2 get_projection_vector(const Vector3 &p_vector, Vector3::Axis p_axis) const;
+ float remap_damped(float p_from, float p_to, float p_damp_threshold, float p_value) const;
+ double get_bspline_y(const Vector2 &p_from, const Vector2 &p_control, const Vector2 &p_to, double p_x) const;
+ bool is_intersecting_axis(const Vector3 &p_prev, const Vector3 &p_current, Vector3::Axis p_flipping_axis, Vector3::Axis p_check_axis, bool p_check_plane = false) const;
+
+ Transform3D look_at_with_axes(const Transform3D &p_rest);
+ void init_transition();
+
+protected:
+ virtual PackedStringArray get_configuration_warnings() const override;
+ void _validate_property(PropertyInfo &p_property) const;
+
+ static void _bind_methods();
+
+ virtual void _process_modification() override;
+
+public:
+ void set_bone(int p_bone);
+ int get_bone() const;
+
+ void set_forward_axis(BoneAxis p_axis);
+ BoneAxis get_forward_axis() const;
+ void set_primary_rotation_axis(Vector3::Axis p_axis);
+ Vector3::Axis get_primary_rotation_axis() const;
+ void set_use_secondary_rotation(bool p_enabled);
+ bool is_using_secondary_rotation() const;
+
+ void set_origin_from(OriginFrom p_origin_from);
+ OriginFrom get_origin_from() const;
+ void set_origin_bone(int p_bone);
+ int get_origin_bone() const;
+ void set_origin_external_node(const NodePath &p_external_node);
+ NodePath get_origin_external_node() const;
+
+ void set_origin_offset(const Vector3 &p_offset);
+ Vector3 get_origin_offset() const;
+ void set_origin_safe_margin(float p_margin);
+ float get_origin_safe_margin() const;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_duration(float p_duration);
+ float get_duration() const;
+ void set_transition_type(Tween::TransitionType p_transition_type);
+ Tween::TransitionType get_transition_type() const;
+ void set_ease_type(Tween::EaseType p_ease_type);
+ Tween::EaseType get_ease_type() const;
+
+ void set_use_angle_limitation(bool p_enabled);
+ bool is_using_angle_limitation() const;
+ void set_symmetry_limitation(bool p_enabled);
+ bool is_limitation_symmetry() const;
+
+ void set_primary_limit_angle(float p_angle);
+ float get_primary_limit_angle() const;
+ void set_primary_damp_threshold(float p_power);
+ float get_primary_damp_threshold() const;
+
+ void set_primary_positive_limit_angle(float p_angle);
+ float get_primary_positive_limit_angle() const;
+ void set_primary_positive_damp_threshold(float p_power);
+ float get_primary_positive_damp_threshold() const;
+ void set_primary_negative_limit_angle(float p_angle);
+ float get_primary_negative_limit_angle() const;
+ void set_primary_negative_damp_threshold(float p_power);
+ float get_primary_negative_damp_threshold() const;
+
+ void set_secondary_limit_angle(float p_angle);
+ float get_secondary_limit_angle() const;
+ void set_secondary_damp_threshold(float p_power);
+ float get_secondary_damp_threshold() const;
+
+ void set_secondary_positive_limit_angle(float p_angle);
+ float get_secondary_positive_limit_angle() const;
+ void set_secondary_positive_damp_threshold(float p_power);
+ float get_secondary_positive_damp_threshold() const;
+ void set_secondary_negative_limit_angle(float p_angle);
+ float get_secondary_negative_limit_angle() const;
+ void set_secondary_negative_damp_threshold(float p_power);
+ float get_secondary_negative_damp_threshold() const;
+
+ float get_interpolation_remaining() const;
+ bool is_interpolating() const;
+ bool is_target_within_limitation() const;
+};
+
+VARIANT_ENUM_CAST(LookAtModifier3D::BoneAxis);
+VARIANT_ENUM_CAST(LookAtModifier3D::OriginFrom);
+
+#endif // LOOK_AT_MODIFIER_3D_H
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..68a6505897 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -92,33 +92,30 @@ void NavigationObstacle3D::_notification(int p_what) {
} else {
_update_map(RID());
}
- previous_transform = get_global_transform();
// need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
NavigationServer3D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
- _update_position(get_global_transform().origin);
+ _update_position(get_global_position());
set_physics_process_internal(true);
#ifdef DEBUG_ENABLED
- if ((NavigationServer3D::get_singleton()->get_debug_avoidance_enabled()) &&
- (NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius())) {
- _update_fake_agent_radius_debug();
- _update_static_obstacle_debug();
- }
+ _update_debug();
#endif // DEBUG_ENABLED
} break;
+#ifdef TOOLS_ENABLED
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ update_gizmos();
+ } break;
+#endif // TOOLS_ENABLED
+
case NOTIFICATION_EXIT_TREE: {
set_physics_process_internal(false);
_update_map(RID());
#ifdef DEBUG_ENABLED
- if (fake_agent_radius_debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, false);
- }
- if (static_obstacle_debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
- }
+ _update_debug();
#endif // DEBUG_ENABLED
} break;
+ case NOTIFICATION_SUSPENDED:
case NOTIFICATION_PAUSED: {
if (!can_process()) {
map_before_pause = map_current;
@@ -130,6 +127,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;
@@ -143,20 +147,13 @@ void NavigationObstacle3D::_notification(int p_what) {
#ifdef DEBUG_ENABLED
case NOTIFICATION_VISIBILITY_CHANGED: {
- if (is_inside_tree()) {
- if (fake_agent_radius_debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, is_visible_in_tree());
- }
- if (static_obstacle_debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, is_visible_in_tree());
- }
- }
+ _update_debug();
} break;
#endif // DEBUG_ENABLED
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (is_inside_tree()) {
- _update_position(get_global_transform().origin);
+ _update_position(get_global_position());
if (velocity_submitted) {
velocity_submitted = false;
@@ -167,15 +164,23 @@ void NavigationObstacle3D::_notification(int p_what) {
previous_velocity = velocity;
}
#ifdef DEBUG_ENABLED
- if (fake_agent_radius_debug_instance.is_valid() && radius > 0.0) {
- Transform3D debug_transform;
- debug_transform.origin = get_global_position();
- RS::get_singleton()->instance_set_transform(fake_agent_radius_debug_instance, debug_transform);
+ if (fake_agent_radius_debug_instance_rid.is_valid() && radius > 0.0) {
+ // 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_rid, debug_transform);
}
- if (static_obstacle_debug_instance.is_valid() && get_vertices().size() > 0) {
- Transform3D debug_transform;
- debug_transform.origin = get_global_position();
- RS::get_singleton()->instance_set_transform(static_obstacle_debug_instance, debug_transform);
+ if (static_obstacle_debug_instance_rid.is_valid() && get_vertices().size() > 0) {
+ // 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_rid, debug_transform);
}
#endif // DEBUG_ENABLED
}
@@ -184,53 +189,79 @@ void NavigationObstacle3D::_notification(int p_what) {
}
NavigationObstacle3D::NavigationObstacle3D() {
- obstacle = NavigationServer3D::get_singleton()->obstacle_create();
+ NavigationServer3D *ns3d = NavigationServer3D::get_singleton();
- NavigationServer3D::get_singleton()->obstacle_set_height(obstacle, height);
- NavigationServer3D::get_singleton()->obstacle_set_radius(obstacle, radius);
- NavigationServer3D::get_singleton()->obstacle_set_vertices(obstacle, vertices);
- NavigationServer3D::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
- NavigationServer3D::get_singleton()->obstacle_set_use_3d_avoidance(obstacle, use_3d_avoidance);
- NavigationServer3D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
+ obstacle = ns3d->obstacle_create();
+
+ ns3d->obstacle_set_height(obstacle, height);
+ ns3d->obstacle_set_radius(obstacle, radius);
+ ns3d->obstacle_set_vertices(obstacle, vertices);
+ ns3d->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
+ ns3d->obstacle_set_use_3d_avoidance(obstacle, use_3d_avoidance);
+ ns3d->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton()->connect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_fake_agent_radius_debug));
- NavigationServer3D::get_singleton()->connect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_static_obstacle_debug));
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ fake_agent_radius_debug_mesh_rid = rs->mesh_create();
+ static_obstacle_debug_mesh_rid = rs->mesh_create();
+
+ fake_agent_radius_debug_instance_rid = rs->instance_create();
+ static_obstacle_debug_instance_rid = rs->instance_create();
+
+ rs->instance_set_base(fake_agent_radius_debug_instance_rid, fake_agent_radius_debug_mesh_rid);
+ rs->instance_set_base(static_obstacle_debug_instance_rid, static_obstacle_debug_mesh_rid);
+
+ ns3d->connect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_fake_agent_radius_debug));
+ ns3d->connect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_static_obstacle_debug));
_update_fake_agent_radius_debug();
_update_static_obstacle_debug();
#endif // DEBUG_ENABLED
+
+#ifdef TOOLS_ENABLED
+ set_notify_transform(true);
+#endif // TOOLS_ENABLED
}
NavigationObstacle3D::~NavigationObstacle3D() {
- ERR_FAIL_NULL(NavigationServer3D::get_singleton());
+ NavigationServer3D *ns3d = NavigationServer3D::get_singleton();
+ ERR_FAIL_NULL(ns3d);
- NavigationServer3D::get_singleton()->free(obstacle);
+ ns3d->free(obstacle);
obstacle = RID();
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton()->disconnect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_fake_agent_radius_debug));
- NavigationServer3D::get_singleton()->disconnect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_static_obstacle_debug));
- if (fake_agent_radius_debug_instance.is_valid()) {
- RenderingServer::get_singleton()->free(fake_agent_radius_debug_instance);
+ ns3d->disconnect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_fake_agent_radius_debug));
+ ns3d->disconnect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_static_obstacle_debug));
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rs);
+ if (fake_agent_radius_debug_instance_rid.is_valid()) {
+ rs->free(fake_agent_radius_debug_instance_rid);
+ fake_agent_radius_debug_instance_rid = RID();
}
- if (fake_agent_radius_debug_mesh.is_valid()) {
- RenderingServer::get_singleton()->free(fake_agent_radius_debug_mesh->get_rid());
+ if (fake_agent_radius_debug_mesh_rid.is_valid()) {
+ rs->free(fake_agent_radius_debug_mesh_rid);
+ fake_agent_radius_debug_mesh_rid = RID();
}
-
- if (static_obstacle_debug_instance.is_valid()) {
- RenderingServer::get_singleton()->free(static_obstacle_debug_instance);
+ if (static_obstacle_debug_instance_rid.is_valid()) {
+ rs->free(static_obstacle_debug_instance_rid);
+ static_obstacle_debug_instance_rid = RID();
}
- if (static_obstacle_debug_mesh.is_valid()) {
- RenderingServer::get_singleton()->free(static_obstacle_debug_mesh->get_rid());
+ if (static_obstacle_debug_mesh_rid.is_valid()) {
+ rs->free(static_obstacle_debug_mesh_rid);
+ static_obstacle_debug_mesh_rid = RID();
}
#endif // DEBUG_ENABLED
}
void NavigationObstacle3D::set_vertices(const Vector<Vector3> &p_vertices) {
vertices = p_vertices;
+
NavigationServer3D::get_singleton()->obstacle_set_vertices(obstacle, vertices);
#ifdef DEBUG_ENABLED
_update_static_obstacle_debug();
+ update_gizmos();
#endif // DEBUG_ENABLED
}
@@ -262,6 +293,7 @@ void NavigationObstacle3D::set_radius(real_t p_radius) {
#ifdef DEBUG_ENABLED
_update_fake_agent_radius_debug();
+ update_gizmos();
#endif // DEBUG_ENABLED
}
@@ -276,6 +308,7 @@ void NavigationObstacle3D::set_height(real_t p_height) {
#ifdef DEBUG_ENABLED
_update_static_obstacle_debug();
+ update_gizmos();
#endif // DEBUG_ENABLED
}
@@ -346,6 +379,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;
@@ -361,30 +413,43 @@ void NavigationObstacle3D::_update_use_3d_avoidance(bool p_use_3d_avoidance) {
}
#ifdef DEBUG_ENABLED
+void NavigationObstacle3D::_update_debug() {
+ RenderingServer *rs = RenderingServer::get_singleton();
+ if (is_inside_tree()) {
+ rs->instance_set_visible(fake_agent_radius_debug_instance_rid, is_visible_in_tree());
+ rs->instance_set_visible(static_obstacle_debug_instance_rid, is_visible_in_tree());
+ rs->instance_set_scenario(fake_agent_radius_debug_instance_rid, get_world_3d()->get_scenario());
+ rs->instance_set_scenario(static_obstacle_debug_instance_rid, get_world_3d()->get_scenario());
+ rs->instance_set_transform(fake_agent_radius_debug_instance_rid, Transform3D(Basis(), get_global_position()));
+ rs->instance_set_transform(static_obstacle_debug_instance_rid, Transform3D(Basis(), get_global_position()));
+ _update_fake_agent_radius_debug();
+ _update_static_obstacle_debug();
+ } else {
+ rs->mesh_clear(fake_agent_radius_debug_mesh_rid);
+ rs->mesh_clear(static_obstacle_debug_mesh_rid);
+ rs->instance_set_scenario(fake_agent_radius_debug_instance_rid, RID());
+ rs->instance_set_scenario(static_obstacle_debug_instance_rid, RID());
+ }
+}
+
void NavigationObstacle3D::_update_fake_agent_radius_debug() {
+ NavigationServer3D *ns3d = NavigationServer3D::get_singleton();
+ RenderingServer *rs = RenderingServer::get_singleton();
+
bool is_debug_enabled = false;
if (Engine::get_singleton()->is_editor_hint()) {
is_debug_enabled = true;
- } else if (NavigationServer3D::get_singleton()->get_debug_enabled() &&
- NavigationServer3D::get_singleton()->get_debug_avoidance_enabled() &&
- NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
+ } else if (ns3d->get_debug_enabled() &&
+ ns3d->get_debug_avoidance_enabled() &&
+ ns3d->get_debug_navigation_avoidance_enable_obstacles_radius()) {
is_debug_enabled = true;
}
- if (is_debug_enabled == false) {
- if (fake_agent_radius_debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, false);
- }
- return;
- }
+ rs->mesh_clear(fake_agent_radius_debug_mesh_rid);
- 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 (!is_debug_enabled) {
+ return;
}
- fake_agent_radius_debug_mesh->clear_surfaces();
Vector<Vector3> face_vertex_array;
Vector<int> face_indices_array;
@@ -439,147 +504,106 @@ void NavigationObstacle3D::_update_fake_agent_radius_debug() {
face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
face_mesh_array[Mesh::ARRAY_INDEX] = face_indices_array;
- fake_agent_radius_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
- Ref<StandardMaterial3D> face_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_material();
- fake_agent_radius_debug_mesh->surface_set_material(0, face_material);
+ rs->mesh_add_surface_from_arrays(fake_agent_radius_debug_mesh_rid, RS::PRIMITIVE_TRIANGLES, face_mesh_array);
+
+ Ref<StandardMaterial3D> face_material = ns3d->get_debug_navigation_avoidance_obstacles_radius_material();
+ rs->instance_set_surface_override_material(fake_agent_radius_debug_instance_rid, 0, face_material->get_rid());
- RS::get_singleton()->instance_set_base(fake_agent_radius_debug_instance, fake_agent_radius_debug_mesh->get_rid());
if (is_inside_tree()) {
- RS::get_singleton()->instance_set_scenario(fake_agent_radius_debug_instance, get_world_3d()->get_scenario());
- RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, is_visible_in_tree());
+ rs->instance_set_scenario(fake_agent_radius_debug_instance_rid, get_world_3d()->get_scenario());
+ rs->instance_set_visible(fake_agent_radius_debug_instance_rid, is_visible_in_tree());
}
}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
void NavigationObstacle3D::_update_static_obstacle_debug() {
- bool is_debug_enabled = false;
if (Engine::get_singleton()->is_editor_hint()) {
- is_debug_enabled = true;
- } else if (NavigationServer3D::get_singleton()->get_debug_enabled() &&
- NavigationServer3D::get_singleton()->get_debug_avoidance_enabled() &&
- NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
- is_debug_enabled = true;
- }
-
- if (is_debug_enabled == false) {
- if (static_obstacle_debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
- }
+ // Don't update inside Editor as Node3D gizmo takes care of this.
return;
}
- if (vertices.size() < 3) {
- if (static_obstacle_debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
- }
- return;
- }
-
- 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));
- }
- static_obstacle_debug_mesh->clear_surfaces();
+ NavigationServer3D *ns3d = NavigationServer3D::get_singleton();
+ RenderingServer *rs = RenderingServer::get_singleton();
- Vector<Vector2> polygon_2d_vertices;
- polygon_2d_vertices.resize(vertices.size());
- Vector2 *polygon_2d_vertices_ptr = polygon_2d_vertices.ptrw();
-
- for (int i = 0; i < vertices.size(); ++i) {
- Vector3 obstacle_vertex = vertices[i];
- Vector2 obstacle_vertex_2d = Vector2(obstacle_vertex.x, obstacle_vertex.z);
- polygon_2d_vertices_ptr[i] = obstacle_vertex_2d;
+ bool is_debug_enabled = false;
+ if (ns3d->get_debug_enabled() &&
+ ns3d->get_debug_avoidance_enabled() &&
+ ns3d->get_debug_navigation_avoidance_enable_obstacles_static()) {
+ is_debug_enabled = true;
}
- Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(polygon_2d_vertices);
+ rs->mesh_clear(static_obstacle_debug_mesh_rid);
- if (triangulated_polygon_2d_indices.is_empty()) {
- // failed triangulation
+ if (!is_debug_enabled) {
return;
}
- bool obstacle_pushes_inward = Geometry2D::is_polygon_clockwise(polygon_2d_vertices);
-
- Vector<Vector3> face_vertex_array;
- Vector<int> face_indices_array;
-
- face_vertex_array.resize(polygon_2d_vertices.size());
- face_indices_array.resize(triangulated_polygon_2d_indices.size());
-
- Vector3 *face_vertex_array_ptr = face_vertex_array.ptrw();
- int *face_indices_array_ptr = face_indices_array.ptrw();
+ const int vertex_count = vertices.size();
- for (int i = 0; i < triangulated_polygon_2d_indices.size(); ++i) {
- int vertex_index = triangulated_polygon_2d_indices[i];
- const Vector2 &vertex_2d = polygon_2d_vertices[vertex_index];
- Vector3 vertex_3d = Vector3(vertex_2d.x, 0.0, vertex_2d.y);
- face_vertex_array_ptr[vertex_index] = vertex_3d;
- face_indices_array_ptr[i] = vertex_index;
+ if (vertex_count < 3) {
+ if (static_obstacle_debug_instance_rid.is_valid()) {
+ rs->instance_set_visible(static_obstacle_debug_instance_rid, false);
+ }
+ return;
}
- Array face_mesh_array;
- face_mesh_array.resize(Mesh::ARRAY_MAX);
- face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
- face_mesh_array[Mesh::ARRAY_INDEX] = face_indices_array;
-
- static_obstacle_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
-
Vector<Vector3> edge_vertex_array;
+ edge_vertex_array.resize(vertex_count * 8);
- for (int i = 0; i < polygon_2d_vertices.size(); ++i) {
- int from_index = i - 1;
- int to_index = i;
+ Vector3 *edge_vertex_array_ptrw = edge_vertex_array.ptrw();
- if (i == 0) {
- from_index = polygon_2d_vertices.size() - 1;
- }
+ int vertex_index = 0;
- const Vector2 &vertex_2d_from = polygon_2d_vertices[from_index];
- const Vector2 &vertex_2d_to = polygon_2d_vertices[to_index];
+ for (int i = 0; i < vertex_count; i++) {
+ Vector3 point = vertices[i];
+ Vector3 next_point = vertices[(i + 1) % vertex_count];
- Vector3 vertex_3d_ground_from = Vector3(vertex_2d_from.x, 0.0, vertex_2d_from.y);
- Vector3 vertex_3d_ground_to = Vector3(vertex_2d_to.x, 0.0, vertex_2d_to.y);
+ Vector3 direction = next_point.direction_to(point);
+ Vector3 arrow_dir = direction.cross(Vector3(0.0, 1.0, 0.0));
+ Vector3 edge_middle = point + ((next_point - point) * 0.5);
- edge_vertex_array.push_back(vertex_3d_ground_from);
- edge_vertex_array.push_back(vertex_3d_ground_to);
+ edge_vertex_array_ptrw[vertex_index++] = edge_middle;
+ edge_vertex_array_ptrw[vertex_index++] = edge_middle + (arrow_dir * 0.5);
- Vector3 vertex_3d_height_from = Vector3(vertex_2d_from.x, height, vertex_2d_from.y);
- Vector3 vertex_3d_height_to = Vector3(vertex_2d_to.x, height, vertex_2d_to.y);
+ edge_vertex_array_ptrw[vertex_index++] = point;
+ edge_vertex_array_ptrw[vertex_index++] = next_point;
- edge_vertex_array.push_back(vertex_3d_height_from);
- edge_vertex_array.push_back(vertex_3d_height_to);
+ edge_vertex_array_ptrw[vertex_index++] = Vector3(point.x, height, point.z);
+ edge_vertex_array_ptrw[vertex_index++] = Vector3(next_point.x, height, next_point.z);
- edge_vertex_array.push_back(vertex_3d_ground_from);
- edge_vertex_array.push_back(vertex_3d_height_from);
+ edge_vertex_array_ptrw[vertex_index++] = point;
+ edge_vertex_array_ptrw[vertex_index++] = Vector3(point.x, height, point.z);
}
Array edge_mesh_array;
edge_mesh_array.resize(Mesh::ARRAY_MAX);
edge_mesh_array[Mesh::ARRAY_VERTEX] = edge_vertex_array;
- static_obstacle_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, edge_mesh_array);
+ rs->mesh_add_surface_from_arrays(static_obstacle_debug_mesh_rid, RS::PRIMITIVE_LINES, edge_mesh_array);
+
+ Vector<Vector2> polygon_2d_vertices;
+ polygon_2d_vertices.resize(vertex_count);
+ for (int i = 0; i < vertex_count; i++) {
+ const Vector3 &vert = vertices[i];
+ polygon_2d_vertices.write[i] = Vector2(vert.x, vert.z);
+ }
+
+ Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(polygon_2d_vertices);
- Ref<StandardMaterial3D> face_material;
Ref<StandardMaterial3D> edge_material;
- if (obstacle_pushes_inward) {
- face_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_material();
- edge_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_material();
+ if (triangulated_polygon_2d_indices.is_empty()) {
+ edge_material = ns3d->get_debug_navigation_avoidance_static_obstacle_pushin_edge_material();
} else {
- face_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_material();
- edge_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_material();
+ edge_material = ns3d->get_debug_navigation_avoidance_static_obstacle_pushout_edge_material();
}
- static_obstacle_debug_mesh->surface_set_material(0, face_material);
- static_obstacle_debug_mesh->surface_set_material(1, edge_material);
+ rs->instance_set_surface_override_material(static_obstacle_debug_instance_rid, 0, edge_material->get_rid());
- RS::get_singleton()->instance_set_base(static_obstacle_debug_instance, static_obstacle_debug_mesh->get_rid());
if (is_inside_tree()) {
- RS::get_singleton()->instance_set_scenario(static_obstacle_debug_instance, get_world_3d()->get_scenario());
- RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, is_visible_in_tree());
+ rs->instance_set_scenario(static_obstacle_debug_instance_rid, get_world_3d()->get_scenario());
+ rs->instance_set_visible(static_obstacle_debug_instance_rid, is_visible_in_tree());
}
}
#endif // DEBUG_ENABLED
diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h
index e9a4669fa2..99a5770f02 100644
--- a/scene/3d/navigation_obstacle_3d.h
+++ b/scene/3d/navigation_obstacle_3d.h
@@ -51,8 +51,6 @@ class NavigationObstacle3D : public Node3D {
bool use_3d_avoidance = false;
- Transform3D previous_transform;
-
Vector3 velocity;
Vector3 previous_velocity;
bool velocity_submitted = false;
@@ -61,13 +59,14 @@ class NavigationObstacle3D : public Node3D {
bool carve_navigation_mesh = false;
#ifdef DEBUG_ENABLED
- RID fake_agent_radius_debug_instance;
- Ref<ArrayMesh> fake_agent_radius_debug_mesh;
+ RID fake_agent_radius_debug_instance_rid;
+ RID fake_agent_radius_debug_mesh_rid;
- RID static_obstacle_debug_instance;
- Ref<ArrayMesh> static_obstacle_debug_mesh;
+ RID static_obstacle_debug_instance_rid;
+ RID static_obstacle_debug_mesh_rid;
private:
+ void _update_debug();
void _update_fake_agent_radius_debug();
void _update_static_obstacle_debug();
#endif // DEBUG_ENABLED
@@ -95,7 +94,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 +106,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 +116,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/rigid_body_3d.cpp b/scene/3d/physics/rigid_body_3d.cpp
index 275e8cd7a9..6c8cea1ac3 100644
--- a/scene/3d/physics/rigid_body_3d.cpp
+++ b/scene/3d/physics/rigid_body_3d.cpp
@@ -659,7 +659,7 @@ void RigidBody3D::_reload_physics_characteristics() {
}
PackedStringArray RigidBody3D::get_configuration_warnings() const {
- PackedStringArray warnings = CollisionObject3D::get_configuration_warnings();
+ PackedStringArray warnings = PhysicsBody3D::get_configuration_warnings();
Vector3 scale = get_transform().get_basis().get_scale();
if (ABS(scale.x - 1.0) > 0.05 || ABS(scale.y - 1.0) > 0.05 || ABS(scale.z - 1.0) > 0.05) {
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..b51d8a3438 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);
}
}
@@ -1032,6 +1032,10 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
return skin_ref;
}
+void Skeleton3D::force_update_deferred() {
+ _make_dirty();
+}
+
void Skeleton3D::force_update_all_dirty_bones() {
if (!dirty) {
return;
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 0db12600c3..6c51c172af 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -31,6 +31,7 @@
#ifndef SKELETON_3D_H
#define SKELETON_3D_H
+#include "core/templates/a_hash_map.h"
#include "scene/3d/node_3d.h"
#include "scene/resources/3d/skin.h"
@@ -159,9 +160,9 @@ private:
bool process_order_dirty = false;
Vector<int> parentless_bones;
- HashMap<String, int> name_to_bone_index;
+ 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();
@@ -283,6 +284,7 @@ public:
void force_update_all_dirty_bones();
void force_update_all_bone_transforms();
void force_update_bone_children_transforms(int bone_idx);
+ void force_update_deferred();
void set_modifier_callback_mode_process(ModifierCallbackModeProcess p_mode);
ModifierCallbackModeProcess get_modifier_callback_mode_process() const;
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/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp
index d5c603112e..e8a8e517a2 100644
--- a/scene/3d/skeleton_modifier_3d.cpp
+++ b/scene/3d/skeleton_modifier_3d.cpp
@@ -75,6 +75,17 @@ void SkeletonModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new)
//
}
+void SkeletonModifier3D::_force_update_skeleton_skin() {
+ if (!is_inside_tree()) {
+ return;
+ }
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return;
+ }
+ skeleton->force_update_deferred();
+}
+
/* Process */
void SkeletonModifier3D::set_active(bool p_active) {
@@ -83,6 +94,7 @@ void SkeletonModifier3D::set_active(bool p_active) {
}
active = p_active;
_set_active(active);
+ _force_update_skeleton_skin();
}
bool SkeletonModifier3D::is_active() const {
@@ -119,6 +131,10 @@ void SkeletonModifier3D::_notification(int p_what) {
case NOTIFICATION_PARENTED: {
_update_skeleton();
} break;
+ case NOTIFICATION_EXIT_TREE:
+ case NOTIFICATION_UNPARENTED: {
+ _force_update_skeleton_skin();
+ } break;
}
}
diff --git a/scene/3d/skeleton_modifier_3d.h b/scene/3d/skeleton_modifier_3d.h
index d00a1e94a9..728b000ff5 100644
--- a/scene/3d/skeleton_modifier_3d.h
+++ b/scene/3d/skeleton_modifier_3d.h
@@ -50,6 +50,7 @@ protected:
void _update_skeleton();
void _update_skeleton_path();
+ void _force_update_skeleton_skin();
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new);
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_mixer.cpp b/scene/animation/animation_mixer.cpp
index eb8bc8c382..0fa6810d23 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -563,6 +563,7 @@ void AnimationMixer::_clear_caches() {
memdelete(K.value);
}
track_cache.clear();
+ animation_track_num_to_track_cashe.clear();
cache_valid = false;
capture_cache.clear();
@@ -922,6 +923,27 @@ bool AnimationMixer::_update_caches() {
idx++;
}
+ for (KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) {
+ K.value->blend_idx = track_map[K.value->path];
+ }
+
+ animation_track_num_to_track_cashe.clear();
+ LocalVector<TrackCache *> track_num_to_track_cashe;
+ for (const StringName &E : sname_list) {
+ Ref<Animation> anim = get_animation(E);
+ const Vector<Animation::Track *> tracks = anim->get_tracks();
+ track_num_to_track_cashe.resize(tracks.size());
+ for (int i = 0; i < tracks.size(); i++) {
+ TrackCache **track_ptr = track_cache.getptr(tracks[i]->thash);
+ if (track_ptr == nullptr) {
+ track_num_to_track_cashe[i] = nullptr;
+ } else {
+ track_num_to_track_cashe[i] = *track_ptr;
+ }
+ }
+ animation_track_num_to_track_cashe.insert(anim, track_num_to_track_cashe);
+ }
+
track_count = idx;
cache_valid = true;
@@ -946,7 +968,7 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) {
clear_animation_instances();
}
-Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) {
+Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant &p_value, ObjectID p_object_id, int p_object_sub_idx) {
#ifndef _3D_DISABLED
switch (p_anim->track_get_type(p_track)) {
case Animation::TYPE_POSITION_3D: {
@@ -1033,7 +1055,7 @@ void AnimationMixer::_blend_init() {
}
}
-bool AnimationMixer::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
+bool AnimationMixer::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
return true;
}
@@ -1084,26 +1106,30 @@ void AnimationMixer::_blend_calc_total_weight() {
real_t weight = ai.playback_info.weight;
const real_t *track_weights_ptr = ai.playback_info.track_weights.ptr();
int track_weights_count = ai.playback_info.track_weights.size();
- static LocalVector<Animation::TypeHash> processed_hashes;
+ ERR_CONTINUE_EDMSG(!animation_track_num_to_track_cashe.has(a), "No animation in cache.");
+ LocalVector<TrackCache *> &track_num_to_track_cashe = animation_track_num_to_track_cashe[a];
+ thread_local HashSet<Animation::TypeHash, HashHasher> processed_hashes;
processed_hashes.clear();
const Vector<Animation::Track *> tracks = a->get_tracks();
- for (const Animation::Track *animation_track : tracks) {
+ Animation::Track *const *tracks_ptr = tracks.ptr();
+ int count = tracks.size();
+ for (int i = 0; i < count; i++) {
+ Animation::Track *animation_track = tracks_ptr[i];
if (!animation_track->enabled) {
continue;
}
Animation::TypeHash thash = animation_track->thash;
- TrackCache **track_ptr = track_cache.getptr(thash);
- if (track_ptr == nullptr || processed_hashes.has(thash)) {
+ TrackCache *track = track_num_to_track_cashe[i];
+ if (track == nullptr || processed_hashes.has(thash)) {
// No path, but avoid error spamming.
// Or, there is the case different track type with same path; These can be distinguished by hash. So don't add the weight doubly.
continue;
}
- TrackCache *track = *track_ptr;
- int blend_idx = track_map[track->path];
+ int blend_idx = track->blend_idx;
ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count);
real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight;
track->total_weight += blend;
- processed_hashes.push_back(thash);
+ processed_hashes.insert(thash);
}
}
}
@@ -1130,6 +1156,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
#ifndef _3D_DISABLED
bool calc_root = !seeked || is_external_seeking;
#endif // _3D_DISABLED
+ ERR_CONTINUE_EDMSG(!animation_track_num_to_track_cashe.has(a), "No animation in cache.");
+ LocalVector<TrackCache *> &track_num_to_track_cashe = animation_track_num_to_track_cashe[a];
const Vector<Animation::Track *> tracks = a->get_tracks();
Animation::Track *const *tracks_ptr = tracks.ptr();
real_t a_length = a->get_length();
@@ -1139,15 +1167,11 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (!animation_track->enabled) {
continue;
}
- Animation::TypeHash thash = animation_track->thash;
- TrackCache **track_ptr = track_cache.getptr(thash);
- if (track_ptr == nullptr) {
+ TrackCache *track = track_num_to_track_cashe[i];
+ if (track == nullptr) {
continue; // No path, but avoid error spamming.
}
- TrackCache *track = *track_ptr;
- int *blend_idx_ptr = track_map.getptr(track->path);
- ERR_CONTINUE(blend_idx_ptr == nullptr);
- int blend_idx = *blend_idx_ptr;
+ int blend_idx = track->blend_idx;
ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count);
real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight;
if (!deterministic) {
@@ -1581,7 +1605,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
track_info.loop = a->get_loop_mode() != Animation::LOOP_NONE;
track_info.backward = backward;
track_info.use_blend = a->audio_track_is_use_blend(i);
- HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
+ AHashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
// Main process to fire key is started from here.
if (p_update_only) {
@@ -1850,7 +1874,7 @@ void AnimationMixer::_blend_apply() {
PlayingAudioTrackInfo &track_info = L.value;
float db = Math::linear_to_db(track_info.use_blend ? track_info.volume : 1.0);
LocalVector<int> erase_streams;
- HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
+ AHashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
for (const KeyValue<int, PlayingAudioStreamInfo> &M : map) {
PlayingAudioStreamInfo pasi = M.value;
@@ -2134,7 +2158,7 @@ void AnimationMixer::restore(const Ref<AnimatedValuesBackup> &p_backup) {
ERR_FAIL_COND(p_backup.is_null());
track_cache = p_backup->get_data();
_blend_apply();
- track_cache = HashMap<Animation::TypeHash, AnimationMixer::TrackCache *>();
+ track_cache = AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher>();
cache_valid = false;
}
@@ -2370,7 +2394,7 @@ AnimationMixer::AnimationMixer() {
AnimationMixer::~AnimationMixer() {
}
-void AnimatedValuesBackup::set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data) {
+void AnimatedValuesBackup::set_data(const AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> p_data) {
clear_data();
for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : p_data) {
@@ -2383,7 +2407,7 @@ void AnimatedValuesBackup::set_data(const HashMap<Animation::TypeHash, Animation
}
}
-HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() const {
+AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> AnimatedValuesBackup::get_data() const {
HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> ret;
for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : data) {
AnimationMixer::TrackCache *track = get_cache_copy(E.value);
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index 27c9a00a9c..1906146c56 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -31,6 +31,7 @@
#ifndef ANIMATION_MIXER_H
#define ANIMATION_MIXER_H
+#include "core/templates/a_hash_map.h"
#include "scene/animation/tween.h"
#include "scene/main/node.h"
#include "scene/resources/animation.h"
@@ -102,7 +103,7 @@ public:
protected:
/* ---- Data lists ---- */
LocalVector<AnimationLibraryData> animation_libraries;
- HashMap<StringName, AnimationData> animation_set; // HashMap<Library name + Animation name, AnimationData>
+ AHashMap<StringName, AnimationData> animation_set; // HashMap<Library name + Animation name, AnimationData>
TypedArray<StringName> _get_animation_library_list() const;
Vector<String> _get_animation_list() const {
@@ -148,6 +149,7 @@ protected:
uint64_t setup_pass = 0;
Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION;
NodePath path;
+ int blend_idx = -1;
ObjectID object_id;
real_t total_weight = 0.0;
@@ -269,7 +271,7 @@ protected:
// Audio track information for mixng and ending.
struct PlayingAudioTrackInfo {
- HashMap<int, PlayingAudioStreamInfo> stream_info;
+ AHashMap<int, PlayingAudioStreamInfo> stream_info;
double length = 0.0;
double time = 0.0;
real_t volume = 0.0;
@@ -308,7 +310,8 @@ protected:
};
RootMotionCache root_motion_cache;
- HashMap<Animation::TypeHash, TrackCache *> track_cache;
+ AHashMap<Animation::TypeHash, TrackCache *, HashHasher> track_cache;
+ AHashMap<Ref<Animation>, LocalVector<TrackCache *>> animation_track_num_to_track_cashe;
HashSet<TrackCache *> playing_caches;
Vector<Node *> playing_audio_stream_players;
@@ -324,7 +327,7 @@ protected:
/* ---- Blending processor ---- */
LocalVector<AnimationInstance> animation_instances;
- HashMap<NodePath, int> track_map;
+ AHashMap<NodePath, int> track_map;
int track_count = 0;
bool deterministic = false;
@@ -359,12 +362,12 @@ protected:
virtual void _process_animation(double p_delta, bool p_update_only = false);
// For post process with retrieved key value during blending.
- virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
+ virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant &p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int);
void _blend_init();
- virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map);
+ virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map);
virtual void _blend_capture(double p_delta);
void _blend_calc_total_weight(); // For undeterministic blending.
void _blend_process(double p_delta, bool p_update_only = false);
@@ -485,11 +488,11 @@ public:
class AnimatedValuesBackup : public RefCounted {
GDCLASS(AnimatedValuesBackup, RefCounted);
- HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> data;
+ AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> data;
public:
- void set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data);
- HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> get_data() const;
+ void set_data(const AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> p_data);
+ AHashMap<Animation::TypeHash, AnimationMixer::TrackCache *, HashHasher> get_data() const;
void clear_data();
AnimationMixer::TrackCache *get_cache_copy(AnimationMixer::TrackCache *p_cache) const;
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index c3c5399a6b..5cc204100c 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -1619,7 +1619,7 @@ AnimationNode::NodeTimeInfo AnimationNodeStateMachine::_process(const AnimationM
playback_new = playback_new->duplicate(); // Don't process original when testing.
}
- return playback_new->process(node_state.base_path, this, p_playback_info, p_test_only);
+ return playback_new->process(node_state.get_base_path(), this, p_playback_info, p_test_only);
}
String AnimationNodeStateMachine::get_caption() const {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 8a2ca47920..b3a75a75a0 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -133,7 +133,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> anim_names;
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
- HashMap<StringName, StringName>::ConstIterator F = animation_next_set.find(E.key);
+ AHashMap<StringName, StringName>::ConstIterator F = animation_next_set.find(E.key);
if (F && F->value != StringName()) {
anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
@@ -299,7 +299,7 @@ void AnimationPlayer::_blend_playback_data(double p_delta, bool p_started) {
}
}
-bool AnimationPlayer::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
+bool AnimationPlayer::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
if (!playback.current.from) {
_set_process(false);
return false;
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 3223e2522d..06b3eecb89 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -52,7 +52,7 @@ public:
#endif // DISABLE_DEPRECATED
private:
- HashMap<StringName, StringName> animation_next_set; // For auto advance.
+ AHashMap<StringName, StringName> animation_next_set; // For auto advance.
float speed_scale = 1.0;
double default_blend_time = 0.0;
@@ -138,7 +138,7 @@ protected:
static void _bind_methods();
// Make animation instances.
- virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) override;
+ virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) override;
virtual void _blend_capture(double p_delta) override;
virtual void _blend_post_process() override;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 19080e61de..d676e2acf4 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -75,20 +75,34 @@ void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_val
if (process_state->is_testing) {
return;
}
+
+ const AHashMap<StringName, int>::Iterator it = property_cache.find(p_name);
+ if (it) {
+ process_state->tree->property_map.get_by_index(it->value).value.first = p_value;
+ return;
+ }
+
ERR_FAIL_COND(!process_state->tree->property_parent_map.has(node_state.base_path));
ERR_FAIL_COND(!process_state->tree->property_parent_map[node_state.base_path].has(p_name));
StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name];
-
- process_state->tree->property_map[path].first = p_value;
+ int idx = process_state->tree->property_map.get_index(path);
+ property_cache.insert_new(p_name, idx);
+ process_state->tree->property_map.get_by_index(idx).value.first = p_value;
}
Variant AnimationNode::get_parameter(const StringName &p_name) const {
ERR_FAIL_NULL_V(process_state, Variant());
+ const AHashMap<StringName, int>::ConstIterator it = property_cache.find(p_name);
+ if (it) {
+ return process_state->tree->property_map.get_by_index(it->value).value.first;
+ }
ERR_FAIL_COND_V(!process_state->tree->property_parent_map.has(node_state.base_path), Variant());
ERR_FAIL_COND_V(!process_state->tree->property_parent_map[node_state.base_path].has(p_name), Variant());
StringName path = process_state->tree->property_parent_map[node_state.base_path][p_name];
- return process_state->tree->property_map[path].first;
+ int idx = process_state->tree->property_map.get_index(path);
+ property_cache.insert_new(p_name, idx);
+ return process_state->tree->property_map.get_by_index(idx).value.first;
}
void AnimationNode::set_node_time_info(const NodeTimeInfo &p_node_time_info) {
@@ -203,7 +217,7 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node
}
for (const KeyValue<NodePath, bool> &E : filter) {
- const HashMap<NodePath, int> &map = *process_state->track_map;
+ const AHashMap<NodePath, int> &map = *process_state->track_map;
if (!map.has(E.key)) {
continue;
}
@@ -292,7 +306,7 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node
// This process, which depends on p_sync is needed to process sync correctly in the case of
// that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync.
- p_node->node_state.base_path = new_path;
+ p_node->set_node_state_base_path(new_path);
p_node->node_state.parent = new_parent;
if (!p_playback_info.seeked && !p_sync && !any_valid) {
p_playback_info.delta = 0.0;
@@ -603,7 +617,7 @@ Ref<AnimationRootNode> AnimationTree::get_root_animation_node() const {
return root_animation_node;
}
-bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) {
+bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) {
_update_properties(); // If properties need updating, update them.
if (!root_animation_node.is_valid()) {
@@ -627,7 +641,7 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const
for (int i = 0; i < p_track_count; i++) {
src_blendsw[i] = 1.0; // By default all go to 1 for the root input.
}
- root_animation_node->node_state.base_path = SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data());
+ root_animation_node->set_node_state_base_path(SNAME(Animation::PARAMETERS_BASE_PATH.ascii().get_data()));
root_animation_node->node_state.parent = nullptr;
}
@@ -732,7 +746,7 @@ void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringN
void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> p_node) {
ERR_FAIL_COND(p_node.is_null());
if (!property_parent_map.has(p_base_path)) {
- property_parent_map[p_base_path] = HashMap<StringName, StringName>();
+ property_parent_map[p_base_path] = AHashMap<StringName, StringName>();
}
if (!property_reference_map.has(p_node->get_instance_id())) {
property_reference_map[p_node->get_instance_id()] = p_base_path;
@@ -767,7 +781,7 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<A
pinfo.name = p_base_path + key;
properties.push_back(pinfo);
}
-
+ p_node->make_cache_dirty();
List<AnimationNode::ChildNode> children;
p_node->get_child_nodes(&children);
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index d4b7bf31c9..eb25a8db1b 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -60,7 +60,7 @@ public:
bool closable = false;
Vector<Input> inputs;
- HashMap<NodePath, bool> filter;
+ AHashMap<NodePath, bool> filter;
bool filter_enabled = false;
// To propagate information from upstream for use in estimation of playback progress.
@@ -97,22 +97,57 @@ public:
// Temporary state for blending process which needs to be stored in each AnimationNodes.
struct NodeState {
+ friend AnimationNode;
+
+ private:
StringName base_path;
+
+ public:
AnimationNode *parent = nullptr;
Vector<StringName> connections;
Vector<real_t> track_weights;
+
+ const StringName get_base_path() const {
+ return base_path;
+ }
+
} node_state;
// Temporary state for blending process which needs to be started in the AnimationTree, pass through the AnimationNodes, and then return to the AnimationTree.
struct ProcessState {
AnimationTree *tree = nullptr;
- const HashMap<NodePath, int> *track_map; // TODO: Is there a better way to manage filter/tracks?
+ const AHashMap<NodePath, int> *track_map; // TODO: Is there a better way to manage filter/tracks?
bool is_testing = false;
bool valid = false;
String invalid_reasons;
uint64_t last_pass = 0;
} *process_state = nullptr;
+private:
+ mutable AHashMap<StringName, int> property_cache;
+
+public:
+ void set_node_state_base_path(const StringName p_base_path) {
+ if (p_base_path != node_state.base_path) {
+ node_state.base_path = p_base_path;
+ make_cache_dirty();
+ }
+ }
+
+ void set_node_state_base_path(const String p_base_path) {
+ if (p_base_path != node_state.base_path) {
+ node_state.base_path = p_base_path;
+ make_cache_dirty();
+ }
+ }
+
+ const StringName get_node_state_base_path() const {
+ return node_state.get_base_path();
+ }
+
+ void make_cache_dirty() {
+ property_cache.clear();
+ }
Array _get_filters() const;
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
@@ -151,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)
@@ -250,9 +285,9 @@ private:
friend class AnimationNode;
List<PropertyInfo> properties;
- HashMap<StringName, HashMap<StringName, StringName>> property_parent_map;
- HashMap<ObjectID, StringName> property_reference_map;
- HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
+ AHashMap<StringName, AHashMap<StringName, StringName>> property_parent_map;
+ AHashMap<ObjectID, StringName> property_reference_map;
+ AHashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
bool properties_dirty = true;
@@ -286,7 +321,7 @@ private:
virtual void _set_active(bool p_active) override;
// Make animation instances.
- virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map) override;
+ virtual bool _blend_pre_process(double p_delta, int p_track_count, const AHashMap<NodePath, int> &p_track_map) override;
#ifndef DISABLE_DEPRECATED
void _set_process_callback_bind_compat_80813(AnimationProcessCallback p_mode);
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 0a5f2ec6c7..3e593a8372 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -296,19 +296,12 @@ void Button::_notification(int p_what) {
}
} break;
case DRAW_HOVER_PRESSED: {
- // Edge case for CheckButton and CheckBox.
- if (has_theme_stylebox("hover_pressed")) {
- if (has_theme_color(SNAME("font_hover_pressed_color"))) {
- font_color = theme_cache.font_hover_pressed_color;
- }
- if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
- icon_modulate_color = theme_cache.icon_hover_pressed_color;
- }
-
- break;
+ font_color = theme_cache.font_hover_pressed_color;
+ if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
+ icon_modulate_color = theme_cache.icon_hover_pressed_color;
}
- }
- [[fallthrough]];
+
+ } break;
case DRAW_PRESSED: {
if (has_theme_color(SNAME("font_pressed_color"))) {
font_color = theme_cache.font_pressed_color;
@@ -568,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);
@@ -649,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;
}
@@ -673,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;
}
@@ -769,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);
@@ -840,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 e92f979c32..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);
}
}
@@ -1553,23 +1553,21 @@ void ColorPicker::_pick_button_pressed_legacy() {
picker_texture_rect = memnew(TextureRect);
picker_texture_rect->set_anchors_preset(Control::PRESET_FULL_RECT);
+ picker_texture_rect->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
picker_window->add_child(picker_texture_rect);
picker_texture_rect->set_default_cursor_shape(CURSOR_POINTING_HAND);
picker_texture_rect->connect(SceneStringName(gui_input), callable_mp(this, &ColorPicker::_picker_texture_input));
- picker_preview = memnew(Panel);
- picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
- picker_preview->set_mouse_filter(MOUSE_FILTER_IGNORE);
- picker_window->add_child(picker_preview);
-
picker_preview_label = memnew(Label);
- picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
+ picker_preview_label->set_anchors_preset(Control::PRESET_CENTER_TOP);
picker_preview_label->set_text(ETR("Color Picking active"));
- picker_preview->add_child(picker_preview_label);
- picker_preview_style_box = (Ref<StyleBoxFlat>)memnew(StyleBoxFlat);
+ picker_preview_style_box.instantiate();
picker_preview_style_box->set_bg_color(Color(1.0, 1.0, 1.0));
- picker_preview->add_theme_style_override(SceneStringName(panel), picker_preview_style_box);
+ picker_preview_style_box->set_content_margin_all(4.0);
+ picker_preview_label->add_theme_style_override(CoreStringName(normal), picker_preview_style_box);
+
+ picker_window->add_child(picker_preview_label);
}
Rect2i screen_rect;
@@ -1611,7 +1609,7 @@ void ColorPicker::_pick_button_pressed_legacy() {
}
picker_window->set_size(screen_rect.size);
- picker_preview->set_size(screen_rect.size / 10.0); // 10% of size in each axis.
+ picker_preview_label->set_custom_minimum_size(screen_rect.size / 10); // 10% of size in each axis.
picker_window->popup();
}
@@ -1634,7 +1632,7 @@ void ColorPicker::_picker_texture_input(const Ref<InputEvent> &p_event) {
Vector2 ofs = mev->get_position();
picker_color = img->get_pixel(ofs.x, ofs.y);
picker_preview_style_box->set_bg_color(picker_color);
- picker_preview_label->set_self_modulate(picker_color.get_luminance() < 0.5 ? Color(1.0f, 1.0f, 1.0f) : Color(0.0f, 0.0f, 0.0f));
+ picker_preview_label->add_theme_color_override(SceneStringName(font_color), picker_color.get_luminance() < 0.5 ? Color(1.0f, 1.0f, 1.0f) : Color(0.0f, 0.0f, 0.0f));
}
}
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index ad028584b1..59540d9ace 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -130,7 +130,6 @@ private:
Popup *picker_window = nullptr;
// Legacy color picking.
TextureRect *picker_texture_rect = nullptr;
- Panel *picker_preview = nullptr;
Label *picker_preview_label = nullptr;
Ref<StyleBoxFlat> picker_preview_style_box;
Color picker_color;
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/spin_box.cpp b/scene/gui/spin_box.cpp
index 01c2b9bffe..e56a9e71ef 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -41,7 +41,11 @@ Size2 SpinBox::get_minimum_size() const {
}
void SpinBox::_update_text(bool p_keep_line_edit) {
- String value = String::num(get_value(), Math::range_step_decimals(get_step()));
+ double step = get_step();
+ if (use_custom_arrow_step && custom_arrow_step != 0.0) {
+ step = custom_arrow_step;
+ }
+ String value = String::num(get_value(), Math::range_step_decimals(step));
if (is_localizing_numeral_system()) {
value = TS->format_number(value);
}
@@ -75,6 +79,9 @@ void SpinBox::_text_submitted(const String &p_string) {
text = text.trim_prefix(prefix + " ").trim_suffix(" " + suffix);
Error err = expr->parse(text);
+
+ use_custom_arrow_step = false;
+
if (err != OK) {
// If the expression failed try without converting commas to dots - they might have been for parameter separation.
text = p_string;
@@ -114,8 +121,13 @@ void SpinBox::_line_edit_input(const Ref<InputEvent> &p_event) {
void SpinBox::_range_click_timeout() {
if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
bool up = get_local_mouse_position().y < (get_size().height / 2);
- double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
- set_value(get_value() + (up ? step : -step));
+ double step = get_step();
+ // Arrow button is being pressed, so we also need to set the step to the same value as custom_arrow_step if its not 0.
+ double temp_step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
+ _set_step_no_signal(temp_step);
+ set_value(get_value() + (up ? temp_step : -temp_step));
+ _set_step_no_signal(step);
+ use_custom_arrow_step = true;
if (range_click_timer->is_one_shot()) {
range_click_timer->set_wait_time(0.075);
@@ -156,8 +168,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
Ref<InputEventMouseMotion> mm = p_event;
- double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
-
+ double step = get_step();
Vector2 mpos;
bool mouse_on_up_button = false;
bool mouse_on_down_button = false;
@@ -177,7 +188,12 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
line_edit->grab_focus();
if (mouse_on_up_button || mouse_on_down_button) {
- set_value(get_value() + (mouse_on_up_button ? step : -step));
+ // Arrow button is being pressed, so step is being changed temporarily.
+ double temp_step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
+ _set_step_no_signal(temp_step);
+ set_value(get_value() + (mouse_on_up_button ? temp_step : -temp_step));
+ _set_step_no_signal(step);
+ use_custom_arrow_step = true;
}
state_cache.up_button_pressed = mouse_on_up_button;
state_cache.down_button_pressed = mouse_on_down_button;
@@ -193,17 +209,20 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
case MouseButton::RIGHT: {
line_edit->grab_focus();
if (mouse_on_up_button || mouse_on_down_button) {
+ use_custom_arrow_step = false;
set_value(mouse_on_up_button ? get_max() : get_min());
}
} break;
case MouseButton::WHEEL_UP: {
if (line_edit->is_editing()) {
+ use_custom_arrow_step = false;
set_value(get_value() + step * mb->get_factor());
accept_event();
}
} break;
case MouseButton::WHEEL_DOWN: {
if (line_edit->is_editing()) {
+ use_custom_arrow_step = false;
set_value(get_value() - step * mb->get_factor());
accept_event();
}
@@ -243,6 +262,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
if (drag.enabled) {
drag.diff_y += mm->get_relative().y;
double diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8) * SIGN(drag.diff_y);
+ use_custom_arrow_step = false;
set_value(CLAMP(drag.base_val + step * diff_y, get_min(), get_max()));
} else if (drag.allowed && drag.capture_pos.distance_to(mm->get_position()) > 2) {
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
@@ -519,6 +539,12 @@ void SpinBox::_update_buttons_state_for_current_value() {
}
}
+void SpinBox::_set_step_no_signal(double p_step) {
+ set_block_signals(true);
+ set_step(p_step);
+ set_block_signals(false);
+}
+
void SpinBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &SpinBox::set_horizontal_alignment);
ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &SpinBox::get_horizontal_alignment);
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 294dc3e5d5..06a0f622b9 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -66,6 +66,7 @@ class SpinBox : public Range {
String suffix;
String last_updated_text;
double custom_arrow_step = 0.0;
+ bool use_custom_arrow_step = false;
void _line_edit_input(const Ref<InputEvent> &p_event);
@@ -133,6 +134,7 @@ class SpinBox : public Range {
void _mouse_exited();
void _update_buttons_state_for_current_value();
+ void _set_step_no_signal(double p_step);
protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
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 6159e26efa..f6e05f5796 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -961,19 +961,17 @@ TreeItem *TreeItem::_get_prev_in_tree(bool p_wrap, bool p_include_invisible) {
if (!prev_item) {
current = current->parent;
- if (current == tree->root && tree->hide_root) {
- return nullptr;
- } else if (!current) {
- if (p_wrap) {
- current = this;
- TreeItem *temp = get_next_visible();
- while (temp) {
- current = temp;
- temp = temp->get_next_visible();
- }
- } else {
+ if (!current || (current == tree->root && tree->hide_root)) {
+ if (!p_wrap) {
return nullptr;
}
+ // Wrap around to the last visible item.
+ current = this;
+ TreeItem *temp = get_next_visible();
+ while (temp) {
+ current = temp;
+ temp = temp->get_next_visible();
+ }
}
} else {
current = prev_item;
@@ -1101,7 +1099,7 @@ void TreeItem::clear_children() {
first_child = nullptr;
last_child = nullptr;
children_cache.clear();
-};
+}
int TreeItem::get_index() {
int idx = 0;
@@ -1576,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);
@@ -1595,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);
@@ -1607,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);
}
@@ -1891,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) {
@@ -4704,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) {
@@ -4829,7 +4794,7 @@ void Tree::clear() {
_determine_hovered_item();
queue_redraw();
-};
+}
void Tree::set_hide_root(bool p_enabled) {
if (hide_root == p_enabled) {
@@ -5364,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 c0386b056f..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);
@@ -1253,7 +1255,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_update_transform"), &CanvasItem::force_update_transform);
- ClassDB::bind_method(D_METHOD("make_canvas_position_local", "screen_point"), &CanvasItem::make_canvas_position_local);
+ ClassDB::bind_method(D_METHOD("make_canvas_position_local", "viewport_point"), &CanvasItem::make_canvas_position_local);
ClassDB::bind_method(D_METHOD("make_input_local", "event"), &CanvasItem::make_input_local);
ClassDB::bind_method(D_METHOD("set_visibility_layer", "layer"), &CanvasItem::set_visibility_layer);
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/missing_node.cpp b/scene/main/missing_node.cpp
index 83672ae5e0..d5a183eab1 100644
--- a/scene/main/missing_node.cpp
+++ b/scene/main/missing_node.cpp
@@ -84,17 +84,17 @@ bool MissingNode::is_recording_properties() const {
PackedStringArray MissingNode::get_configuration_warnings() const {
// The mere existence of this node is warning.
- PackedStringArray ret;
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!original_scene.is_empty()) {
- ret.push_back(vformat(RTR("This node was an instance of scene '%s', which was no longer available when this scene was loaded."), original_scene));
- ret.push_back(vformat(RTR("Saving current scene will discard instance and all its properties, including editable children edits (if existing).")));
+ warnings.push_back(vformat(RTR("This node was an instance of scene '%s', which was no longer available when this scene was loaded."), original_scene));
+ warnings.push_back(vformat(RTR("Saving current scene will discard instance and all its properties, including editable children edits (if existing).")));
} else if (!original_class.is_empty()) {
- ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class));
- ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss."));
+ warnings.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class));
+ warnings.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss."));
} else {
- ret.push_back(RTR("Unrecognized missing node. Check scene dependency errors for details."));
+ warnings.push_back(RTR("Unrecognized missing node. Check scene dependency errors for details."));
}
- return ret;
+ return warnings;
}
void MissingNode::_bind_methods() {
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 0cdb23618f..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);
@@ -1921,7 +1921,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(mpos));
tooltip = tooltip.strip_edges();
- if (tooltip.is_empty() || tooltip != gui.tooltip_text) {
+ if (tooltip != gui.tooltip_text) {
_gui_cancel_tooltip();
} else {
is_tooltip_shown = 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/property_utils.cpp b/scene/property_utils.cpp
index 94a037bd9b..f068e34beb 100644
--- a/scene/property_utils.cpp
+++ b/scene/property_utils.cpp
@@ -89,6 +89,16 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const
*r_is_valid = false;
}
+ // Handle special case "script" property, where the default value is either null or the custom type script.
+ // Do this only if there's no states stack cache to trace for default values.
+ if (!p_states_stack_cache && p_property == CoreStringName(script) && p_object->has_meta(SceneStringName(_custom_type_script))) {
+ Ref<Script> ct_scr = p_object->get_meta(SceneStringName(_custom_type_script));
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return ct_scr;
+ }
+
Ref<Script> topmost_script;
if (const Node *node = Object::cast_to<Node>(p_object)) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 6b1ce2b4ca..8a048e9cc3 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -243,6 +243,7 @@
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/3d/lightmap_probe.h"
+#include "scene/3d/look_at_modifier_3d.h"
#include "scene/3d/marker_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/multimesh_instance_3d.h"
@@ -512,6 +513,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);
@@ -608,6 +612,7 @@ void register_scene_types() {
GDREGISTER_CLASS(SkeletonIK3D);
GDREGISTER_CLASS(BoneAttachment3D);
+ GDREGISTER_CLASS(LookAtModifier3D);
GDREGISTER_CLASS(VehicleBody3D);
GDREGISTER_CLASS(VehicleWheel3D);
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 229e18be23..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);
}
}
@@ -6480,9 +6514,9 @@ int TileData::get_terrain_set() const {
}
void TileData::set_terrain(int p_terrain) {
- ERR_FAIL_COND(terrain_set < 0);
ERR_FAIL_COND(p_terrain < -1);
- if (tile_set) {
+ ERR_FAIL_COND(terrain_set < 0 && p_terrain != -1);
+ if (tile_set && terrain_set >= 0) {
ERR_FAIL_COND(p_terrain >= tile_set->get_terrains_count(terrain_set));
}
terrain = p_terrain;
@@ -6495,9 +6529,9 @@ int TileData::get_terrain() const {
void TileData::set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) {
ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX);
- ERR_FAIL_COND(terrain_set < 0);
ERR_FAIL_COND(p_terrain_index < -1);
- if (tile_set) {
+ ERR_FAIL_COND(terrain_set < 0 && p_terrain_index != -1);
+ if (tile_set && terrain_set >= 0) {
ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set));
ERR_FAIL_COND(!is_valid_terrain_peering_bit(p_peering_bit));
}
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/curve.cpp b/scene/resources/curve.cpp
index 8926eb1d51..765373ce99 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -479,6 +479,9 @@ void Curve::set_bake_resolution(int p_resolution) {
}
real_t Curve::sample_baked(real_t p_offset) const {
+ // Make sure that p_offset is finite.
+ ERR_FAIL_COND_V_MSG(!Math::is_finite(p_offset), 0, "Offset is non-finite");
+
if (_baked_cache_dirty) {
// Last-second bake if not done already
const_cast<Curve *>(this)->bake();
@@ -981,6 +984,9 @@ Transform2D Curve2D::_sample_posture(Interval p_interval) const {
}
Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const {
+ // Make sure that p_offset is finite.
+ ERR_FAIL_COND_V_MSG(!Math::is_finite(p_offset), Vector2(), "Offset is non-finite");
+
if (baked_cache_dirty) {
_bake();
}
@@ -1000,6 +1006,9 @@ Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const {
}
Transform2D Curve2D::sample_baked_with_rotation(real_t p_offset, bool p_cubic) const {
+ // Make sure that p_offset is finite.
+ ERR_FAIL_COND_V_MSG(!Math::is_finite(p_offset), Transform2D(), "Offset is non-finite");
+
if (baked_cache_dirty) {
_bake();
}
@@ -1881,6 +1890,9 @@ Basis Curve3D::get_point_baked_posture(int p_index, bool p_apply_tilt) const {
#endif
Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const {
+ // Make sure that p_offset is finite.
+ ERR_FAIL_COND_V_MSG(!Math::is_finite(p_offset), Vector3(), "Offset is non-finite");
+
if (baked_cache_dirty) {
_bake();
}
@@ -1900,6 +1912,9 @@ Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const {
}
Transform3D Curve3D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_apply_tilt) const {
+ // Make sure that p_offset is finite.
+ ERR_FAIL_COND_V_MSG(!Math::is_finite(p_offset), Transform3D(), "Offset is non-finite");
+
if (baked_cache_dirty) {
_bake();
}
@@ -1929,6 +1944,9 @@ Transform3D Curve3D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, b
}
real_t Curve3D::sample_baked_tilt(real_t p_offset) const {
+ // Make sure that p_offset is finite.
+ ERR_FAIL_COND_V_MSG(!Math::is_finite(p_offset), 0, "Offset is non-finite");
+
if (baked_cache_dirty) {
_bake();
}
@@ -1948,6 +1966,9 @@ real_t Curve3D::sample_baked_tilt(real_t p_offset) const {
}
Vector3 Curve3D::sample_baked_up_vector(real_t p_offset, bool p_apply_tilt) const {
+ // Make sure that p_offset is finite.
+ ERR_FAIL_COND_V_MSG(!Math::is_finite(p_offset), Vector3(0, 1, 0), "Offset is non-finite");
+
if (baked_cache_dirty) {
_bake();
}
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/external_texture.cpp b/scene/resources/external_texture.cpp
index 0552bbd081..c8b714372a 100644
--- a/scene/resources/external_texture.cpp
+++ b/scene/resources/external_texture.cpp
@@ -39,12 +39,14 @@ void ExternalTexture::_bind_methods() {
}
uint64_t ExternalTexture::get_external_texture_id() const {
+ _ensure_created();
return RenderingServer::get_singleton()->texture_get_native_handle(texture);
}
void ExternalTexture::set_size(const Size2 &p_size) {
if (p_size.width > 0 && p_size.height > 0 && p_size != size) {
size = p_size;
+ _ensure_created();
RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer);
emit_changed();
}
@@ -57,6 +59,7 @@ Size2 ExternalTexture::get_size() const {
void ExternalTexture::set_external_buffer_id(uint64_t p_external_buffer) {
if (p_external_buffer != external_buffer) {
external_buffer = p_external_buffer;
+ _ensure_created();
RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer);
}
}
@@ -74,11 +77,29 @@ bool ExternalTexture::has_alpha() const {
}
RID ExternalTexture::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ using_placeholder = true;
+ }
return texture;
}
+void ExternalTexture::_ensure_created() const {
+ if (texture.is_valid() && !using_placeholder) {
+ return;
+ }
+
+ RID new_texture = RenderingServer::get_singleton()->texture_external_create(size.width, size.height);
+ if (using_placeholder) {
+ DEV_ASSERT(texture.is_valid());
+ RenderingServer::get_singleton()->texture_replace(texture, new_texture);
+ using_placeholder = false;
+ } else {
+ texture = new_texture;
+ }
+}
+
ExternalTexture::ExternalTexture() {
- texture = RenderingServer::get_singleton()->texture_external_create(size.width, size.height);
}
ExternalTexture::~ExternalTexture() {
diff --git a/scene/resources/external_texture.h b/scene/resources/external_texture.h
index 96bcd8d0fe..cd60bcc030 100644
--- a/scene/resources/external_texture.h
+++ b/scene/resources/external_texture.h
@@ -38,10 +38,13 @@ class ExternalTexture : public Texture2D {
GDCLASS(ExternalTexture, Texture2D);
private:
- RID texture;
+ mutable RID texture;
+ mutable bool using_placeholder = false;
Size2 size = Size2(256, 256);
uint64_t external_buffer = 0;
+ void _ensure_created() const;
+
protected:
static void _bind_methods();
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/style_box_flat.cpp b/scene/resources/style_box_flat.cpp
index 15816925c1..202ab3615b 100644
--- a/scene/resources/style_box_flat.cpp
+++ b/scene/resources/style_box_flat.cpp
@@ -596,10 +596,10 @@ void StyleBoxFlat::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew");
ADD_GROUP("Border Width", "border_width_");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM);
ADD_GROUP("Border", "border_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color");
@@ -607,18 +607,18 @@ void StyleBoxFlat::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "border_blend"), "set_border_blend", "get_border_blend");
ADD_GROUP("Corner Radius", "corner_radius_");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT);
ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail");
ADD_GROUP("Expand Margins", "expand_margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
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
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 140e588291..31daeb3ae3 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -130,6 +130,8 @@ SceneStringNames::SceneStringNames() {
shader_overrides_group = StaticCString::create("_shader_overrides_group_");
shader_overrides_group_active = StaticCString::create("_shader_overrides_group_active_");
+ _custom_type_script = StaticCString::create("_custom_type_script");
+
pressed = StaticCString::create("pressed");
id_pressed = StaticCString::create("id_pressed");
toggled = StaticCString::create("toggled");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index fc22be33b2..0a2ebeda7a 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -143,6 +143,8 @@ public:
StringName shader_overrides_group;
StringName shader_overrides_group_active;
+ StringName _custom_type_script;
+
StringName pressed;
StringName id_pressed;
StringName toggled;