summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite_2d.h2
-rw-r--r--scene/2d/area_2d.cpp8
-rw-r--r--scene/2d/audio_listener_2d.h6
-rw-r--r--scene/2d/audio_stream_player_2d.h2
-rw-r--r--scene/2d/back_buffer_copy.h6
-rw-r--r--scene/2d/camera_2d.cpp4
-rw-r--r--scene/2d/canvas_group.h6
-rw-r--r--scene/2d/canvas_modulate.h6
-rw-r--r--scene/2d/gpu_particles_2d.h6
-rw-r--r--scene/2d/light_occluder_2d.h6
-rw-r--r--scene/2d/line_2d.h6
-rw-r--r--scene/2d/navigation_agent_2d.h2
-rw-r--r--scene/2d/navigation_obstacle_2d.h2
-rw-r--r--scene/2d/navigation_region_2d.cpp30
-rw-r--r--scene/2d/node_2d.h6
-rw-r--r--scene/2d/physics_body_2d.cpp4
-rw-r--r--scene/2d/ray_cast_2d.cpp2
-rw-r--r--scene/2d/shape_cast_2d.cpp2
-rw-r--r--scene/2d/shape_cast_2d.h6
-rw-r--r--scene/2d/sprite_2d.h2
-rw-r--r--scene/2d/tile_map.cpp3
-rw-r--r--scene/2d/touch_screen_button.cpp4
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.h2
-rw-r--r--scene/3d/area_3d.cpp8
-rw-r--r--scene/3d/audio_listener_3d.h6
-rw-r--r--scene/3d/audio_stream_player_3d.h1
-rw-r--r--scene/3d/bone_attachment_3d.h6
-rw-r--r--scene/3d/camera_3d.cpp16
-rw-r--r--scene/3d/camera_3d.h12
-rw-r--r--scene/3d/collision_object_3d.cpp5
-rw-r--r--scene/3d/collision_object_3d.h2
-rw-r--r--scene/3d/collision_polygon_3d.h2
-rw-r--r--scene/3d/collision_shape_3d.h6
-rw-r--r--scene/3d/cpu_particles_3d.h6
-rw-r--r--scene/3d/decal.cpp9
-rw-r--r--scene/3d/decal.h4
-rw-r--r--scene/3d/gpu_particles_3d.cpp2
-rw-r--r--scene/3d/gpu_particles_3d.h6
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp12
-rw-r--r--scene/3d/importer_mesh_instance_3d.h7
-rw-r--r--scene/3d/joint_3d.cpp116
-rw-r--r--scene/3d/light_3d.cpp5
-rw-r--r--scene/3d/lightmap_gi.h2
-rw-r--r--scene/3d/mesh_instance_3d.h6
-rw-r--r--scene/3d/multimesh_instance_3d.h2
-rw-r--r--scene/3d/navigation_agent_3d.h6
-rw-r--r--scene/3d/navigation_obstacle_3d.h6
-rw-r--r--scene/3d/navigation_region_3d.cpp339
-rw-r--r--scene/3d/navigation_region_3d.h19
-rw-r--r--scene/3d/node_3d.cpp4
-rw-r--r--scene/3d/occluder_instance_3d.h2
-rw-r--r--scene/3d/path_3d.cpp2
-rw-r--r--scene/3d/path_3d.h6
-rw-r--r--scene/3d/physics_body_3d.cpp4
-rw-r--r--scene/3d/physics_body_3d.h2
-rw-r--r--scene/3d/ray_cast_3d.h2
-rw-r--r--scene/3d/reflection_probe.h6
-rw-r--r--scene/3d/remote_transform_3d.h6
-rw-r--r--scene/3d/shape_cast_3d.cpp630
-rw-r--r--scene/3d/shape_cast_3d.h142
-rw-r--r--scene/3d/skeleton_3d.cpp22
-rw-r--r--scene/3d/skeleton_3d.h6
-rw-r--r--scene/3d/skeleton_ik_3d.h6
-rw-r--r--scene/3d/soft_dynamic_body_3d.h6
-rw-r--r--scene/3d/spring_arm_3d.h6
-rw-r--r--scene/3d/vehicle_body_3d.h6
-rw-r--r--scene/3d/velocity_tracker_3d.h6
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.h2
-rw-r--r--scene/3d/visual_instance_3d.cpp9
-rw-r--r--scene/3d/visual_instance_3d.h6
-rw-r--r--scene/3d/voxelizer.h6
-rw-r--r--scene/3d/world_environment.h6
-rw-r--r--scene/3d/xr_nodes.cpp8
-rw-r--r--scene/SCsub1
-rw-r--r--scene/animation/animation_blend_space_1d.cpp4
-rw-r--r--scene/animation/animation_blend_space_2d.cpp4
-rw-r--r--scene/animation/animation_blend_tree.cpp6
-rw-r--r--scene/animation/animation_node_state_machine.cpp6
-rw-r--r--scene/animation/animation_player.cpp79
-rw-r--r--scene/animation/animation_player.h11
-rw-r--r--scene/animation/animation_tree.cpp44
-rw-r--r--scene/animation/animation_tree.h8
-rw-r--r--scene/animation/easing_equations.h2
-rw-r--r--scene/animation/tween.cpp15
-rw-r--r--scene/animation/tween.h2
-rw-r--r--scene/debugger/scene_debugger.h2
-rw-r--r--scene/gui/base_button.h2
-rw-r--r--scene/gui/button.h2
-rw-r--r--scene/gui/code_edit.h6
-rw-r--r--scene/gui/color_picker.cpp24
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/control.cpp2897
-rw-r--r--scene/gui/control.h325
-rw-r--r--scene/gui/dialogs.cpp2
-rw-r--r--scene/gui/dialogs.h2
-rw-r--r--scene/gui/file_dialog.cpp6
-rw-r--r--scene/gui/file_dialog.h2
-rw-r--r--scene/gui/graph_edit.cpp6
-rw-r--r--scene/gui/graph_edit.h2
-rw-r--r--scene/gui/item_list.h6
-rw-r--r--scene/gui/label.cpp2
-rw-r--r--scene/gui/label.h2
-rw-r--r--scene/gui/line_edit.h2
-rw-r--r--scene/gui/link_button.h6
-rw-r--r--scene/gui/menu_button.cpp4
-rw-r--r--scene/gui/menu_button.h2
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/option_button.h2
-rw-r--r--scene/gui/popup.h2
-rw-r--r--scene/gui/popup_menu.h2
-rw-r--r--scene/gui/range.h2
-rw-r--r--scene/gui/rich_text_label.cpp199
-rw-r--r--scene/gui/rich_text_label.h7
-rw-r--r--scene/gui/scroll_bar.cpp4
-rw-r--r--scene/gui/scroll_bar.h2
-rw-r--r--scene/gui/scroll_container.h2
-rw-r--r--scene/gui/separator.h2
-rw-r--r--scene/gui/spin_box.cpp6
-rw-r--r--scene/gui/subviewport_container.h6
-rw-r--r--scene/gui/text_edit.cpp10
-rw-r--r--scene/gui/texture_button.h1
-rw-r--r--scene/gui/tree.cpp24
-rw-r--r--scene/gui/tree.h3
-rw-r--r--scene/gui/view_panner.cpp2
-rw-r--r--scene/main/canvas_item.cpp12
-rw-r--r--scene/main/canvas_item.h1
-rw-r--r--scene/main/http_request.h6
-rw-r--r--scene/main/multiplayer_api.cpp416
-rw-r--r--scene/main/multiplayer_api.h115
-rw-r--r--scene/main/multiplayer_peer.cpp303
-rw-r--r--scene/main/multiplayer_peer.h157
-rw-r--r--scene/main/node.cpp88
-rw-r--r--scene/main/node.h32
-rw-r--r--scene/main/scene_tree.cpp18
-rw-r--r--scene/main/shader_globals_override.cpp22
-rw-r--r--scene/main/viewport.cpp36
-rw-r--r--scene/main/viewport.h8
-rw-r--r--scene/main/window.cpp2
-rw-r--r--scene/multiplayer/SCsub5
-rw-r--r--scene/multiplayer/multiplayer_spawner.cpp302
-rw-r--r--scene/multiplayer/multiplayer_spawner.h117
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.cpp304
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.h95
-rw-r--r--scene/multiplayer/scene_cache_interface.cpp274
-rw-r--r--scene/multiplayer/scene_cache_interface.h83
-rw-r--r--scene/multiplayer/scene_replication_interface.cpp533
-rw-r--r--scene/multiplayer/scene_replication_interface.h87
-rw-r--r--scene/multiplayer/scene_replication_state.cpp267
-rw-r--r--scene/multiplayer/scene_replication_state.h132
-rw-r--r--scene/multiplayer/scene_rpc_interface.cpp509
-rw-r--r--scene/multiplayer/scene_rpc_interface.h91
-rw-r--r--scene/register_scene_types.cpp29
-rw-r--r--scene/register_scene_types.h2
-rw-r--r--scene/resources/animation.cpp4
-rw-r--r--scene/resources/animation.h2
-rw-r--r--scene/resources/animation_library.h2
-rw-r--r--scene/resources/audio_stream_wav.cpp (renamed from scene/resources/audio_stream_sample.cpp)168
-rw-r--r--scene/resources/audio_stream_wav.h (renamed from scene/resources/audio_stream_sample.h)34
-rw-r--r--scene/resources/box_shape_3d.h6
-rw-r--r--scene/resources/capsule_shape_3d.h6
-rw-r--r--scene/resources/concave_polygon_shape_2d.h2
-rw-r--r--scene/resources/concave_polygon_shape_3d.h2
-rw-r--r--scene/resources/convex_polygon_shape_3d.h6
-rw-r--r--scene/resources/cylinder_shape_3d.h2
-rw-r--r--scene/resources/default_theme/default_theme.h2
-rw-r--r--scene/resources/environment.cpp2
-rw-r--r--scene/resources/font.cpp387
-rw-r--r--scene/resources/font.h74
-rw-r--r--scene/resources/height_map_shape_3d.h6
-rw-r--r--scene/resources/immediate_mesh.h2
-rw-r--r--scene/resources/importer_mesh.h7
-rw-r--r--scene/resources/label_settings.cpp2
-rw-r--r--scene/resources/material.cpp93
-rw-r--r--scene/resources/material.h13
-rw-r--r--scene/resources/mesh.cpp66
-rw-r--r--scene/resources/mesh.h5
-rw-r--r--scene/resources/multimesh.h2
-rw-r--r--scene/resources/navigation_mesh.cpp101
-rw-r--r--scene/resources/navigation_mesh.h4
-rw-r--r--scene/resources/packed_scene.cpp8
-rw-r--r--scene/resources/packed_scene.h2
-rw-r--r--scene/resources/primitive_meshes.h3
-rw-r--r--scene/resources/scene_replication_config.cpp205
-rw-r--r--scene/resources/scene_replication_config.h91
-rw-r--r--scene/resources/separation_ray_shape_2d.cpp2
-rw-r--r--scene/resources/separation_ray_shape_3d.h8
-rw-r--r--scene/resources/shader.cpp14
-rw-r--r--scene/resources/shader.h2
-rw-r--r--scene/resources/shape_3d.h2
-rw-r--r--scene/resources/skeleton_modification_2d.h6
-rw-r--r--scene/resources/skeleton_modification_2d_ccdik.h6
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.h6
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.h6
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.h6
-rw-r--r--scene/resources/skeleton_modification_2d_physicalbones.h6
-rw-r--r--scene/resources/skeleton_modification_2d_stackholder.h6
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.h6
-rw-r--r--scene/resources/skeleton_modification_3d.h6
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.h6
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h6
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.h6
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.h6
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.h6
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.h6
-rw-r--r--scene/resources/skeleton_modification_stack_2d.h6
-rw-r--r--scene/resources/skeleton_modification_stack_3d.h6
-rw-r--r--scene/resources/skeleton_profile.cpp44
-rw-r--r--scene/resources/skeleton_profile.h9
-rw-r--r--scene/resources/sky_material.h2
-rw-r--r--scene/resources/sphere_shape_3d.h2
-rw-r--r--scene/resources/style_box.h2
-rw-r--r--scene/resources/surface_tool.h2
-rw-r--r--scene/resources/syntax_highlighter.h2
-rw-r--r--scene/resources/text_file.h6
-rw-r--r--scene/resources/texture.cpp6
-rw-r--r--scene/resources/texture.h2
-rw-r--r--scene/resources/theme.cpp8
-rw-r--r--scene/resources/theme.h2
-rw-r--r--scene/resources/tile_set.cpp55
-rw-r--r--scene/resources/tile_set.h3
-rw-r--r--scene/resources/video_stream.h2
-rw-r--r--scene/resources/visual_shader.cpp8
-rw-r--r--scene/resources/visual_shader_particle_nodes.h2
-rw-r--r--scene/resources/world_boundary_shape_3d.h3
224 files changed, 5565 insertions, 5478 deletions
diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h
index 3a41f810dc..ec38795a1a 100644
--- a/scene/2d/animated_sprite_2d.h
+++ b/scene/2d/animated_sprite_2d.h
@@ -114,4 +114,4 @@ public:
AnimatedSprite2D();
};
-#endif // ANIMATED_SPRITE_H
+#endif // ANIMATED_SPRITE_2D_H
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index dfc1016c84..7890348314 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -184,8 +184,8 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i
E->value.rc = 0;
E->value.in_tree = node && node->is_inside_tree();
if (node) {
- node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree), make_binds(objid));
- node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree), make_binds(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree).bind(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree).bind(objid));
if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
}
@@ -277,8 +277,8 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i
E->value.rc = 0;
E->value.in_tree = node && node->is_inside_tree();
if (node) {
- node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree), make_binds(objid));
- node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree), make_binds(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree).bind(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree).bind(objid));
if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_entered, node);
}
diff --git a/scene/2d/audio_listener_2d.h b/scene/2d/audio_listener_2d.h
index 172d388efc..5cd1bfb251 100644
--- a/scene/2d/audio_listener_2d.h
+++ b/scene/2d/audio_listener_2d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LISTENER_2D_H
-#define LISTENER_2D_H
+#ifndef AUDIO_LISTENER_2D_H
+#define AUDIO_LISTENER_2D_H
#include "scene/2d/node_2d.h"
#include "scene/main/window.h"
@@ -56,4 +56,4 @@ public:
bool is_current() const;
};
-#endif
+#endif // AUDIO_LISTENER_2D_H
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index a22782fe44..d1c4dc4fdf 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -135,4 +135,4 @@ public:
~AudioStreamPlayer2D();
};
-#endif
+#endif // AUDIO_STREAM_PLAYER_2D_H
diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h
index 4e7cac1f3e..1f2d5810b0 100644
--- a/scene/2d/back_buffer_copy.h
+++ b/scene/2d/back_buffer_copy.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef BACKBUFFERCOPY_H
-#define BACKBUFFERCOPY_H
+#ifndef BACK_BUFFER_COPY_H
+#define BACK_BUFFER_COPY_H
#include "scene/2d/node_2d.h"
@@ -71,4 +71,4 @@ public:
VARIANT_ENUM_CAST(BackBufferCopy::CopyMode);
-#endif // BACKBUFFERCOPY_H
+#endif // BACK_BUFFER_COPY_H
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 76b354805c..c43a796170 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -247,8 +247,8 @@ void Camera2D::_notification(int p_what) {
add_to_group(canvas_group_name);
_update_process_callback();
- _update_scroll();
first = true;
+ _update_scroll();
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -439,7 +439,9 @@ void Camera2D::clear_current() {
void Camera2D::set_limit(Side p_side, int p_limit) {
ERR_FAIL_INDEX((int)p_side, 4);
limit[p_side] = p_limit;
+ Point2 old_smoothed_camera_pos = smoothed_camera_pos;
_update_scroll();
+ smoothed_camera_pos = old_smoothed_camera_pos;
}
int Camera2D::get_limit(Side p_side) const {
diff --git a/scene/2d/canvas_group.h b/scene/2d/canvas_group.h
index 9bc1772ee2..557e7e23dc 100644
--- a/scene/2d/canvas_group.h
+++ b/scene/2d/canvas_group.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CANVASGROUP_H
-#define CANVASGROUP_H
+#ifndef CANVAS_GROUP_H
+#define CANVAS_GROUP_H
#include "scene/2d/node_2d.h"
@@ -56,4 +56,4 @@ public:
~CanvasGroup();
};
-#endif // CANVASGROUP_H
+#endif // CANVAS_GROUP_H
diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h
index ec37449f8f..1fd54898f8 100644
--- a/scene/2d/canvas_modulate.h
+++ b/scene/2d/canvas_modulate.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CANVASMODULATE_H
-#define CANVASMODULATE_H
+#ifndef CANVAS_MODULATE_H
+#define CANVAS_MODULATE_H
#include "scene/2d/node_2d.h"
@@ -52,4 +52,4 @@ public:
~CanvasModulate();
};
-#endif // CANVASMODULATE_H
+#endif // CANVAS_MODULATE_H
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index 3c7f4cd9b5..a4231cc45d 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PARTICLES_2D_H
-#define PARTICLES_2D_H
+#ifndef GPU_PARTICLES_2D_H
+#define GPU_PARTICLES_2D_H
#include "scene/2d/node_2d.h"
@@ -167,4 +167,4 @@ public:
VARIANT_ENUM_CAST(GPUParticles2D::DrawOrder)
VARIANT_ENUM_CAST(GPUParticles2D::EmitFlags)
-#endif // PARTICLES_2D_H
+#endif // GPU_PARTICLES_2D_H
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index 4acfeaf781..b61e23464a 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LIGHTOCCLUDER2D_H
-#define LIGHTOCCLUDER2D_H
+#ifndef LIGHT_OCCLUDER_2D_H
+#define LIGHT_OCCLUDER_2D_H
#include "scene/2d/node_2d.h"
@@ -111,4 +111,4 @@ public:
~LightOccluder2D();
};
-#endif // LIGHTOCCLUDER2D_H
+#endif // LIGHT_OCCLUDER_2D_H
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 5322c5a5fe..27c510171a 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LINE2D_H
-#define LINE2D_H
+#ifndef LINE_2D_H
+#define LINE_2D_H
#include "node_2d.h"
@@ -138,4 +138,4 @@ private:
bool _antialiased = false;
};
-#endif // LINE2D_H
+#endif // LINE_2D_H
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index 032a15cad2..76eba20058 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -163,4 +163,4 @@ private:
void _check_distance_to_target();
};
-#endif
+#endif // NAVIGATION_AGENT_2D_H
diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h
index 948cf5b61a..afda05956a 100644
--- a/scene/2d/navigation_obstacle_2d.h
+++ b/scene/2d/navigation_obstacle_2d.h
@@ -74,4 +74,4 @@ private:
real_t estimate_agent_radius() const;
};
-#endif
+#endif // NAVIGATION_OBSTACLE_2D_H
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 6e8fd891cb..6f189a57e8 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -35,6 +35,7 @@
#include "core/os/mutex.h"
#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
+#include "servers/navigation_server_3d.h"
#include "thirdparty/misc/polypartition.h"
@@ -371,9 +372,11 @@ void NavigationRegion2D::set_enabled(bool p_enabled) {
NavigationServer2D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
}
- if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) {
+#ifdef DEBUG_ENABLED
+ if (Engine::get_singleton()->is_editor_hint() || NavigationServer3D::get_singleton()->get_debug_enabled()) {
update();
}
+#endif // DEBUG_ENABLED
}
bool NavigationRegion2D::is_enabled() const {
@@ -462,7 +465,8 @@ void NavigationRegion2D::_notification(int p_what) {
} break;
case NOTIFICATION_DRAW: {
- if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) {
+#ifdef DEBUG_ENABLED
+ if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer3D::get_singleton()->get_debug_enabled()) && navpoly.is_valid()) {
Vector<Vector2> verts = navpoly->get_vertices();
if (verts.size() < 3) {
return;
@@ -470,11 +474,11 @@ void NavigationRegion2D::_notification(int p_what) {
Color color;
if (enabled) {
- color = get_tree()->get_debug_navigation_color();
+ color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color();
} else {
- color = get_tree()->get_debug_navigation_disabled_color();
+ color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_color();
}
- Color doors_color = color.lightened(0.2);
+ Color doors_color = NavigationServer3D::get_singleton()->get_debug_navigation_edge_connection_color();
RandomPCG rand;
@@ -516,6 +520,7 @@ void NavigationRegion2D::_notification(int p_what) {
draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color);
}
}
+#endif // DEBUG_ENABLED
} break;
}
}
@@ -552,10 +557,13 @@ void NavigationRegion2D::_navpoly_changed() {
NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly);
}
}
+
void NavigationRegion2D::_map_changed(RID p_map) {
- if (enabled && get_world_2d()->get_navigation_map() == p_map) {
+#ifdef DEBUG_ENABLED
+ if (is_inside_tree() && get_world_2d()->get_navigation_map() == p_map) {
update();
}
+#endif // DEBUG_ENABLED
}
TypedArray<String> NavigationRegion2D::get_configuration_warnings() const {
@@ -605,8 +613,18 @@ NavigationRegion2D::NavigationRegion2D() {
region = NavigationServer2D::get_singleton()->region_create();
NavigationServer2D::get_singleton()->region_set_enter_cost(region, get_enter_cost());
NavigationServer2D::get_singleton()->region_set_travel_cost(region, get_travel_cost());
+
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+#endif // DEBUG_ENABLED
}
NavigationRegion2D::~NavigationRegion2D() {
NavigationServer2D::get_singleton()->free(region);
+
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+#endif // DEBUG_ENABLED
}
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index 473c34768f..0d8a31e6bb 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NODE2D_H
-#define NODE2D_H
+#ifndef NODE_2D_H
+#define NODE_2D_H
#include "scene/main/canvas_item.h"
@@ -124,4 +124,4 @@ public:
Node2D() {}
};
-#endif // NODE2D_H
+#endif // NODE_2D_H
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index e60a5ed034..ce22f32b01 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -387,8 +387,8 @@ void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p
//E->value.rc=0;
E->value.in_scene = node && node->is_inside_tree();
if (node) {
- node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree), make_binds(objid));
- node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree), make_binds(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree).bind(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree).bind(objid));
if (E->value.in_scene) {
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
}
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 8953813452..68e5ffdcf9 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -240,7 +240,7 @@ void RayCast2D::_draw_debug_shape() {
Transform2D xf;
xf.rotate(target_position.angle());
- xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
+ xf.translate_local(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
Vector<Vector2> pts = {
xf.xform(Vector2(arrow_size, 0)),
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
index ae810156a2..7589af0924 100644
--- a/scene/2d/shape_cast_2d.cpp
+++ b/scene/2d/shape_cast_2d.cpp
@@ -237,7 +237,7 @@ void ShapeCast2D::_notification(int p_what) {
if (target_position != Vector2()) {
Transform2D xf;
xf.rotate(target_position.angle());
- xf.translate(Vector2(target_position.length(), 0));
+ xf.translate_local(Vector2(target_position.length(), 0));
draw_line(Vector2(), target_position, draw_col, 2);
diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h
index 7ff080aed0..660e52f189 100644
--- a/scene/2d/shape_cast_2d.h
+++ b/scene/2d/shape_cast_2d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SHAPE_CAST_2D
-#define SHAPE_CAST_2D
+#ifndef SHAPE_CAST_2D_H
+#define SHAPE_CAST_2D_H
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
@@ -120,4 +120,4 @@ public:
TypedArray<String> get_configuration_warnings() const override;
};
-#endif
+#endif // SHAPE_CAST_2D_H
diff --git a/scene/2d/sprite_2d.h b/scene/2d/sprite_2d.h
index 6893e92d4a..5b33bb6802 100644
--- a/scene/2d/sprite_2d.h
+++ b/scene/2d/sprite_2d.h
@@ -125,4 +125,4 @@ public:
~Sprite2D();
};
-#endif // SPRITE_H
+#endif // SPRITE_2D_H
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index cf8b6b8f94..5ba8c95a06 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -2652,7 +2652,7 @@ void TileMap::clear_layer(int p_layer) {
// Remove all tiles.
_clear_layer_internals(p_layer);
layers[p_layer].tile_map.clear();
-
+ _recreate_layer_internals(p_layer);
used_rect_cache_dirty = true;
}
@@ -2662,6 +2662,7 @@ void TileMap::clear() {
for (unsigned int i = 0; i < layers.size(); i++) {
layers[i].tile_map.clear();
}
+ _recreate_internals();
used_rect_cache_dirty = true;
}
diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp
index 4a4a2a1da0..9dea69cd64 100644
--- a/scene/2d/touch_screen_button.cpp
+++ b/scene/2d/touch_screen_button.cpp
@@ -131,7 +131,7 @@ void TouchScreenButton::_notification(int p_what) {
pos = texture_normal->get_size() * 0.5;
}
- draw_set_transform_matrix(get_canvas_transform().translated(pos));
+ draw_set_transform_matrix(get_canvas_transform().translated_local(pos));
shape->draw(get_canvas_item(), draw_col);
}
} break;
@@ -258,7 +258,7 @@ bool TouchScreenButton::_is_point_inside(const Point2 &p_point) {
pos = texture_normal->get_size() * 0.5;
}
- touched = shape->collide(Transform2D().translated(pos), unit_rect, Transform2D(0, coord + Vector2(0.5, 0.5)));
+ touched = shape->collide(Transform2D().translated_local(pos), unit_rect, Transform2D(0, coord + Vector2(0.5, 0.5)));
}
if (bitmask.is_valid()) {
diff --git a/scene/2d/visible_on_screen_notifier_2d.h b/scene/2d/visible_on_screen_notifier_2d.h
index 38b508e2f6..ac7fad95a5 100644
--- a/scene/2d/visible_on_screen_notifier_2d.h
+++ b/scene/2d/visible_on_screen_notifier_2d.h
@@ -102,4 +102,4 @@ public:
VARIANT_ENUM_CAST(VisibleOnScreenEnabler2D::EnableMode);
-#endif // VISIBILITY_NOTIFIER_2D_H
+#endif // VISIBLE_ON_SCREEN_NOTIFIER_2D_H
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index fb0c59daa1..e9e19488e9 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -239,8 +239,8 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i
E->value.rc = 0;
E->value.in_tree = node && node->is_inside_tree();
if (node) {
- node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree), make_binds(objid));
- node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree), make_binds(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree).bind(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree).bind(objid));
if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
}
@@ -426,8 +426,8 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i
E->value.rc = 0;
E->value.in_tree = node && node->is_inside_tree();
if (node) {
- node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree), make_binds(objid));
- node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree), make_binds(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree).bind(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree).bind(objid));
if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_entered, node);
}
diff --git a/scene/3d/audio_listener_3d.h b/scene/3d/audio_listener_3d.h
index ebc37673ed..44c49f526e 100644
--- a/scene/3d/audio_listener_3d.h
+++ b/scene/3d/audio_listener_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LISTENER_3D_H
-#define LISTENER_3D_H
+#ifndef AUDIO_LISTENER_3D_H
+#define AUDIO_LISTENER_3D_H
#include "scene/3d/node_3d.h"
@@ -67,4 +67,4 @@ public:
~AudioListener3D();
};
-#endif
+#endif // AUDIO_LISTENER_3D_H
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 85ece6d8d5..647b18a4a7 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -196,4 +196,5 @@ public:
VARIANT_ENUM_CAST(AudioStreamPlayer3D::AttenuationModel)
VARIANT_ENUM_CAST(AudioStreamPlayer3D::DopplerTracking)
+
#endif // AUDIO_STREAM_PLAYER_3D_H
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index 137360b141..3224361a25 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef BONE_ATTACHMENT_H
-#define BONE_ATTACHMENT_H
+#ifndef BONE_ATTACHMENT_3D_H
+#define BONE_ATTACHMENT_3D_H
#include "scene/3d/skeleton_3d.h"
#ifdef TOOLS_ENABLED
@@ -99,4 +99,4 @@ public:
BoneAttachment3D();
};
-#endif // BONE_ATTACHMENT_H
+#endif // BONE_ATTACHMENT_3D_H
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 10348b1eb6..da4a14394d 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -31,7 +31,7 @@
#include "camera_3d.h"
#include "collision_object_3d.h"
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
#include "scene/main/viewport.h"
void Camera3D::_update_audio_listener_state() {
@@ -197,7 +197,7 @@ void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, rea
update_gizmos();
}
-void Camera3D::set_projection(Camera3D::Projection p_mode) {
+void Camera3D::set_projection(ProjectionType p_mode) {
if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
mode = p_mode;
_update_camera_mode();
@@ -265,7 +265,7 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const {
if (mode == PROJECTION_ORTHOGONAL) {
ray = Vector3(0, 0, -1);
} else {
- CameraMatrix cm;
+ Projection cm;
cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
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, -near).normalized();
@@ -314,7 +314,7 @@ Vector<Vector3> Camera3D::get_near_plane_points() const {
Size2 viewport_size = get_viewport()->get_visible_rect().size;
- CameraMatrix cm;
+ Projection cm;
if (mode == PROJECTION_ORTHOGONAL) {
cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
@@ -340,7 +340,7 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const {
Size2 viewport_size = get_viewport()->get_visible_rect().size;
- CameraMatrix cm;
+ Projection cm;
if (mode == PROJECTION_ORTHOGONAL) {
cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
@@ -368,7 +368,7 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons
}
Size2 viewport_size = get_viewport()->get_visible_rect().size;
- CameraMatrix cm;
+ Projection cm;
if (mode == PROJECTION_ORTHOGONAL) {
cm.set_orthogonal(size, viewport_size.aspect(), p_z_depth, far, keep_aspect == KEEP_WIDTH);
@@ -544,7 +544,7 @@ real_t Camera3D::get_far() const {
return far;
}
-Camera3D::Projection Camera3D::get_projection() const {
+Camera3D::ProjectionType Camera3D::get_projection() const {
return mode;
}
@@ -607,7 +607,7 @@ Vector<Plane> Camera3D::get_frustum() const {
ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());
Size2 viewport_size = get_viewport()->get_visible_rect().size;
- CameraMatrix cm;
+ Projection cm;
if (mode == PROJECTION_PERSPECTIVE) {
cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
} else {
diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h
index 9f2f8ceed1..cedd976890 100644
--- a/scene/3d/camera_3d.h
+++ b/scene/3d/camera_3d.h
@@ -40,7 +40,7 @@ class Camera3D : public Node3D {
GDCLASS(Camera3D, Node3D);
public:
- enum Projection {
+ enum ProjectionType {
PROJECTION_PERSPECTIVE,
PROJECTION_ORTHOGONAL,
PROJECTION_FRUSTUM
@@ -62,7 +62,7 @@ private:
bool current = false;
Viewport *viewport = nullptr;
- Projection mode = PROJECTION_PERSPECTIVE;
+ ProjectionType mode = PROJECTION_PERSPECTIVE;
real_t fov = 0.0;
real_t size = 1.0;
@@ -112,7 +112,7 @@ public:
void set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far);
void set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far);
void set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far);
- void set_projection(Camera3D::Projection p_mode);
+ void set_projection(Camera3D::ProjectionType p_mode);
void make_current();
void clear_current(bool p_enable_next = true);
@@ -127,7 +127,7 @@ public:
real_t get_near() const;
Vector2 get_frustum_offset() const;
- Projection get_projection() const;
+ ProjectionType get_projection() const;
void set_fov(real_t p_fov);
void set_size(real_t p_size);
@@ -181,8 +181,8 @@ public:
~Camera3D();
};
-VARIANT_ENUM_CAST(Camera3D::Projection);
+VARIANT_ENUM_CAST(Camera3D::ProjectionType);
VARIANT_ENUM_CAST(Camera3D::KeepAspect);
VARIANT_ENUM_CAST(Camera3D::DopplerTracking);
-#endif
+#endif // CAMERA_3D_H
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index a36357555a..0871bf536b 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -319,7 +319,7 @@ bool CollisionObject3D::_are_collision_shapes_visible() {
void CollisionObject3D::_update_shape_data(uint32_t p_owner) {
if (_are_collision_shapes_visible()) {
if (debug_shapes_to_update.is_empty()) {
- callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferred({}, 0);
+ callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferredp({}, 0);
}
debug_shapes_to_update.insert(p_owner);
}
@@ -365,8 +365,7 @@ void CollisionObject3D::_update_debug_shapes() {
RS::get_singleton()->instance_set_scenario(s.debug_shape, get_world_3d()->get_scenario());
if (!s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) {
- s.shape->connect("changed", callable_mp(this, &CollisionObject3D::_shape_changed),
- varray(s.shape), CONNECT_DEFERRED);
+ s.shape->connect("changed", callable_mp(this, &CollisionObject3D::_shape_changed).bind(s.shape), CONNECT_DEFERRED);
}
++debug_shapes_count;
diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h
index 098f573551..3ec3aa0fc1 100644
--- a/scene/3d/collision_object_3d.h
+++ b/scene/3d/collision_object_3d.h
@@ -166,4 +166,4 @@ public:
VARIANT_ENUM_CAST(CollisionObject3D::DisableMode);
-#endif // COLLISION_OBJECT__H
+#endif // COLLISION_OBJECT_3D_H
diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h
index a24d485af2..74e5867a2f 100644
--- a/scene/3d/collision_polygon_3d.h
+++ b/scene/3d/collision_polygon_3d.h
@@ -79,4 +79,4 @@ public:
CollisionPolygon3D();
};
-#endif // COLLISION_POLYGON_H
+#endif // COLLISION_POLYGON_3D_H
diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h
index 5c32230942..124c0d166d 100644
--- a/scene/3d/collision_shape_3d.h
+++ b/scene/3d/collision_shape_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef COLLISION_SHAPE_H
-#define COLLISION_SHAPE_H
+#ifndef COLLISION_SHAPE_3D_H
+#define COLLISION_SHAPE_3D_H
#include "scene/3d/node_3d.h"
#include "scene/resources/shape_3d.h"
@@ -68,4 +68,4 @@ public:
~CollisionShape3D();
};
-#endif // BODY_VOLUME_H
+#endif // COLLISION_SHAPE_3D_H
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index 7f225ee98d..4cb693f494 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CPU_PARTICLES_H
-#define CPU_PARTICLES_H
+#ifndef CPU_PARTICLES_3D_H
+#define CPU_PARTICLES_3D_H
#include "scene/3d/visual_instance_3d.h"
@@ -317,4 +317,4 @@ VARIANT_ENUM_CAST(CPUParticles3D::Parameter)
VARIANT_ENUM_CAST(CPUParticles3D::ParticleFlags)
VARIANT_ENUM_CAST(CPUParticles3D::EmissionShape)
-#endif // CPU_PARTICLES_H
+#endif // CPU_PARTICLES_3D_H
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index 01cab493ec..0112f24e0c 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -215,11 +215,13 @@ void Decal::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cull_mask"), &Decal::get_cull_mask);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,suffix:m"), "set_extents", "get_extents");
+
ADD_GROUP("Textures", "texture_");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ALBEDO);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_NORMAL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_orm", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ORM);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION);
+
ADD_GROUP("Parameters", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
@@ -227,13 +229,16 @@ void Decal::_bind_methods() {
// A Normal Fade of 1.0 causes the decal to be invisible even if fully perpendicular to a surface.
// Due to this, limit Normal Fade to 0.999.
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_fade", PROPERTY_HINT_RANGE, "0,0.999,0.001"), "set_normal_fade", "get_normal_fade");
+
ADD_GROUP("Vertical Fade", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "upper_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_upper_fade", "get_upper_fade");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lower_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_lower_fade", "get_lower_fade");
+
ADD_GROUP("Distance Fade", "distance_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_NONE, "suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_NONE, "suffix:m"), "set_distance_fade_length", "get_distance_fade_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length");
+
ADD_GROUP("Cull Mask", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
diff --git a/scene/3d/decal.h b/scene/3d/decal.h
index d5990272c6..38da4c14e3 100644
--- a/scene/3d/decal.h
+++ b/scene/3d/decal.h
@@ -57,8 +57,8 @@ private:
real_t upper_fade = 0.3;
real_t lower_fade = 0.3;
bool distance_fade_enabled = false;
- real_t distance_fade_begin = 10.0;
- real_t distance_fade_length = 1.0;
+ real_t distance_fade_begin = 40.0;
+ real_t distance_fade_length = 10.0;
protected:
static void _bind_methods();
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index b352114c7f..01aff32954 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -222,7 +222,7 @@ void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) {
draw_passes.write[p_pass] = p_mesh;
if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) {
- draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), varray(), CONNECT_DEFERRED);
+ draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), CONNECT_DEFERRED);
}
RID mesh_rid;
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index adce45a0a9..0c745dd734 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PARTICLES_H
-#define PARTICLES_H
+#ifndef GPU_PARTICLES_3D_H
+#define GPU_PARTICLES_3D_H
#include "scene/3d/visual_instance_3d.h"
#include "scene/resources/skin.h"
@@ -179,4 +179,4 @@ VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder)
VARIANT_ENUM_CAST(GPUParticles3D::TransformAlign)
VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags)
-#endif // PARTICLES_H
+#endif // GPU_PARTICLES_3D_H
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
index da0789ccd5..1fcd5160f6 100644
--- a/scene/3d/gpu_particles_collision_3d.cpp
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -30,6 +30,7 @@
#include "gpu_particles_collision_3d.h"
+#include "core/object/worker_thread_pool.h"
#include "mesh_instance_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/main/viewport.h"
@@ -339,15 +340,12 @@ void GPUParticlesCollisionSDF3D::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *
}
void GPUParticlesCollisionSDF3D::_compute_sdf(ComputeSDFParams *params) {
- ThreadWorkPool work_pool;
- work_pool.init();
- work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params);
- while (!work_pool.is_done_dispatching()) {
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params, params->size.z);
+ while (!WorkerThreadPool::get_singleton()->is_group_task_completed(group_task)) {
OS::get_singleton()->delay_usec(10000);
- bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF");
+ bake_step_function(WorkerThreadPool::get_singleton()->get_group_processed_element_count(group_task) * 100 / params->size.z, "Baking SDF");
}
- work_pool.end_work();
- work_pool.finish();
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
}
Vector3i GPUParticlesCollisionSDF3D::get_estimated_cell_size() const {
diff --git a/scene/3d/importer_mesh_instance_3d.h b/scene/3d/importer_mesh_instance_3d.h
index 3daf06771d..223b6fc80a 100644
--- a/scene/3d/importer_mesh_instance_3d.h
+++ b/scene/3d/importer_mesh_instance_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCENE_IMPORTER_MESH_INSTANCE_3D_H
-#define SCENE_IMPORTER_MESH_INSTANCE_3D_H
+#ifndef IMPORTER_MESH_INSTANCE_3D_H
+#define IMPORTER_MESH_INSTANCE_3D_H
#include "scene/3d/node_3d.h"
#include "scene/resources/immediate_mesh.h"
@@ -61,4 +61,5 @@ public:
void set_skeleton_path(const NodePath &p_path);
NodePath get_skeleton_path() const;
};
-#endif
+
+#endif // IMPORTER_MESH_INSTANCE_3D_H
diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp
index 0b824ef28b..b0509475a7 100644
--- a/scene/3d/joint_3d.cpp
+++ b/scene/3d/joint_3d.cpp
@@ -737,7 +737,8 @@ void Generic6DOFJoint3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flag_z", "flag", "value"), &Generic6DOFJoint3D::set_flag_z);
ClassDB::bind_method(D_METHOD("get_flag_z", "flag"), &Generic6DOFJoint3D::get_flag_z);
- // X
+ ADD_GROUP("Linear Limit", "linear_limit_");
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_x", "get_param_x", PARAM_LINEAR_UPPER_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_x", "get_param_x", PARAM_LINEAR_LOWER_LIMIT);
@@ -745,15 +746,53 @@ void Generic6DOFJoint3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_RESTITUTION);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_UPPER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_LOWER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_UPPER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_LOWER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING);
+
+ ADD_GROUP("Linear Motor", "linear_motor_");
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+
+ ADD_GROUP("Linear Spring", "linear_spring_");
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_SPRING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_STIFFNESS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/damping"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
+
+ ADD_GROUP("Angular Limit", "angular_limit_");
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_x/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_x", "_get_angular_hi_limit_x");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_x", "_get_angular_lo_limit_x");
@@ -763,68 +802,15 @@ void Generic6DOFJoint3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/erp"), "set_param_x", "get_param_x", PARAM_ANGULAR_ERP);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
-
- // Y
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_UPPER_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_LOWER_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
-
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_y/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_y", "_get_angular_hi_limit_y");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_y", "_get_angular_lo_limit_y");
-
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_LIMIT_SOFTNESS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_RESTITUTION);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/erp"), "set_param_y", "get_param_y", PARAM_ANGULAR_ERP);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
-
- // Z
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_UPPER_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_LOWER_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_SPRING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_STIFFNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
-
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_z/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_z", "_get_angular_hi_limit_z");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_z", "_get_angular_lo_limit_z");
@@ -834,10 +820,32 @@ void Generic6DOFJoint3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/erp"), "set_param_z", "get_param_z", PARAM_ANGULAR_ERP);
+ ADD_GROUP("Angular Motor", "angular_motor_");
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+ ADD_GROUP("Angular Spring", "angular_spring_");
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_SPRING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_STIFFNESS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING);
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 6c999d85e2..678c217676 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -291,6 +291,7 @@ void Light3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_SPECULAR);
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI/SDFGI only)"), "set_bake_mode", "get_bake_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
+
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BIAS);
@@ -299,12 +300,16 @@ 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_fog_fade", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BLUR);
+
+ ADD_GROUP("Distance Fade", "distance_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_shadow", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_shadow", "get_distance_fade_shadow");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length");
+
ADD_GROUP("Editor", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only");
+
ADD_GROUP("", "");
BIND_ENUM_CONSTANT(PARAM_ENERGY);
diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h
index f7a23c776a..85150b833f 100644
--- a/scene/3d/lightmap_gi.h
+++ b/scene/3d/lightmap_gi.h
@@ -271,4 +271,4 @@ VARIANT_ENUM_CAST(LightmapGI::GenerateProbes);
VARIANT_ENUM_CAST(LightmapGI::BakeError);
VARIANT_ENUM_CAST(LightmapGI::EnvironmentMode);
-#endif // BAKED_LIGHTMAP_H
+#endif // LIGHTMAP_GI_H
diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h
index dc9c64fa41..48d76b9a88 100644
--- a/scene/3d/mesh_instance_3d.h
+++ b/scene/3d/mesh_instance_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef MESH_INSTANCE_H
-#define MESH_INSTANCE_H
+#ifndef MESH_INSTANCE_3D_H
+#define MESH_INSTANCE_3D_H
#include "core/templates/local_vector.h"
#include "scene/3d/visual_instance_3d.h"
@@ -98,4 +98,4 @@ public:
~MeshInstance3D();
};
-#endif
+#endif // MESH_INSTANCE_3D_H
diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h
index 111f1e9c09..2fa8dd965f 100644
--- a/scene/3d/multimesh_instance_3d.h
+++ b/scene/3d/multimesh_instance_3d.h
@@ -53,4 +53,4 @@ public:
~MultiMeshInstance3D();
};
-#endif // MULTIMESH_INSTANCE_H
+#endif // MULTIMESH_INSTANCE_3D_H
diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h
index 0a00d769c3..e05f0287f7 100644
--- a/scene/3d/navigation_agent_3d.h
+++ b/scene/3d/navigation_agent_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NAVIGATION_AGENT_H
-#define NAVIGATION_AGENT_H
+#ifndef NAVIGATION_AGENT_3D_H
+#define NAVIGATION_AGENT_3D_H
#include "scene/main/node.h"
@@ -175,4 +175,4 @@ private:
void _check_distance_to_target();
};
-#endif
+#endif // NAVIGATION_AGENT_3D_H
diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h
index 0ddde64c0e..0316fc37a8 100644
--- a/scene/3d/navigation_obstacle_3d.h
+++ b/scene/3d/navigation_obstacle_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NAVIGATION_OBSTACLE_H
-#define NAVIGATION_OBSTACLE_H
+#ifndef NAVIGATION_OBSTACLE_3D_H
+#define NAVIGATION_OBSTACLE_3D_H
#include "scene/3d/node_3d.h"
@@ -73,4 +73,4 @@ private:
real_t estimate_agent_radius() const;
};
-#endif
+#endif // NAVIGATION_OBSTACLE_3D_H
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 2a8149c6f6..0abb0e8dea 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -49,14 +49,29 @@ void NavigationRegion3D::set_enabled(bool p_enabled) {
NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
}
- if (debug_view) {
- MeshInstance3D *dm = Object::cast_to<MeshInstance3D>(debug_view);
- if (is_enabled()) {
- dm->set_material_override(get_tree()->get_debug_navigation_material());
+#ifdef DEBUG_ENABLED
+ if (debug_instance.is_valid()) {
+ if (!is_enabled()) {
+ if (debug_mesh.is_valid()) {
+ if (debug_mesh->get_surface_count() > 0) {
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
+ }
+ if (debug_mesh->get_surface_count() > 1) {
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
+ }
+ }
} else {
- dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
+ if (debug_mesh.is_valid()) {
+ if (debug_mesh->get_surface_count() > 0) {
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, RID());
+ }
+ if (debug_mesh->get_surface_count() > 1) {
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, RID());
+ }
+ }
}
}
+#endif // DEBUG_ENABLED
update_gizmos();
}
@@ -124,30 +139,36 @@ void NavigationRegion3D::_notification(int p_what) {
NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
}
- if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) {
- MeshInstance3D *dm = memnew(MeshInstance3D);
- dm->set_mesh(navmesh->get_debug_mesh());
- if (is_enabled()) {
- dm->set_material_override(get_tree()->get_debug_navigation_material());
- } else {
- dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
- }
- add_child(dm);
- debug_view = dm;
+#ifdef DEBUG_ENABLED
+ if (NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ _update_debug_mesh();
}
+#endif // DEBUG_ENABLED
+
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform());
+
+#ifdef DEBUG_ENABLED
+ if (is_inside_tree() && debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform());
+ }
+#endif // DEBUG_ENABLED
+
} break;
case NOTIFICATION_EXIT_TREE: {
NavigationServer3D::get_singleton()->region_set_map(region, RID());
- if (debug_view) {
- debug_view->queue_delete();
- debug_view = nullptr;
+#ifdef DEBUG_ENABLED
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_instance, false);
+ }
+ if (debug_edge_connections_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
+#endif // DEBUG_ENABLED
} break;
}
}
@@ -169,20 +190,21 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmes
NavigationServer3D::get_singleton()->region_set_navmesh(region, p_navmesh);
- if (debug_view == nullptr && is_inside_tree() && navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) {
- MeshInstance3D *dm = memnew(MeshInstance3D);
- dm->set_mesh(navmesh->get_debug_mesh());
- if (is_enabled()) {
- dm->set_material_override(get_tree()->get_debug_navigation_material());
+#ifdef DEBUG_ENABLED
+ if (is_inside_tree() && NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (navmesh.is_valid()) {
+ _update_debug_mesh();
+ _update_debug_edge_connections_mesh();
} else {
- dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_instance, false);
+ }
+ if (debug_edge_connections_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
+ }
}
- add_child(dm);
- debug_view = dm;
- }
- if (debug_view && navmesh.is_valid()) {
- Object::cast_to<MeshInstance3D>(debug_view)->set_mesh(navmesh->get_debug_mesh());
}
+#endif // DEBUG_ENABLED
emit_signal(SNAME("navigation_mesh_changed"));
@@ -287,13 +309,31 @@ void NavigationRegion3D::_bind_methods() {
void NavigationRegion3D::_navigation_changed() {
update_gizmos();
update_configuration_warnings();
+
+#ifdef DEBUG_ENABLED
+ _update_debug_edge_connections_mesh();
+#endif // DEBUG_ENABLED
+}
+
+#ifdef DEBUG_ENABLED
+void NavigationRegion3D::_navigation_map_changed(RID p_map) {
+ if (is_inside_tree() && p_map == get_world_3d()->get_navigation_map()) {
+ _update_debug_edge_connections_mesh();
+ }
}
+#endif // DEBUG_ENABLED
NavigationRegion3D::NavigationRegion3D() {
set_notify_transform(true);
region = NavigationServer3D::get_singleton()->region_create();
NavigationServer3D::get_singleton()->region_set_enter_cost(region, get_enter_cost());
NavigationServer3D::get_singleton()->region_set_travel_cost(region, get_travel_cost());
+
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
+ NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
+ NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
+#endif // DEBUG_ENABLED
}
NavigationRegion3D::~NavigationRegion3D() {
@@ -301,4 +341,245 @@ NavigationRegion3D::~NavigationRegion3D() {
navmesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed));
}
NavigationServer3D::get_singleton()->free(region);
+
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
+ NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
+ NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
+ if (debug_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(debug_instance);
+ }
+ if (debug_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(debug_mesh->get_rid());
+ }
+ if (debug_edge_connections_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(debug_edge_connections_instance);
+ }
+ if (debug_edge_connections_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(debug_edge_connections_mesh->get_rid());
+ }
+#endif // DEBUG_ENABLED
+}
+
+#ifdef DEBUG_ENABLED
+void NavigationRegion3D::_update_debug_mesh() {
+ if (!NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_instance, false);
+ }
+ return;
+ }
+
+ if (!navmesh.is_valid()) {
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_instance, false);
+ }
+ return;
+ }
+
+ if (!debug_instance.is_valid()) {
+ debug_instance = RenderingServer::get_singleton()->instance_create();
+ }
+
+ if (!debug_mesh.is_valid()) {
+ debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ }
+
+ debug_mesh->clear_surfaces();
+
+ bool enabled_geometry_face_random_color = NavigationServer3D::get_singleton()->get_debug_navigation_enable_geometry_face_random_color();
+ bool enabled_edge_lines = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_lines();
+
+ Vector<Vector3> vertices = navmesh->get_vertices();
+ if (vertices.size() == 0) {
+ return;
+ }
+
+ int polygon_count = navmesh->get_polygon_count();
+ if (polygon_count == 0) {
+ return;
+ }
+
+ Vector<Vector3> face_vertex_array;
+ face_vertex_array.resize(polygon_count * 3);
+
+ Vector<Color> face_color_array;
+ if (enabled_geometry_face_random_color) {
+ face_color_array.resize(polygon_count * 3);
+ }
+
+ Vector<Vector3> line_vertex_array;
+ if (enabled_edge_lines) {
+ line_vertex_array.resize(polygon_count * 6);
+ }
+
+ Color debug_navigation_geometry_face_color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color();
+
+ Ref<StandardMaterial3D> face_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_material();
+ Ref<StandardMaterial3D> line_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_material();
+
+ Color polygon_color = debug_navigation_geometry_face_color;
+
+ for (int i = 0; i < polygon_count; i++) {
+ if (enabled_geometry_face_random_color) {
+ polygon_color = debug_navigation_geometry_face_color * (Color(Math::randf(), Math::randf(), Math::randf()));
+ }
+
+ Vector<int> polygon = navmesh->get_polygon(i);
+
+ face_vertex_array.push_back(vertices[polygon[0]]);
+ face_vertex_array.push_back(vertices[polygon[1]]);
+ face_vertex_array.push_back(vertices[polygon[2]]);
+ if (enabled_geometry_face_random_color) {
+ face_color_array.push_back(polygon_color);
+ face_color_array.push_back(polygon_color);
+ face_color_array.push_back(polygon_color);
+ }
+
+ if (enabled_edge_lines) {
+ line_vertex_array.push_back(vertices[polygon[0]]);
+ line_vertex_array.push_back(vertices[polygon[1]]);
+ line_vertex_array.push_back(vertices[polygon[1]]);
+ line_vertex_array.push_back(vertices[polygon[2]]);
+ line_vertex_array.push_back(vertices[polygon[2]]);
+ line_vertex_array.push_back(vertices[polygon[0]]);
+ }
+ }
+
+ Array face_mesh_array;
+ face_mesh_array.resize(Mesh::ARRAY_MAX);
+ face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
+ if (enabled_geometry_face_random_color) {
+ face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array;
+ }
+ debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
+ debug_mesh->surface_set_material(0, face_material);
+
+ if (enabled_edge_lines) {
+ Array line_mesh_array;
+ line_mesh_array.resize(Mesh::ARRAY_MAX);
+ line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
+ debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array);
+ debug_mesh->surface_set_material(1, line_material);
+ }
+
+ RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid());
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree());
+ }
+ if (!is_enabled()) {
+ if (debug_mesh.is_valid()) {
+ if (debug_mesh->get_surface_count() > 0) {
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
+ }
+ if (debug_mesh->get_surface_count() > 1) {
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
+ }
+ }
+ } else {
+ if (debug_mesh.is_valid()) {
+ if (debug_mesh->get_surface_count() > 0) {
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, RID());
+ }
+ if (debug_mesh->get_surface_count() > 1) {
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, RID());
+ }
+ }
+ }
+}
+#endif // DEBUG_ENABLED
+
+#ifdef DEBUG_ENABLED
+void NavigationRegion3D::_update_debug_edge_connections_mesh() {
+ if (!NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (debug_edge_connections_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
+ }
+ return;
+ }
+
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (!navmesh.is_valid()) {
+ if (debug_edge_connections_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
+ }
+ return;
+ }
+
+ if (!debug_edge_connections_instance.is_valid()) {
+ debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create();
+ }
+
+ if (!debug_edge_connections_mesh.is_valid()) {
+ debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ }
+
+ debug_edge_connections_mesh->clear_surfaces();
+
+ float edge_connection_margin = NavigationServer3D::get_singleton()->map_get_edge_connection_margin(get_world_3d()->get_navigation_map());
+ float half_edge_connection_margin = edge_connection_margin * 0.5;
+ int connections_count = NavigationServer3D::get_singleton()->region_get_connections_count(region);
+
+ if (connections_count == 0) {
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
+ return;
+ }
+
+ Vector<Vector3> vertex_array;
+
+ for (int i = 0; i < connections_count; i++) {
+ Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(region, i);
+ Vector3 connection_pathway_end = NavigationServer3D::get_singleton()->region_get_connection_pathway_end(region, i);
+
+ Vector3 direction_start_end = connection_pathway_start.direction_to(connection_pathway_end);
+ Vector3 direction_end_start = connection_pathway_end.direction_to(connection_pathway_start);
+
+ Vector3 start_right_dir = direction_start_end.cross(Vector3(0, 1, 0));
+ Vector3 start_left_dir = -start_right_dir;
+
+ Vector3 end_right_dir = direction_end_start.cross(Vector3(0, 1, 0));
+ Vector3 end_left_dir = -end_right_dir;
+
+ Vector3 left_start_pos = connection_pathway_start + (start_left_dir * half_edge_connection_margin);
+ Vector3 right_start_pos = connection_pathway_start + (start_right_dir * half_edge_connection_margin);
+ Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin);
+ Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin);
+
+ vertex_array.push_back(right_end_pos);
+ vertex_array.push_back(left_start_pos);
+ vertex_array.push_back(right_start_pos);
+
+ vertex_array.push_back(left_end_pos);
+ vertex_array.push_back(right_end_pos);
+ vertex_array.push_back(right_start_pos);
+ }
+
+ if (vertex_array.size() == 0) {
+ return;
+ }
+
+ Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_edge_connections_material();
+
+ Array mesh_array;
+ mesh_array.resize(Mesh::ARRAY_MAX);
+ mesh_array[Mesh::ARRAY_VERTEX] = vertex_array;
+
+ debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array);
+ debug_edge_connections_mesh->surface_set_material(0, edge_connections_material);
+
+ RS::get_singleton()->instance_set_base(debug_edge_connections_instance, debug_edge_connections_mesh->get_rid());
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, is_visible_in_tree());
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(debug_edge_connections_instance, get_world_3d()->get_scenario());
+ }
+
+ bool enable_edge_connections = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_connections();
+ if (!enable_edge_connections) {
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
+ }
}
+#endif // DEBUG_ENABLED
diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h
index aaaf5dd3b8..ba326abb46 100644
--- a/scene/3d/navigation_region_3d.h
+++ b/scene/3d/navigation_region_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NAVIGATION_REGION_H
-#define NAVIGATION_REGION_H
+#ifndef NAVIGATION_REGION_3D_H
+#define NAVIGATION_REGION_3D_H
#include "scene/3d/node_3d.h"
#include "scene/resources/navigation_mesh.h"
@@ -44,11 +44,22 @@ class NavigationRegion3D : public Node3D {
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
- Node *debug_view = nullptr;
Thread bake_thread;
void _navigation_changed();
+#ifdef DEBUG_ENABLED
+ RID debug_instance;
+ RID debug_edge_connections_instance;
+ Ref<ArrayMesh> debug_mesh;
+ Ref<ArrayMesh> debug_edge_connections_mesh;
+
+private:
+ void _update_debug_mesh();
+ void _update_debug_edge_connections_mesh();
+ void _navigation_map_changed(RID p_map);
+#endif // DEBUG_ENABLED
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -85,4 +96,4 @@ public:
~NavigationRegion3D();
};
-#endif // NAVIGATION_REGION_H
+#endif // NAVIGATION_REGION_3D_H
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 04b1081516..1de85d57a3 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -733,7 +733,7 @@ void Node3D::rotate_z(real_t p_angle) {
void Node3D::translate(const Vector3 &p_offset) {
Transform3D t = get_transform();
- t.translate(p_offset);
+ t.translate_local(p_offset);
set_transform(t);
}
@@ -741,7 +741,7 @@ void Node3D::translate_object_local(const Vector3 &p_offset) {
Transform3D t = get_transform();
Transform3D s;
- s.translate(p_offset);
+ s.translate_local(p_offset);
set_transform(t * s);
}
diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h
index ed6610074e..11d731b989 100644
--- a/scene/3d/occluder_instance_3d.h
+++ b/scene/3d/occluder_instance_3d.h
@@ -211,4 +211,4 @@ public:
~OccluderInstance3D();
};
-#endif
+#endif // OCCLUDER_INSTANCE_3D_H
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index 1f10337b4c..25226ad384 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -296,7 +296,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) {
}
}
- t.translate(Vector3(h_offset, v_offset, 0));
+ t.translate_local(Vector3(h_offset, v_offset, 0));
} else {
t.origin = pos + Vector3(h_offset, v_offset, 0);
}
diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h
index 7c7284534e..b4cc6db7e3 100644
--- a/scene/3d/path_3d.h
+++ b/scene/3d/path_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PATH_H
-#define PATH_H
+#ifndef PATH_3D_H
+#define PATH_3D_H
#include "scene/3d/node_3d.h"
#include "scene/resources/curve.h"
@@ -119,4 +119,4 @@ public:
VARIANT_ENUM_CAST(PathFollow3D::RotationMode);
-#endif // PATH_H
+#endif // PATH_3D_H
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 30f7a025fa..993608c306 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -439,8 +439,8 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p
//E->value.rc=0;
E->value.in_tree = node && node->is_inside_tree();
if (node) {
- node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree), make_binds(objid));
- node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree), make_binds(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree).bind(objid));
+ node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree).bind(objid));
if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index 22dcb218bc..e4a41be6c0 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -784,4 +784,4 @@ private:
VARIANT_ENUM_CAST(PhysicalBone3D::JointType);
VARIANT_ENUM_CAST(PhysicalBone3D::DampMode);
-#endif // PHYSICS_BODY__H
+#endif // PHYSICS_BODY_3D_H
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index c69c910efb..aa62f6927e 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -126,4 +126,4 @@ public:
RayCast3D();
};
-#endif // RAY_CAST_H
+#endif // RAY_CAST_3D_H
diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h
index 424976d895..a161717ece 100644
--- a/scene/3d/reflection_probe.h
+++ b/scene/3d/reflection_probe.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef REFLECTIONPROBE_H
-#define REFLECTIONPROBE_H
+#ifndef REFLECTION_PROBE_H
+#define REFLECTION_PROBE_H
#include "scene/3d/visual_instance_3d.h"
@@ -121,4 +121,4 @@ public:
VARIANT_ENUM_CAST(ReflectionProbe::AmbientMode);
VARIANT_ENUM_CAST(ReflectionProbe::UpdateMode);
-#endif // REFLECTIONPROBE_H
+#endif // REFLECTION_PROBE_H
diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h
index 03bb253578..ab134c1261 100644
--- a/scene/3d/remote_transform_3d.h
+++ b/scene/3d/remote_transform_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef REMOTETRANSFORM_H
-#define REMOTETRANSFORM_H
+#ifndef REMOTE_TRANSFORM_3D_H
+#define REMOTE_TRANSFORM_3D_H
#include "scene/3d/node_3d.h"
@@ -75,4 +75,4 @@ public:
RemoteTransform3D();
};
-#endif // REMOTETRANSFORM_H
+#endif // REMOTE_TRANSFORM_3D_H
diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp
new file mode 100644
index 0000000000..df8bd7dfc9
--- /dev/null
+++ b/scene/3d/shape_cast_3d.cpp
@@ -0,0 +1,630 @@
+/*************************************************************************/
+/* shape_cast_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "shape_cast_3d.h"
+
+#include "collision_object_3d.h"
+#include "mesh_instance_3d.h"
+#include "scene/resources/concave_polygon_shape_3d.h"
+
+void ShapeCast3D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ _update_debug_shape_vertices();
+ }
+ if (enabled && !Engine::get_singleton()->is_editor_hint()) {
+ set_physics_process_internal(true);
+ } else {
+ set_physics_process_internal(false);
+ }
+
+ if (get_tree()->is_debugging_collisions_hint()) {
+ _update_debug_shape();
+ }
+
+ if (Object::cast_to<CollisionObject3D>(get_parent())) {
+ if (exclude_parent_body) {
+ exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid());
+ } else {
+ exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid());
+ }
+ }
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ if (enabled) {
+ set_physics_process_internal(false);
+ }
+
+ if (debug_shape) {
+ _clear_debug_shape();
+ }
+ } break;
+
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (!enabled) {
+ break;
+ }
+
+ bool prev_collision_state = collided;
+ _update_shapecast_state();
+ if (get_tree()->is_debugging_collisions_hint()) {
+ if (prev_collision_state != collided) {
+ _update_debug_shape_material(true);
+ }
+ if (collided) {
+ _update_debug_shape();
+ }
+ if (prev_collision_state == collided && !collided) {
+ _update_debug_shape();
+ }
+ }
+ } break;
+ }
+}
+
+void ShapeCast3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &ShapeCast3D::resource_changed);
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ShapeCast3D::set_enabled);
+ ClassDB::bind_method(D_METHOD("is_enabled"), &ShapeCast3D::is_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_shape", "shape"), &ShapeCast3D::set_shape);
+ ClassDB::bind_method(D_METHOD("get_shape"), &ShapeCast3D::get_shape);
+
+ ClassDB::bind_method(D_METHOD("set_target_position", "local_point"), &ShapeCast3D::set_target_position);
+ ClassDB::bind_method(D_METHOD("get_target_position"), &ShapeCast3D::get_target_position);
+
+ ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ShapeCast3D::set_margin);
+ ClassDB::bind_method(D_METHOD("get_margin"), &ShapeCast3D::get_margin);
+
+ ClassDB::bind_method(D_METHOD("set_max_results", "max_results"), &ShapeCast3D::set_max_results);
+ ClassDB::bind_method(D_METHOD("get_max_results"), &ShapeCast3D::get_max_results);
+
+ ClassDB::bind_method(D_METHOD("is_colliding"), &ShapeCast3D::is_colliding);
+ ClassDB::bind_method(D_METHOD("get_collision_count"), &ShapeCast3D::get_collision_count);
+
+ ClassDB::bind_method(D_METHOD("force_shapecast_update"), &ShapeCast3D::force_shapecast_update);
+
+ ClassDB::bind_method(D_METHOD("get_collider", "index"), &ShapeCast3D::get_collider);
+ ClassDB::bind_method(D_METHOD("get_collider_shape", "index"), &ShapeCast3D::get_collider_shape);
+ ClassDB::bind_method(D_METHOD("get_collision_point", "index"), &ShapeCast3D::get_collision_point);
+ ClassDB::bind_method(D_METHOD("get_collision_normal", "index"), &ShapeCast3D::get_collision_normal);
+
+ ClassDB::bind_method(D_METHOD("get_closest_collision_safe_fraction"), &ShapeCast3D::get_closest_collision_safe_fraction);
+ ClassDB::bind_method(D_METHOD("get_closest_collision_unsafe_fraction"), &ShapeCast3D::get_closest_collision_unsafe_fraction);
+
+ ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ShapeCast3D::add_exception_rid);
+ ClassDB::bind_method(D_METHOD("add_exception", "node"), &ShapeCast3D::add_exception);
+
+ ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ShapeCast3D::remove_exception_rid);
+ ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ShapeCast3D::remove_exception);
+
+ ClassDB::bind_method(D_METHOD("clear_exceptions"), &ShapeCast3D::clear_exceptions);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ShapeCast3D::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &ShapeCast3D::get_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ShapeCast3D::set_collision_mask_value);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ShapeCast3D::get_collision_mask_value);
+
+ ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &ShapeCast3D::set_exclude_parent_body);
+ ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &ShapeCast3D::get_exclude_parent_body);
+
+ ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &ShapeCast3D::set_collide_with_areas);
+ ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &ShapeCast3D::is_collide_with_areas_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast3D::set_collide_with_bodies);
+ ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast3D::is_collide_with_bodies_enabled);
+
+ ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast3D::_get_collision_result);
+
+ ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &ShapeCast3D::set_debug_shape_custom_color);
+ ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &ShapeCast3D::get_debug_shape_custom_color);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "suffix:m"), "set_target_position", "get_target_position");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_margin", "get_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result");
+
+ ADD_GROUP("Collide With", "collide_with");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled");
+
+ ADD_GROUP("Debug Shape", "debug_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_shape_custom_color"), "set_debug_shape_custom_color", "get_debug_shape_custom_color");
+}
+
+TypedArray<String> ShapeCast3D::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node3D::get_configuration_warnings();
+
+ if (shape.is_null()) {
+ warnings.push_back(RTR("This node cannot interact with other objects unless a Shape3D is assigned."));
+ }
+ if (shape.is_valid() && Object::cast_to<ConcavePolygonShape3D>(*shape)) {
+ warnings.push_back(RTR("ShapeCast3D does not support ConcavePolygonShape3Ds. Collisions will not be reported."));
+ }
+ return warnings;
+}
+
+void ShapeCast3D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+ update_gizmos();
+
+ if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
+ set_physics_process_internal(p_enabled);
+ }
+ if (!p_enabled) {
+ collided = false;
+ }
+
+ if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) {
+ if (p_enabled) {
+ _update_debug_shape();
+ } else {
+ _clear_debug_shape();
+ }
+ }
+}
+
+bool ShapeCast3D::is_enabled() const {
+ return enabled;
+}
+
+void ShapeCast3D::set_target_position(const Vector3 &p_point) {
+ target_position = p_point;
+ if (is_inside_tree()) {
+ _update_debug_shape();
+ }
+ update_gizmos();
+
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (is_inside_tree()) {
+ _update_debug_shape_vertices();
+ }
+ } else if (debug_shape) {
+ _update_debug_shape();
+ }
+}
+
+Vector3 ShapeCast3D::get_target_position() const {
+ return target_position;
+}
+
+void ShapeCast3D::set_margin(real_t p_margin) {
+ margin = p_margin;
+}
+
+real_t ShapeCast3D::get_margin() const {
+ return margin;
+}
+
+void ShapeCast3D::set_max_results(int p_max_results) {
+ max_results = p_max_results;
+}
+
+int ShapeCast3D::get_max_results() const {
+ return max_results;
+}
+
+void ShapeCast3D::set_collision_mask(uint32_t p_mask) {
+ collision_mask = p_mask;
+}
+
+uint32_t ShapeCast3D::get_collision_mask() const {
+ return collision_mask;
+}
+
+void ShapeCast3D::set_collision_mask_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_collision_mask();
+ if (p_value) {
+ mask |= 1 << (p_layer_number - 1);
+ } else {
+ mask &= ~(1 << (p_layer_number - 1));
+ }
+ set_collision_mask(mask);
+}
+
+bool ShapeCast3D::get_collision_mask_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_collision_mask() & (1 << (p_layer_number - 1));
+}
+
+int ShapeCast3D::get_collision_count() const {
+ return result.size();
+}
+
+bool ShapeCast3D::is_colliding() const {
+ return collided;
+}
+
+Object *ShapeCast3D::get_collider(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), nullptr, "No collider found.");
+
+ if (result[p_idx].collider_id.is_null()) {
+ return nullptr;
+ }
+ return ObjectDB::get_instance(result[p_idx].collider_id);
+}
+
+int ShapeCast3D::get_collider_shape(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), -1, "No collider shape found.");
+ return result[p_idx].shape;
+}
+
+Vector3 ShapeCast3D::get_collision_point(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), Vector3(), "No collision point found.");
+ return result[p_idx].point;
+}
+
+Vector3 ShapeCast3D::get_collision_normal(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), Vector3(), "No collision normal found.");
+ return result[p_idx].normal;
+}
+
+real_t ShapeCast3D::get_closest_collision_safe_fraction() const {
+ return collision_safe_fraction;
+}
+
+real_t ShapeCast3D::get_closest_collision_unsafe_fraction() const {
+ return collision_unsafe_fraction;
+}
+
+void ShapeCast3D::resource_changed(Ref<Resource> p_res) {
+ if (is_inside_tree()) {
+ _update_debug_shape();
+ }
+ update_gizmos();
+}
+
+void ShapeCast3D::set_shape(const Ref<Shape3D> &p_shape) {
+ if (p_shape == shape) {
+ return;
+ }
+ if (!shape.is_null()) {
+ shape->unregister_owner(this);
+ }
+ shape = p_shape;
+ if (!shape.is_null()) {
+ shape->register_owner(this);
+ }
+ if (p_shape.is_valid()) {
+ shape_rid = shape->get_rid();
+ }
+
+ if (is_inside_tree()) {
+ _update_debug_shape();
+ }
+
+ update_gizmos();
+ update_configuration_warnings();
+}
+
+Ref<Shape3D> ShapeCast3D::get_shape() const {
+ return shape;
+}
+
+void ShapeCast3D::set_exclude_parent_body(bool p_exclude_parent_body) {
+ if (exclude_parent_body == p_exclude_parent_body) {
+ return;
+ }
+ exclude_parent_body = p_exclude_parent_body;
+
+ if (!is_inside_tree()) {
+ return;
+ }
+ if (Object::cast_to<CollisionObject3D>(get_parent())) {
+ if (exclude_parent_body) {
+ exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid());
+ } else {
+ exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid());
+ }
+ }
+}
+
+bool ShapeCast3D::get_exclude_parent_body() const {
+ return exclude_parent_body;
+}
+
+void ShapeCast3D::_update_shapecast_state() {
+ result.clear();
+
+ ERR_FAIL_COND_MSG(shape.is_null(), "Null reference to shape. ShapeCast3D requires a Shape3D to sweep for collisions.");
+
+ Ref<World3D> w3d = get_world_3d();
+ ERR_FAIL_COND(w3d.is_null());
+
+ PhysicsDirectSpaceState3D *dss = PhysicsServer3D::get_singleton()->space_get_direct_state(w3d->get_space());
+ ERR_FAIL_COND(!dss);
+
+ Transform3D gt = get_global_transform();
+
+ PhysicsDirectSpaceState3D::ShapeParameters params;
+ params.shape_rid = shape_rid;
+ params.transform = gt;
+ params.motion = gt.basis.xform(target_position);
+ params.margin = margin;
+ params.exclude = exclude;
+ params.collision_mask = collision_mask;
+ params.collide_with_bodies = collide_with_bodies;
+ params.collide_with_areas = collide_with_areas;
+
+ collision_safe_fraction = 0.0;
+ collision_unsafe_fraction = 0.0;
+
+ if (target_position != Vector3()) {
+ dss->cast_motion(params, collision_safe_fraction, collision_unsafe_fraction);
+ if (collision_unsafe_fraction < 1.0) {
+ // Move shape transform to the point of impact,
+ // so we can collect contact info at that point.
+ gt.set_origin(gt.get_origin() + params.motion * (collision_unsafe_fraction + CMP_EPSILON));
+ params.transform = gt;
+ }
+ }
+ // Regardless of whether the shape is stuck or it's moved along
+ // the motion vector, we'll only consider static collisions from now on.
+ params.motion = Vector3();
+
+ bool intersected = true;
+ while (intersected && result.size() < max_results) {
+ PhysicsDirectSpaceState3D::ShapeRestInfo info;
+ intersected = dss->rest_info(params, &info);
+ if (intersected) {
+ result.push_back(info);
+ params.exclude.insert(info.rid);
+ }
+ }
+ collided = !result.is_empty();
+}
+
+void ShapeCast3D::force_shapecast_update() {
+ _update_shapecast_state();
+}
+
+void ShapeCast3D::add_exception_rid(const RID &p_rid) {
+ exclude.insert(p_rid);
+}
+
+void ShapeCast3D::add_exception(const Object *p_object) {
+ ERR_FAIL_NULL(p_object);
+ const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
+ if (!co) {
+ return;
+ }
+ add_exception_rid(co->get_rid());
+}
+
+void ShapeCast3D::remove_exception_rid(const RID &p_rid) {
+ exclude.erase(p_rid);
+}
+
+void ShapeCast3D::remove_exception(const Object *p_object) {
+ ERR_FAIL_NULL(p_object);
+ const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
+ if (!co) {
+ return;
+ }
+ remove_exception_rid(co->get_rid());
+}
+
+void ShapeCast3D::clear_exceptions() {
+ exclude.clear();
+}
+
+void ShapeCast3D::set_collide_with_areas(bool p_clip) {
+ collide_with_areas = p_clip;
+}
+
+bool ShapeCast3D::is_collide_with_areas_enabled() const {
+ return collide_with_areas;
+}
+
+void ShapeCast3D::set_collide_with_bodies(bool p_clip) {
+ collide_with_bodies = p_clip;
+}
+
+bool ShapeCast3D::is_collide_with_bodies_enabled() const {
+ return collide_with_bodies;
+}
+
+Array ShapeCast3D::_get_collision_result() const {
+ Array ret;
+
+ for (int i = 0; i < result.size(); ++i) {
+ const PhysicsDirectSpaceState3D::ShapeRestInfo &sri = result[i];
+
+ Dictionary col;
+ col["point"] = sri.point;
+ col["normal"] = sri.normal;
+ col["rid"] = sri.rid;
+ col["collider"] = ObjectDB::get_instance(sri.collider_id);
+ col["collider_id"] = sri.collider_id;
+ col["shape"] = sri.shape;
+ col["linear_velocity"] = sri.linear_velocity;
+
+ ret.push_back(col);
+ }
+ return ret;
+}
+
+void ShapeCast3D::_update_debug_shape_vertices() {
+ debug_shape_vertices.clear();
+ debug_line_vertices.clear();
+
+ if (!shape.is_null()) {
+ debug_shape_vertices.append_array(shape->get_debug_mesh_lines());
+ for (int i = 0; i < debug_shape_vertices.size(); i++) {
+ debug_shape_vertices.set(i, debug_shape_vertices[i] + Vector3(target_position * get_closest_collision_safe_fraction()));
+ }
+ }
+
+ if (target_position == Vector3()) {
+ return;
+ }
+
+ debug_line_vertices.push_back(Vector3());
+ debug_line_vertices.push_back(target_position);
+}
+
+const Vector<Vector3> &ShapeCast3D::get_debug_shape_vertices() const {
+ return debug_shape_vertices;
+}
+
+const Vector<Vector3> &ShapeCast3D::get_debug_line_vertices() const {
+ return debug_line_vertices;
+}
+
+void ShapeCast3D::set_debug_shape_custom_color(const Color &p_color) {
+ debug_shape_custom_color = p_color;
+ if (debug_material.is_valid()) {
+ _update_debug_shape_material();
+ }
+}
+
+Ref<StandardMaterial3D> ShapeCast3D::get_debug_material() {
+ _update_debug_shape_material();
+ return debug_material;
+}
+
+const Color &ShapeCast3D::get_debug_shape_custom_color() const {
+ return debug_shape_custom_color;
+}
+
+void ShapeCast3D::_create_debug_shape() {
+ _update_debug_shape_material();
+
+ Ref<ArrayMesh> mesh = memnew(ArrayMesh);
+
+ MeshInstance3D *mi = memnew(MeshInstance3D);
+ mi->set_mesh(mesh);
+
+ add_child(mi);
+ debug_shape = mi;
+}
+
+void ShapeCast3D::_update_debug_shape_material(bool p_check_collision) {
+ if (!debug_material.is_valid()) {
+ Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
+ debug_material = material;
+
+ material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ // Use double-sided rendering so that the RayCast can be seen if the camera is inside.
+ material->set_cull_mode(BaseMaterial3D::CULL_DISABLED);
+ material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);
+ }
+
+ Color color = debug_shape_custom_color;
+ if (color == Color(0.0, 0.0, 0.0)) {
+ // Use the default debug shape color defined in the Project Settings.
+ color = get_tree()->get_debug_collisions_color();
+ }
+
+ if (p_check_collision && collided) {
+ if ((color.get_h() < 0.055 || color.get_h() > 0.945) && color.get_s() > 0.5 && color.get_v() > 0.5) {
+ // If base color is already quite reddish, highlight collision with green color
+ color = Color(0.0, 1.0, 0.0, color.a);
+ } else {
+ // Else, highlight collision with red color
+ color = Color(1.0, 0, 0, color.a);
+ }
+ }
+
+ Ref<StandardMaterial3D> material = static_cast<Ref<StandardMaterial3D>>(debug_material);
+ material->set_albedo(color);
+}
+
+void ShapeCast3D::_update_debug_shape() {
+ if (!enabled) {
+ return;
+ }
+
+ if (!debug_shape) {
+ _create_debug_shape();
+ }
+
+ MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape);
+ Ref<ArrayMesh> mesh = mi->get_mesh();
+ if (!mesh.is_valid()) {
+ return;
+ }
+
+ _update_debug_shape_vertices();
+
+ mesh->clear_surfaces();
+
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+
+ uint32_t flags = 0;
+ int surface_count = 0;
+
+ if (!debug_shape_vertices.is_empty()) {
+ a[Mesh::ARRAY_VERTEX] = debug_shape_vertices;
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags);
+ mesh->surface_set_material(surface_count, debug_material);
+ ++surface_count;
+ }
+
+ if (!debug_line_vertices.is_empty()) {
+ a[Mesh::ARRAY_VERTEX] = debug_line_vertices;
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags);
+ mesh->surface_set_material(surface_count, debug_material);
+ ++surface_count;
+ }
+}
+
+void ShapeCast3D::_clear_debug_shape() {
+ if (!debug_shape) {
+ return;
+ }
+
+ MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape);
+ if (mi->is_inside_tree()) {
+ mi->queue_delete();
+ } else {
+ memdelete(mi);
+ }
+
+ debug_shape = nullptr;
+}
+
+ShapeCast3D::~ShapeCast3D() {
+ if (!shape.is_null()) {
+ shape->unregister_owner(this);
+ }
+}
diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h
new file mode 100644
index 0000000000..5bda15e4b0
--- /dev/null
+++ b/scene/3d/shape_cast_3d.h
@@ -0,0 +1,142 @@
+/*************************************************************************/
+/* shape_cast_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SHAPE_CAST_3D_H
+#define SHAPE_CAST_3D_H
+
+#include "scene/3d/node_3d.h"
+#include "scene/resources/shape_3d.h"
+
+class ShapeCast3D : public Node3D {
+ GDCLASS(ShapeCast3D, Node3D);
+
+ bool enabled = true;
+ void resource_changed(Ref<Resource> p_res);
+
+ Ref<Shape3D> shape;
+ RID shape_rid;
+ Vector3 target_position = Vector3(0, -1, 0);
+
+ HashSet<RID> exclude;
+ real_t margin = 0.0;
+ uint32_t collision_mask = 1;
+ bool exclude_parent_body = true;
+ bool collide_with_areas = false;
+ bool collide_with_bodies = true;
+
+ Node *debug_shape = nullptr;
+ Ref<Material> debug_material;
+ Color debug_shape_custom_color = Color(0.0, 0.0, 0.0);
+ Vector<Vector3> debug_shape_vertices;
+ Vector<Vector3> debug_line_vertices;
+
+ void _create_debug_shape();
+ void _update_debug_shape();
+ void _update_debug_shape_material(bool p_check_collision = false);
+ void _update_debug_shape_vertices();
+ void _clear_debug_shape();
+
+ // Result
+ int max_results = 32;
+ Vector<PhysicsDirectSpaceState3D::ShapeRestInfo> result;
+ bool collided = false;
+ real_t collision_safe_fraction = 1.0;
+ real_t collision_unsafe_fraction = 1.0;
+
+ Array _get_collision_result() const;
+
+ ~ShapeCast3D();
+
+protected:
+ void _notification(int p_what);
+ void _update_shapecast_state();
+ static void _bind_methods();
+
+public:
+ void set_collide_with_areas(bool p_clip);
+ bool is_collide_with_areas_enabled() const;
+
+ void set_collide_with_bodies(bool p_clip);
+ bool is_collide_with_bodies_enabled() const;
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+
+ void set_shape(const Ref<Shape3D> &p_shape);
+ Ref<Shape3D> get_shape() const;
+
+ void set_target_position(const Vector3 &p_point);
+ Vector3 get_target_position() const;
+
+ void set_margin(real_t p_margin);
+ real_t get_margin() const;
+
+ void set_max_results(int p_max_results);
+ int get_max_results() const;
+
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_collision_mask_value(int p_layer_number, bool p_value);
+ bool get_collision_mask_value(int p_layer_number) const;
+
+ void set_exclude_parent_body(bool p_exclude_parent_body);
+ bool get_exclude_parent_body() const;
+
+ const Color &get_debug_shape_custom_color() const;
+ void set_debug_shape_custom_color(const Color &p_color);
+
+ const Vector<Vector3> &get_debug_shape_vertices() const;
+ const Vector<Vector3> &get_debug_line_vertices() const;
+
+ Ref<StandardMaterial3D> get_debug_material();
+
+ int get_collision_count() const;
+ Object *get_collider(int p_idx) const;
+ int get_collider_shape(int p_idx) const;
+ Vector3 get_collision_point(int p_idx) const;
+ Vector3 get_collision_normal(int p_idx) const;
+
+ real_t get_closest_collision_safe_fraction() const;
+ real_t get_closest_collision_unsafe_fraction() const;
+
+ void force_shapecast_update();
+ bool is_colliding() const;
+
+ void add_exception_rid(const RID &p_rid);
+ void add_exception(const Object *p_object);
+ void remove_exception_rid(const RID &p_rid);
+ void remove_exception(const Object *p_object);
+ void clear_exceptions();
+
+ virtual TypedArray<String> get_configuration_warnings() const override;
+};
+
+#endif // SHAPE_CAST_3D_H
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index b342660b85..4c38fccc8b 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -493,6 +493,19 @@ int Skeleton3D::get_bone_axis_forward_enum(int p_bone) {
return bones[p_bone].rest_bone_forward_axis;
}
+void Skeleton3D::set_motion_scale(float p_motion_scale) {
+ if (p_motion_scale <= 0) {
+ motion_scale = 1;
+ ERR_FAIL_MSG("Motion scale must be larger than 0.");
+ }
+ motion_scale = p_motion_scale;
+}
+
+float Skeleton3D::get_motion_scale() const {
+ ERR_FAIL_COND_V(motion_scale <= 0, 1);
+ return motion_scale;
+}
+
// Skeleton creation api
void Skeleton3D::add_bone(const String &p_name) {
@@ -1255,6 +1268,9 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms);
ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms);
+ ClassDB::bind_method(D_METHOD("set_motion_scale", "motion_scale"), &Skeleton3D::set_motion_scale);
+ ClassDB::bind_method(D_METHOD("get_motion_scale"), &Skeleton3D::get_motion_scale);
+
// Helper functions
ClassDB::bind_method(D_METHOD("global_pose_to_world_transform", "global_pose"), &Skeleton3D::global_pose_to_world_transform);
ClassDB::bind_method(D_METHOD("world_transform_to_global_pose", "world_transform"), &Skeleton3D::world_transform_to_global_pose);
@@ -1278,15 +1294,13 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton3D::get_modification_stack);
ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications);
-#ifndef _3D_DISABLED
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater"), "set_motion_scale", "get_motion_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only");
+#ifndef _3D_DISABLED
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones");
#endif // _3D_DISABLED
-#ifdef TOOLS_ENABLED
ADD_SIGNAL(MethodInfo("pose_updated"));
-#endif // TOOLS_ENABLED
-
ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx")));
ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx")));
ADD_SIGNAL(MethodInfo("show_rest_only_changed"));
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index cb4c82d232..8b69410a39 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -146,6 +146,7 @@ private:
bool rest_dirty = false;
bool show_rest_only = false;
+ float motion_scale = 1.0;
uint64_t version = 1;
@@ -211,6 +212,9 @@ public:
bool is_show_rest_only() const;
void clear_bones();
+ void set_motion_scale(float p_motion_scale);
+ float get_motion_scale() const;
+
// posing api
void set_bone_pose_position(int p_bone, const Vector3 &p_position);
@@ -288,4 +292,4 @@ public:
~Skeleton3D();
};
-#endif
+#endif // SKELETON_3D_H
diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h
index 0f656187de..6ae86a2bf6 100644
--- a/scene/3d/skeleton_ik_3d.h
+++ b/scene/3d/skeleton_ik_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETON_IK_H
-#define SKELETON_IK_H
+#ifndef SKELETON_IK_3D_H
+#define SKELETON_IK_3D_H
#ifndef _3D_DISABLED
@@ -192,4 +192,4 @@ private:
#endif // _3D_DISABLED
-#endif // SKELETON_IK_H
+#endif // SKELETON_IK_3D_H
diff --git a/scene/3d/soft_dynamic_body_3d.h b/scene/3d/soft_dynamic_body_3d.h
index e11e5c73df..04f3365f72 100644
--- a/scene/3d/soft_dynamic_body_3d.h
+++ b/scene/3d/soft_dynamic_body_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SOFT_DYNAMIC_BODY_H
-#define SOFT_DYNAMIC_BODY_H
+#ifndef SOFT_DYNAMIC_BODY_3D_H
+#define SOFT_DYNAMIC_BODY_3D_H
#include "scene/3d/mesh_instance_3d.h"
#include "servers/physics_server_3d.h"
@@ -199,4 +199,4 @@ private:
VARIANT_ENUM_CAST(SoftDynamicBody3D::DisableMode);
-#endif // SOFT_DYNAMIC_BODY_H
+#endif // SOFT_DYNAMIC_BODY_3D_H
diff --git a/scene/3d/spring_arm_3d.h b/scene/3d/spring_arm_3d.h
index 0b5307acf7..1a6f03abe4 100644
--- a/scene/3d/spring_arm_3d.h
+++ b/scene/3d/spring_arm_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SPRING_ARM_H
-#define SPRING_ARM_H
+#ifndef SPRING_ARM_3D_H
+#define SPRING_ARM_3D_H
#include "scene/3d/node_3d.h"
@@ -68,4 +68,4 @@ private:
void process_spring();
};
-#endif
+#endif // SPRING_ARM_3D_H
diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h
index 0ef8bd7482..2f3a37af2a 100644
--- a/scene/3d/vehicle_body_3d.h
+++ b/scene/3d/vehicle_body_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VEHICLE_BODY_H
-#define VEHICLE_BODY_H
+#ifndef VEHICLE_BODY_3D_H
+#define VEHICLE_BODY_3D_H
#include "scene/3d/physics_body_3d.h"
@@ -210,4 +210,4 @@ public:
VehicleBody3D();
};
-#endif // VEHICLE_BODY_H
+#endif // VEHICLE_BODY_3D_H
diff --git a/scene/3d/velocity_tracker_3d.h b/scene/3d/velocity_tracker_3d.h
index 7fdcacc9c1..6b27cdffc2 100644
--- a/scene/3d/velocity_tracker_3d.h
+++ b/scene/3d/velocity_tracker_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SPATIAL_VELOCITY_TRACKER_H
-#define SPATIAL_VELOCITY_TRACKER_H
+#ifndef VELOCITY_TRACKER_3D_H
+#define VELOCITY_TRACKER_3D_H
#include "scene/3d/node_3d.h"
@@ -58,4 +58,4 @@ public:
VelocityTracker3D();
};
-#endif // SPATIAL_VELOCITY_TRACKER_H
+#endif // VELOCITY_TRACKER_3D_H
diff --git a/scene/3d/visible_on_screen_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h
index fe17f1e444..60461569f4 100644
--- a/scene/3d/visible_on_screen_notifier_3d.h
+++ b/scene/3d/visible_on_screen_notifier_3d.h
@@ -96,4 +96,4 @@ public:
VARIANT_ENUM_CAST(VisibleOnScreenEnabler3D::EnableMode);
-#endif // VISIBILITY_NOTIFIER_H
+#endif // VISIBLE_ON_SCREEN_NOTIFIER_3D_H
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 69917f6992..e76e85cfef 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -461,15 +461,16 @@ void GeometryInstance3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01,suffix:m"), "set_extra_cull_margin", "get_extra_cull_margin");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling");
+
ADD_GROUP("Global Illumination", "gi_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)"), "set_gi_mode", "get_gi_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale");
ADD_GROUP("Visibility Range", "visibility_range_");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_begin", "get_visibility_range_begin");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_end", "get_visibility_range_end");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_end_margin", "get_visibility_range_end_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin", "get_visibility_range_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_end", "get_visibility_range_end");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_end_margin", "get_visibility_range_end_margin");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_range_fade_mode", PROPERTY_HINT_ENUM, "Disabled,Self,Dependencies"), "set_visibility_range_fade_mode", "get_visibility_range_fade_mode");
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF);
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index 9e0d9b9a2a..159b14613c 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VISUAL_INSTANCE_H
-#define VISUAL_INSTANCE_H
+#ifndef VISUAL_INSTANCE_3D_H
+#define VISUAL_INSTANCE_3D_H
#include "scene/3d/node_3d.h"
@@ -196,4 +196,4 @@ VARIANT_ENUM_CAST(GeometryInstance3D::LightmapScale);
VARIANT_ENUM_CAST(GeometryInstance3D::GIMode);
VARIANT_ENUM_CAST(GeometryInstance3D::VisibilityRangeFadeMode);
-#endif
+#endif // VISUAL_INSTANCE_3D_H
diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h
index 0179795ddc..68bce768b7 100644
--- a/scene/3d/voxelizer.h
+++ b/scene/3d/voxelizer.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VOXEL_LIGHT_BAKER_H
-#define VOXEL_LIGHT_BAKER_H
+#ifndef VOXELIZER_H
+#define VOXELIZER_H
#include "scene/resources/multimesh.h"
@@ -129,4 +129,4 @@ public:
Voxelizer();
};
-#endif // VOXEL_LIGHT_BAKER_H
+#endif // VOXELIZER_H
diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h
index 8dbb57364c..9955aa72a8 100644
--- a/scene/3d/world_environment.h
+++ b/scene/3d/world_environment.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCENARIO_FX_H
-#define SCENARIO_FX_H
+#ifndef WORLD_ENVIRONMENT_H
+#define WORLD_ENVIRONMENT_H
#include "scene/main/node.h"
#include "scene/resources/camera_effects.h"
@@ -60,4 +60,4 @@ public:
WorldEnvironment();
};
-#endif
+#endif // WORLD_ENVIRONMENT_H
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index 1dad6078b4..40a43043c6 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -120,7 +120,7 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
Vector3 ray;
// Just use the first view, if multiple views are supported this function has no good result
- CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+ Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
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, -get_near()).normalized();
@@ -143,7 +143,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const {
Size2 viewport_size = get_viewport()->get_visible_rect().size;
// Just use the first view, if multiple views are supported this function has no good result
- CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+ Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
Plane p(get_camera_transform().xform_inv(p_pos), 1.0);
@@ -173,7 +173,7 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) co
Size2 viewport_size = get_viewport()->get_visible_rect().size;
// Just use the first view, if multiple views are supported this function has no good result
- CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+ Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
Vector2 vp_he = cm.get_viewport_half_extents();
@@ -202,7 +202,7 @@ Vector<Plane> XRCamera3D::get_frustum() const {
Size2 viewport_size = get_viewport()->get_visible_rect().size;
// TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here.
- CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+ Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
return cm.get_projection_planes(get_camera_transform());
};
diff --git a/scene/SCsub b/scene/SCsub
index a7b23af598..92288211bb 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -9,7 +9,6 @@ env.add_source_files(env.scene_sources, "*.cpp")
# Chain load SCsubs
SConscript("main/SCsub")
-SConscript("multiplayer/SCsub")
SConscript("gui/SCsub")
if not env["disable_3d"]:
SConscript("3d/SCsub")
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index d9a5adc883..b42e426f51 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -121,7 +121,7 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_
blend_points[p_at_index].node = p_node;
blend_points[p_at_index].position = p_position;
- blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
blend_points_used++;
emit_signal(SNAME("tree_changed"));
@@ -142,7 +142,7 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim
}
blend_points[p_point].node = p_node;
- blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
emit_signal(SNAME("tree_changed"));
}
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index 0f77befd9d..6b5851a977 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -80,7 +80,7 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_
blend_points[p_at_index].node = p_node;
blend_points[p_at_index].position = p_position;
- blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
blend_points_used++;
_queue_auto_triangles();
@@ -102,7 +102,7 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
}
blend_points[p_point].node = p_node;
- blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
emit_signal(SNAME("tree_changed"));
}
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index d0aac931c0..fe2fb1b7a1 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -844,8 +844,8 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
emit_changed();
emit_signal(SNAME("tree_changed"));
- p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
- p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed), varray(p_name), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED);
}
Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const {
@@ -945,7 +945,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN
}
}
//connection must be done with new name
- nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed), varray(p_new_name), CONNECT_REFERENCE_COUNTED);
+ nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED);
emit_signal(SNAME("tree_changed"));
}
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index fbe23bedad..3a45d9f26c 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -723,7 +723,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
emit_changed();
emit_signal(SNAME("tree_changed"));
- p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
}
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
@@ -743,7 +743,7 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
emit_changed();
emit_signal(SNAME("tree_changed"));
- p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
}
bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const {
@@ -1032,7 +1032,7 @@ void AnimationNodeStateMachine::add_transition(const StringName &p_from, const S
tr.local_to = local_to;
tr.transition = p_transition;
- tr.transition->connect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ tr.transition->connect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
transitions.push_back(tr);
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 2e87dbf9da..636c9e26a5 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -64,7 +64,7 @@ void AnimatedValuesBackup::restore() const {
if (arr.size() == 3) {
Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]);
Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]);
- Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[0]);
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[2]);
}
}
}
@@ -325,7 +325,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
{
if (!child->is_connected("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed))) {
- child->connect("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed), make_binds(child), CONNECT_ONESHOT);
+ child->connect("tree_exiting", callable_mp(this, &AnimationPlayer::_node_removed).bind(child), CONNECT_ONESHOT);
}
}
@@ -370,6 +370,10 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
node_cache->node_3d = nullptr;
ERR_CONTINUE(node_cache->bone_idx < 0);
}
+ Transform3D rest = node_cache->skeleton->get_bone_rest(bone_idx);
+ node_cache->init_loc = rest.origin;
+ node_cache->init_rot = rest.basis.get_rotation_quaternion();
+ node_cache->init_scale = rest.basis.get_scale();
} else {
// no property, just use spatialnode
node_cache->skeleton = nullptr;
@@ -450,6 +454,23 @@ static void _call_object(Object *p_object, const StringName &p_method, const Vec
}
}
+Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) {
+ switch (p_anim->track_get_type(p_track)) {
+#ifndef _3D_DISABLED
+ case Animation::TYPE_POSITION_3D: {
+ if (p_object_idx >= 0) {
+ const Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_object);
+ return Vector3(p_value) * skel->get_motion_scale();
+ }
+ return p_value;
+ } break;
+#endif // _3D_DISABLED
+ default: {
+ } break;
+ }
+ return p_value;
+}
+
void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, int p_pingponged) {
_ensure_node_caches(p_anim);
ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count());
@@ -494,14 +515,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (err != OK) {
continue;
}
+ loc = _post_process_key_value(a, i, loc, nc->node_3d, nc->bone_idx);
if (nc->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
cache_update[cache_update_size++] = nc;
nc->accum_pass = accum_pass;
nc->loc_accum = loc;
- nc->rot_accum = Quaternion();
- nc->scale_accum = Vector3();
+ nc->rot_accum = nc->init_rot;
+ nc->scale_accum = nc->init_scale;
} else {
nc->loc_accum = nc->loc_accum.lerp(loc, p_interp);
}
@@ -521,14 +543,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (err != OK) {
continue;
}
+ rot = _post_process_key_value(a, i, rot, nc->node_3d, nc->bone_idx);
if (nc->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
cache_update[cache_update_size++] = nc;
nc->accum_pass = accum_pass;
- nc->loc_accum = Vector3();
+ nc->loc_accum = nc->init_loc;
nc->rot_accum = rot;
- nc->scale_accum = Vector3();
+ nc->scale_accum = nc->init_scale;
} else {
nc->rot_accum = nc->rot_accum.slerp(rot, p_interp);
}
@@ -548,13 +571,14 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (err != OK) {
continue;
}
+ scale = _post_process_key_value(a, i, scale, nc->node_3d, nc->bone_idx);
if (nc->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
cache_update[cache_update_size++] = nc;
nc->accum_pass = accum_pass;
- nc->loc_accum = Vector3();
- nc->rot_accum = Quaternion();
+ nc->loc_accum = nc->init_loc;
+ nc->rot_accum = nc->init_rot;
nc->scale_accum = scale;
} else {
nc->scale_accum = nc->scale_accum.lerp(scale, p_interp);
@@ -575,6 +599,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (err != OK) {
continue;
}
+ blend = _post_process_key_value(a, i, blend, nc->node_blend_shape, nc->blend_shape_idx);
if (nc->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
@@ -627,9 +652,9 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (p_time < first_key_time) {
double c = Math::ease(p_time / first_key_time, transition);
Variant first_value = a->track_get_key_value(i, first_key);
+ first_value = _post_process_key_value(a, i, first_value, nc->node);
Variant interp_value;
Variant::interpolate(pa->capture, first_value, c, interp_value);
-
if (pa->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX);
cache_update_prop[cache_update_prop_size++] = pa;
@@ -649,6 +674,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (value == Variant()) {
continue;
}
+ value = _post_process_key_value(a, i, value, nc->node);
if (pa->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX);
@@ -665,6 +691,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
+ value = _post_process_key_value(a, i, value, nc->node);
switch (pa->special) {
case SP_NONE: {
bool valid;
@@ -749,6 +776,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
TrackNodeCache::BezierAnim *ba = &E->value;
real_t bezier = a->bezier_track_interpolate(i, p_time);
+ bezier = _post_process_key_value(a, i, bezier, nc->node);
if (ba->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX);
cache_update_bezier[cache_update_bezier_size++] = ba;
@@ -1173,11 +1201,15 @@ void AnimationPlayer::_animation_process(double p_delta) {
emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name);
}
} else {
- //stop();
playing = false;
_set_process(false);
if (end_notify) {
emit_signal(SceneStringNames::get_singleton()->animation_finished, playback.assigned);
+
+ if (movie_quit_on_finish && OS::get_singleton()->has_feature("movie")) {
+ print_line(vformat("Movie Maker mode is enabled. Quitting on animation finish as requested by: %s", get_path()));
+ get_tree()->quit();
+ }
}
}
end_reached = false;
@@ -1345,9 +1377,9 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref
animation_libraries.insert(insert_pos, ald);
- ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_name));
- ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_name));
- ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed), varray(p_name));
+ ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_name));
+ ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_name));
+ ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_name));
_animation_set_cache_update();
@@ -1385,7 +1417,7 @@ void AnimationPlayer::remove_animation_library(const StringName &p_name) {
}
void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) {
- Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), CONNECT_REFERENCE_COUNTED);
}
void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) {
@@ -1411,9 +1443,9 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S
animation_libraries[i].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added));
animation_libraries[i].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed));
- animation_libraries[i].library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_new_name));
- animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_new_name));
- animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed), varray(p_new_name));
+ animation_libraries[i].library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name));
+ animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name));
+ animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name));
for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) {
StringName old_name = p_name == StringName() ? K.key : StringName(String(p_name) + "/" + String(K.key));
@@ -1864,6 +1896,14 @@ AnimationPlayer::AnimationMethodCallMode AnimationPlayer::get_method_call_mode()
return method_call_mode;
}
+void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) {
+ movie_quit_on_finish = p_enabled;
+}
+
+bool AnimationPlayer::is_movie_quit_on_finish_enabled() const {
+ return movie_quit_on_finish;
+}
+
void AnimationPlayer::_set_process(bool p_process, bool p_force) {
if (processing == p_process && !p_force) {
return;
@@ -2084,6 +2124,9 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode);
ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode);
+ ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled);
+ ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled);
+
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
@@ -2105,6 +2148,8 @@ void AnimationPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled");
+
ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING_NAME, "anim_name")));
ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name")));
ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name")));
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index d3eb37a345..b6d8dab1ed 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -109,6 +109,9 @@ private:
bool loc_used = false;
bool rot_used = false;
bool scale_used = false;
+ Vector3 init_loc = Vector3(0, 0, 0);
+ Quaternion init_rot = Quaternion(0, 0, 0, 1);
+ Vector3 init_scale = Vector3(1, 1, 1);
Vector3 loc_accum;
Quaternion rot_accum;
@@ -259,6 +262,7 @@ private:
bool reset_on_save = true;
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
AnimationMethodCallMode method_call_mode = ANIMATION_METHOD_CALL_DEFERRED;
+ bool movie_quit_on_finish = false;
bool processing = false;
bool active = true;
@@ -313,6 +317,8 @@ protected:
static void _bind_methods();
+ virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1);
+
public:
StringName find_animation(const Ref<Animation> &p_animation) const;
StringName find_animation_library(const Ref<Animation> &p_animation) const;
@@ -368,6 +374,9 @@ public:
void set_method_call_mode(AnimationMethodCallMode p_mode);
AnimationMethodCallMode get_method_call_mode() const;
+ void set_movie_quit_on_finish_enabled(bool p_enabled);
+ bool is_movie_quit_on_finish_enabled() const;
+
void seek(double p_time, bool p_update = false);
void seek_delta(double p_time, float p_delta);
float get_current_animation_position() const;
@@ -395,4 +404,4 @@ public:
VARIANT_ENUM_CAST(AnimationPlayer::AnimationProcessCallback);
VARIANT_ENUM_CAST(AnimationPlayer::AnimationMethodCallMode);
-#endif
+#endif // ANIMATION_PLAYER_H
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 4b80f571ae..14cf64afad 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -589,7 +589,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
if (!child->is_connected("tree_exited", callable_mp(this, &AnimationTree::_node_removed))) {
- child->connect("tree_exited", callable_mp(this, &AnimationTree::_node_removed), varray(child));
+ child->connect("tree_exited", callable_mp(this, &AnimationTree::_node_removed).bind(child));
}
switch (track_type) {
@@ -1054,7 +1054,9 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
a->position_track_interpolate(i, (double)a->get_length(), &loc[1]);
+ loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
t->loc += (loc[1] - loc[0]) * blend;
prev_time = 0;
}
@@ -1064,7 +1066,9 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
a->position_track_interpolate(i, 0, &loc[1]);
+ loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
t->loc += (loc[1] - loc[0]) * blend;
prev_time = (double)a->get_length();
}
@@ -1074,8 +1078,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
a->position_track_interpolate(i, time, &loc[1]);
+ loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
t->loc += (loc[1] - loc[0]) * blend;
prev_time = !backward ? 0 : (double)a->get_length();
@@ -1092,6 +1098,7 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ loc = _post_process_key_value(a, i, loc, t->object, t->bone_idx);
t->loc += (loc - t->init_loc) * blend;
}
@@ -1150,7 +1157,9 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
+ rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = 0;
}
@@ -1160,6 +1169,7 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
a->rotation_track_interpolate(i, 0, &rot[1]);
t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = (double)a->get_length();
@@ -1170,8 +1180,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
a->rotation_track_interpolate(i, time, &rot[1]);
+ rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = !backward ? 0 : (double)a->get_length();
@@ -1188,6 +1200,7 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ rot = _post_process_key_value(a, i, rot, t->object, t->bone_idx);
t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized();
}
@@ -1246,8 +1259,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]);
t->scale += (scale[1] - scale[0]) * blend;
+ scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
prev_time = 0;
}
} else {
@@ -1256,7 +1271,9 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
a->scale_track_interpolate(i, 0, &scale[1]);
+ scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
t->scale += (scale[1] - scale[0]) * blend;
prev_time = (double)a->get_length();
}
@@ -1266,8 +1283,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
a->scale_track_interpolate(i, time, &scale[1]);
+ scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
t->scale += (scale[1] - scale[0]) * blend;
prev_time = !backward ? 0 : (double)a->get_length();
@@ -1284,6 +1303,7 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ scale = _post_process_key_value(a, i, scale, t->object, t->bone_idx);
t->scale += (scale - t->init_scale) * blend;
}
@@ -1306,6 +1326,7 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
+ value = _post_process_key_value(a, i, value, t->object, t->shape_index);
t->value += (value - t->init_value) * blend;
#endif // _3D_DISABLED
@@ -1317,6 +1338,7 @@ void AnimationTree::_process_graph(double p_delta) {
if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) {
Variant value = a->value_track_interpolate(i, time);
+ value = _post_process_key_value(a, i, value, t->object);
if (value == Variant()) {
continue;
@@ -1344,12 +1366,14 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
Variant value = a->track_get_key_value(i, idx);
+ value = _post_process_key_value(a, i, value, t->object);
t->object->set_indexed(t->subpath, value);
} else {
List<int> indices;
a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
+ value = _post_process_key_value(a, i, value, t->object);
t->object->set_indexed(t->subpath, value);
}
}
@@ -1388,6 +1412,7 @@ void AnimationTree::_process_graph(double p_delta) {
TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
real_t bezier = a->bezier_track_interpolate(i, time);
+ bezier = _post_process_key_value(a, i, bezier, t->object);
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
@@ -1663,6 +1688,23 @@ void AnimationTree::_process_graph(double p_delta) {
}
}
+Variant AnimationTree::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) {
+ switch (p_anim->track_get_type(p_track)) {
+#ifndef _3D_DISABLED
+ case Animation::TYPE_POSITION_3D: {
+ if (p_object_idx >= 0) {
+ const Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_object);
+ return Vector3(p_value) * skel->get_motion_scale();
+ }
+ return p_value;
+ } break;
+#endif // _3D_DISABLED
+ default: {
+ } break;
+ }
+ return p_value;
+}
+
void AnimationTree::advance(real_t p_time) {
_process_graph(p_time);
}
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 4f9a330a89..99851d140e 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ANIMATION_GRAPH_PLAYER_H
-#define ANIMATION_GRAPH_PLAYER_H
+#ifndef ANIMATION_TREE_H
+#define ANIMATION_TREE_H
#include "animation_player.h"
#include "scene/3d/node_3d.h"
@@ -321,6 +321,8 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1);
+
public:
void set_tree_root(const Ref<AnimationNode> &p_root);
Ref<AnimationNode> get_tree_root() const;
@@ -359,4 +361,4 @@ public:
VARIANT_ENUM_CAST(AnimationTree::AnimationProcessCallback)
-#endif // ANIMATION_GRAPH_PLAYER_H
+#endif // ANIMATION_TREE_H
diff --git a/scene/animation/easing_equations.h b/scene/animation/easing_equations.h
index 6d246c7a93..094829e406 100644
--- a/scene/animation/easing_equations.h
+++ b/scene/animation/easing_equations.h
@@ -402,4 +402,4 @@ static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
}
}; // namespace back
-#endif
+#endif // EASING_EQUATIONS_H
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 5457da472f..dbc71cd9e7 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -464,6 +464,17 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
APPLY_EQUATION(columns[2][1]);
return r;
}
+ case Variant::VECTOR4: {
+ Vector4 i = p_initial_val;
+ Vector4 d = p_delta_val;
+ Vector4 r;
+
+ APPLY_EQUATION(x);
+ APPLY_EQUATION(y);
+ APPLY_EQUATION(z);
+ APPLY_EQUATION(w);
+ return r;
+ }
case Variant::QUATERNION: {
Quaternion i = p_initial_val;
@@ -853,7 +864,7 @@ bool CallbackTweener::step(float &r_delta) {
if (elapsed_time >= delay) {
Variant result;
Callable::CallError ce;
- callback.call(nullptr, 0, result, ce);
+ callback.callp(nullptr, 0, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce));
}
@@ -924,7 +935,7 @@ bool MethodTweener::step(float &r_delta) {
Variant result;
Callable::CallError ce;
- callback.call(argptr, 1, result, ce);
+ callback.callp(argptr, 1, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce));
}
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 40268405cf..b57ec2e5e7 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -283,4 +283,4 @@ private:
Callable callback;
};
-#endif
+#endif // TWEEN_H
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
index 4ed126d36e..911363f45d 100644
--- a/scene/debugger/scene_debugger.h
+++ b/scene/debugger/scene_debugger.h
@@ -174,4 +174,4 @@ public:
};
#endif
-#endif
+#endif // SCENE_DEBUGGER_H
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index ba3852ec98..7cf8de6432 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -155,4 +155,4 @@ public:
ButtonGroup();
};
-#endif
+#endif // BASE_BUTTON_H
diff --git a/scene/gui/button.h b/scene/gui/button.h
index 7a29cba677..9d8d457f7c 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -98,4 +98,4 @@ public:
~Button();
};
-#endif
+#endif // BUTTON_H
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index a431d8a5b2..08bd91a368 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CODEEDIT_H
-#define CODEEDIT_H
+#ifndef CODE_EDIT_H
+#define CODE_EDIT_H
#include "scene/gui/text_edit.h"
@@ -433,4 +433,4 @@ public:
VARIANT_ENUM_CAST(CodeEdit::CodeCompletionKind);
-#endif // CODEEDIT_H
+#endif // CODE_EDIT_H
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index bfe5ee335b..e2ead6415a 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -357,7 +357,7 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) {
s->set_h_size_flags(SIZE_EXPAND_FILL);
s->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed));
- s->connect("draw", callable_mp(this, &ColorPicker::_slider_draw), make_binds(idx));
+ s->connect("draw", callable_mp(this, &ColorPicker::_slider_draw).bind(idx));
if (idx < SLIDER_COUNT) {
sliders[idx] = s;
@@ -515,7 +515,7 @@ void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
ColorPresetButton *btn_preset = memnew(ColorPresetButton(p_color));
btn_preset->set_preset_color(p_color);
btn_preset->set_custom_minimum_size(Size2(p_size, p_size));
- btn_preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input), varray(p_color));
+ btn_preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input).bind(p_color));
btn_preset->set_tooltip(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1)));
preset_container->add_child(btn_preset);
}
@@ -998,10 +998,10 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
h = y / w_edit->get_size().height;
}
- if (current_mode == MODE_HSV) {
- color.set_hsv(h, s, v, color.a);
- } else if (current_mode == MODE_OKHSL) {
+ if (actual_shape == SHAPE_OKHSL_CIRCLE) {
color.set_ok_hsl(h, s, v, color.a);
+ } else {
+ color.set_hsv(h, s, v, color.a);
}
last_color = color;
@@ -1075,7 +1075,7 @@ void ColorPicker::_screen_pick_pressed() {
screen->set_default_cursor_shape(CURSOR_POINTING_HAND);
screen->connect("gui_input", callable_mp(this, &ColorPicker::_screen_input));
// It immediately toggles off in the first press otherwise.
- screen->call_deferred(SNAME("connect"), "hidden", Callable(btn_pick, "set_pressed"), varray(false));
+ screen->call_deferred(SNAME("connect"), "hidden", Callable(btn_pick, "set_pressed").bind(false));
} else {
screen->show();
}
@@ -1204,11 +1204,11 @@ ColorPicker::ColorPicker() :
uv_edit = memnew(Control);
hb_edit->add_child(uv_edit);
- uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(uv_edit));
+ uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input).bind(uv_edit));
uv_edit->set_mouse_filter(MOUSE_FILTER_PASS);
uv_edit->set_h_size_flags(SIZE_EXPAND_FILL);
uv_edit->set_v_size_flags(SIZE_EXPAND_FILL);
- uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit));
+ uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, uv_edit));
HBoxContainer *hb_smpl = memnew(HBoxContainer);
add_child(hb_smpl, false, INTERNAL_MODE_FRONT);
@@ -1295,19 +1295,19 @@ ColorPicker::ColorPicker() :
wheel = memnew(Control);
wheel_margin->add_child(wheel);
wheel->set_mouse_filter(MOUSE_FILTER_PASS);
- wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(2, wheel));
+ wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(2, wheel));
wheel_uv = memnew(Control);
wheel_margin->add_child(wheel_uv);
- wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(wheel_uv));
- wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv));
+ wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input).bind(wheel_uv));
+ wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, wheel_uv));
w_edit = memnew(Control);
hb_edit->add_child(w_edit);
w_edit->set_h_size_flags(SIZE_FILL);
w_edit->set_v_size_flags(SIZE_EXPAND_FILL);
w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input));
- w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(1, w_edit));
+ w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(1, w_edit));
_update_controls();
updating = false;
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index e219c78319..8e65ee1861 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -276,4 +276,5 @@ public:
VARIANT_ENUM_CAST(ColorPicker::PickerShapeType);
VARIANT_ENUM_CAST(ColorPicker::ColorModeType);
+
#endif // COLOR_PICKER_H
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 686045901c..06aa913eb1 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -50,6 +50,9 @@
#include "editor/plugins/control_editor_plugin.h"
#endif
+// Editor plugin interoperability.
+
+// TODO: Decouple controls from their editor plugin and get rid of this.
#ifdef TOOLS_ENABLED
Dictionary Control::_edit_get_state() const {
Dictionary s;
@@ -181,6 +184,49 @@ Size2 Control::_edit_get_minimum_size() const {
}
#endif
+// Editor integration.
+
+void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ Node::get_argument_options(p_function, p_idx, r_options);
+
+ if (p_idx == 0) {
+ List<StringName> sn;
+ String pf = p_function;
+ if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") {
+ Theme::get_default()->get_color_list(get_class(), &sn);
+ } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") {
+ Theme::get_default()->get_stylebox_list(get_class(), &sn);
+ } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") {
+ Theme::get_default()->get_font_list(get_class(), &sn);
+ } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") {
+ Theme::get_default()->get_font_size_list(get_class(), &sn);
+ } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") {
+ Theme::get_default()->get_constant_list(get_class(), &sn);
+ }
+
+ sn.sort_custom<StringName::AlphCompare>();
+ for (const StringName &name : sn) {
+ r_options->push_back(String(name).quote());
+ }
+ }
+}
+
+TypedArray<String> Control::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
+ warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
+ }
+
+ return warnings;
+}
+
+bool Control::is_text_field() const {
+ return false;
+}
+
+// Dynamic properties.
+
String Control::properties_managed_by_container[] = {
"offset_left",
"offset_top",
@@ -196,58 +242,6 @@ String Control::properties_managed_by_container[] = {
"size"
};
-void Control::accept_event() {
- if (is_inside_tree()) {
- get_viewport()->_gui_accept_event();
- }
-}
-
-void Control::set_custom_minimum_size(const Size2 &p_custom) {
- if (p_custom == data.custom_minimum_size) {
- return;
- }
- data.custom_minimum_size = p_custom;
- update_minimum_size();
-}
-
-Size2 Control::get_custom_minimum_size() const {
- return data.custom_minimum_size;
-}
-
-void Control::_update_minimum_size_cache() {
- Size2 minsize = get_minimum_size();
- minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
- minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
-
- bool size_changed = false;
- if (data.minimum_size_cache != minsize) {
- size_changed = true;
- }
-
- data.minimum_size_cache = minsize;
- data.minimum_size_valid = true;
-
- if (size_changed) {
- update_minimum_size();
- }
-}
-
-Size2 Control::get_combined_minimum_size() const {
- if (!data.minimum_size_valid) {
- const_cast<Control *>(this)->_update_minimum_size_cache();
- }
- return data.minimum_size_cache;
-}
-
-Transform2D Control::_get_internal_transform() const {
- Transform2D rot_scale;
- rot_scale.set_rotation_and_scale(data.rotation, data.scale);
- Transform2D offset;
- offset.set_origin(-data.pivot_offset);
-
- return offset.affine_inverse() * (rot_scale * offset);
-}
-
bool Control::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (!name.begins_with("theme_override")) {
@@ -258,21 +252,21 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
if (data.icon_override.has(dname)) {
- data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
data.icon_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
} else if (name.begins_with("theme_override_styles/")) {
String dname = name.get_slicec('/', 1);
if (data.style_override.has(dname)) {
- data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
data.style_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
} else if (name.begins_with("theme_override_fonts/")) {
String dname = name.get_slicec('/', 1);
if (data.font_override.has(dname)) {
- data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
data.font_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
@@ -318,21 +312,6 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
-void Control::_update_minimum_size() {
- if (!is_inside_tree()) {
- return;
- }
-
- Size2 minsize = get_combined_minimum_size();
- data.updating_last_minimum_size = false;
-
- if (minsize != data.last_minimum_size) {
- data.last_minimum_size = minsize;
- _size_changed();
- emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
- }
-}
-
bool Control::_get(const StringName &p_name, Variant &r_ret) const {
String sname = p_name;
if (!sname.begins_with("theme_override")) {
@@ -586,6 +565,12 @@ void Control::_validate_property(PropertyInfo &property) const {
}
}
+// Global relations.
+
+bool Control::is_top_level_control() const {
+ return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level());
+}
+
Control *Control::get_parent_control() const {
return data.parent;
}
@@ -594,97 +579,64 @@ Window *Control::get_parent_window() const {
return data.parent_window;
}
-void Control::set_layout_direction(Control::LayoutDirection p_direction) {
- ERR_FAIL_INDEX((int)p_direction, 4);
-
- data.layout_dir = p_direction;
- data.is_rtl_dirty = true;
-
- propagate_notification(NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
-}
+Control *Control::get_root_parent_control() const {
+ const CanvasItem *ci = this;
+ const Control *root = this;
-Control::LayoutDirection Control::get_layout_direction() const {
- return data.layout_dir;
-}
+ while (ci) {
+ const Control *c = Object::cast_to<Control>(ci);
+ if (c) {
+ root = c;
-bool Control::is_layout_rtl() const {
- if (data.is_rtl_dirty) {
- const_cast<Control *>(this)->data.is_rtl_dirty = false;
- if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) {
- Window *parent_window = get_parent_window();
- Control *parent_control = get_parent_control();
- if (parent_control) {
- const_cast<Control *>(this)->data.is_rtl = parent_control->is_layout_rtl();
- } else if (parent_window) {
- const_cast<Control *>(this)->data.is_rtl = parent_window->is_layout_rtl();
- } else {
- if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
- const_cast<Control *>(this)->data.is_rtl = true;
- } else {
- String locale = TranslationServer::get_singleton()->get_tool_locale();
- const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
- }
- }
- } else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
- if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
- const_cast<Control *>(this)->data.is_rtl = true;
- } else {
- String locale = TranslationServer::get_singleton()->get_tool_locale();
- const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ if (c->data.RI || c->is_top_level_control()) {
+ break;
}
- } else {
- const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);
}
- }
- return data.is_rtl;
-}
-void Control::set_auto_translate(bool p_enable) {
- if (p_enable == data.auto_translate) {
- return;
+ ci = ci->get_parent_item();
}
- data.auto_translate = p_enable;
-
- notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
-}
-
-bool Control::is_auto_translating() const {
- return data.auto_translate;
-}
-
-void Control::_clear_size_warning() {
- data.size_warning = false;
+ return const_cast<Control *>(root);
}
-//moved theme configuration here, so controls can set up even if still not inside active scene
-
-void Control::add_child_notify(Node *p_child) {
- Control *child_c = Object::cast_to<Control>(p_child);
-
- if (child_c && child_c->data.theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
- _propagate_theme_changed(child_c, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
+Rect2 Control::get_parent_anchorable_rect() const {
+ if (!is_inside_tree()) {
+ return Rect2();
}
- Window *child_w = Object::cast_to<Window>(p_child);
+ Rect2 parent_rect;
+ if (data.parent_canvas_item) {
+ parent_rect = data.parent_canvas_item->get_anchorable_rect();
+ } else {
+#ifdef TOOLS_ENABLED
+ Node *edited_root = get_tree()->get_edited_scene_root();
+ if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) {
+ parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
+ } else {
+ parent_rect = get_viewport()->get_visible_rect();
+ }
- if (child_w && child_w->theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
- _propagate_theme_changed(child_w, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
+#else
+ parent_rect = get_viewport()->get_visible_rect();
+#endif
}
+
+ return parent_rect;
}
-void Control::remove_child_notify(Node *p_child) {
- Control *child_c = Object::cast_to<Control>(p_child);
+Size2 Control::get_parent_area_size() const {
+ return get_parent_anchorable_rect().size;
+}
- if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) {
- _propagate_theme_changed(child_c, nullptr, nullptr);
- }
+// Positioning and sizing.
- Window *child_w = Object::cast_to<Window>(p_child);
+Transform2D Control::_get_internal_transform() const {
+ Transform2D rot_scale;
+ rot_scale.set_rotation_and_scale(data.rotation, data.scale);
+ Transform2D offset;
+ offset.set_origin(-data.pivot_offset);
- if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) {
- _propagate_theme_changed(child_w, nullptr, nullptr);
- }
+ return offset.affine_inverse() * (rot_scale * offset);
}
void Control::_update_canvas_item_transform() {
@@ -699,815 +651,146 @@ void Control::_update_canvas_item_transform() {
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), xform);
}
-void Control::_notification(int p_notification) {
- switch (p_notification) {
- case NOTIFICATION_POST_ENTER_TREE: {
- data.minimum_size_valid = false;
- data.is_rtl_dirty = true;
- _size_changed();
- } break;
-
- case NOTIFICATION_EXIT_TREE: {
- release_focus();
- get_viewport()->_gui_remove_control(this);
- } break;
-
- case NOTIFICATION_READY: {
-#ifdef DEBUG_ENABLED
- connect("ready", callable_mp(this, &Control::_clear_size_warning), varray(), CONNECT_DEFERRED | CONNECT_ONESHOT);
-#endif
- } break;
-
- case NOTIFICATION_ENTER_CANVAS: {
- data.parent = Object::cast_to<Control>(get_parent());
- data.parent_window = Object::cast_to<Window>(get_parent());
- data.is_rtl_dirty = true;
-
- if (data.theme.is_null()) {
- if (data.parent && (data.parent->data.theme_owner || data.parent->data.theme_owner_window)) {
- data.theme_owner = data.parent->data.theme_owner;
- data.theme_owner_window = data.parent->data.theme_owner_window;
- notification(NOTIFICATION_THEME_CHANGED);
- } else if (data.parent_window && (data.parent_window->theme_owner || data.parent_window->theme_owner_window)) {
- data.theme_owner = data.parent_window->theme_owner;
- data.theme_owner_window = data.parent_window->theme_owner_window;
- notification(NOTIFICATION_THEME_CHANGED);
- }
- }
-
- CanvasItem *node = this;
- bool has_parent_control = false;
-
- while (!node->is_set_as_top_level()) {
- CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent());
- if (!parent) {
- break;
- }
-
- Control *parent_control = Object::cast_to<Control>(parent);
- if (parent_control) {
- has_parent_control = true;
- break;
- }
-
- node = parent;
- }
-
- if (has_parent_control) {
- // Do nothing, has a parent control.
- } else {
- // Is a regular root control or top_level.
- Viewport *viewport = get_viewport();
- ERR_FAIL_COND(!viewport);
- data.RI = viewport->_gui_add_root_control(this);
- }
-
- data.parent_canvas_item = get_parent_item();
-
- if (data.parent_canvas_item) {
- data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed));
- } else {
- // Connect viewport.
- Viewport *viewport = get_viewport();
- ERR_FAIL_COND(!viewport);
- viewport->connect("size_changed", callable_mp(this, &Control::_size_changed));
- }
- } break;
-
- case NOTIFICATION_EXIT_CANVAS: {
- if (data.parent_canvas_item) {
- data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed));
- data.parent_canvas_item = nullptr;
- } else if (!is_set_as_top_level()) {
- //disconnect viewport
- Viewport *viewport = get_viewport();
- ERR_FAIL_COND(!viewport);
- viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed));
- }
-
- if (data.RI) {
- get_viewport()->_gui_remove_root_control(data.RI);
- data.RI = nullptr;
- }
-
- data.parent = nullptr;
- data.parent_canvas_item = nullptr;
- data.parent_window = nullptr;
- data.is_rtl_dirty = true;
- } break;
-
- case NOTIFICATION_MOVED_IN_PARENT: {
- // some parents need to know the order of the children to draw (like TabContainer)
- // update if necessary
- if (data.parent) {
- data.parent->update();
- }
- update();
-
- if (data.RI) {
- get_viewport()->_gui_set_root_order_dirty();
- }
- } break;
-
- case NOTIFICATION_RESIZED: {
- emit_signal(SceneStringNames::get_singleton()->resized);
- } break;
-
- case NOTIFICATION_DRAW: {
- _update_canvas_item_transform();
- RenderingServer::get_singleton()->canvas_item_set_custom_rect(get_canvas_item(), !data.disable_visibility_clip, Rect2(Point2(), get_size()));
- RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), data.clip_contents);
- } break;
-
- case NOTIFICATION_MOUSE_ENTER: {
- emit_signal(SceneStringNames::get_singleton()->mouse_entered);
- } break;
-
- case NOTIFICATION_MOUSE_EXIT: {
- emit_signal(SceneStringNames::get_singleton()->mouse_exited);
- } break;
-
- case NOTIFICATION_FOCUS_ENTER: {
- emit_signal(SceneStringNames::get_singleton()->focus_entered);
- update();
- } break;
-
- case NOTIFICATION_FOCUS_EXIT: {
- emit_signal(SceneStringNames::get_singleton()->focus_exited);
- update();
- } break;
-
- case NOTIFICATION_THEME_CHANGED: {
- update_minimum_size();
- update();
- } break;
-
- case NOTIFICATION_VISIBILITY_CHANGED: {
- if (!is_visible_in_tree()) {
- if (get_viewport() != nullptr) {
- get_viewport()->_gui_hide_control(this);
- }
- } else {
- data.minimum_size_valid = false;
- _update_minimum_size();
- _size_changed();
- }
- } break;
-
- case NOTIFICATION_TRANSLATION_CHANGED:
- case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- if (is_inside_tree()) {
- data.is_rtl_dirty = true;
- _size_changed();
- }
- } break;
- }
-}
-
-bool Control::has_point(const Point2 &p_point) const {
- bool ret;
- if (GDVIRTUAL_CALL(_has_point, p_point, ret)) {
- return ret;
- }
- return Rect2(Point2(), get_size()).has_point(p_point);
-}
-
-void Control::set_drag_forwarding(Object *p_target) {
- if (p_target) {
- data.drag_owner = p_target->get_instance_id();
- } else {
- data.drag_owner = ObjectID();
- }
-}
-
-Variant Control::get_drag_data(const Point2 &p_point) {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- return obj->call("_get_drag_data_fw", p_point, this);
- }
- }
-
- Variant dd;
- if (GDVIRTUAL_CALL(_get_drag_data, p_point, dd)) {
- return dd;
- }
-
- return Variant();
-}
-
-bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- return obj->call("_can_drop_data_fw", p_point, p_data, this);
- }
- }
-
- bool ret;
- if (GDVIRTUAL_CALL(_can_drop_data, p_point, p_data, ret)) {
- return ret;
- }
- return false;
-}
-
-void Control::drop_data(const Point2 &p_point, const Variant &p_data) {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- obj->call("_drop_data_fw", p_point, p_data, this);
- return;
- }
- }
-
- GDVIRTUAL_CALL(_drop_data, p_point, p_data);
-}
-
-void Control::force_drag(const Variant &p_data, Control *p_control) {
- ERR_FAIL_COND(!is_inside_tree());
- ERR_FAIL_COND(p_data.get_type() == Variant::NIL);
-
- get_viewport()->_gui_force_drag(this, p_data, p_control);
-}
-
-void Control::set_drag_preview(Control *p_control) {
- ERR_FAIL_COND(!is_inside_tree());
- ERR_FAIL_COND(!get_viewport()->gui_is_dragging());
- get_viewport()->_gui_set_drag_preview(this, p_control);
-}
-
-bool Control::is_drag_successful() const {
- return is_inside_tree() && get_viewport()->gui_is_drag_successful();
-}
-
-void Control::_call_gui_input(const Ref<InputEvent> &p_event) {
- emit_signal(SceneStringNames::get_singleton()->gui_input, p_event); //signal should be first, so it's possible to override an event (and then accept it)
- if (!is_inside_tree() || get_viewport()->is_input_handled()) {
- return; //input was handled, abort
- }
- GDVIRTUAL_CALL(_gui_input, p_event);
- if (!is_inside_tree() || get_viewport()->is_input_handled()) {
- return; //input was handled, abort
- }
- gui_input(p_event);
-}
-void Control::gui_input(const Ref<InputEvent> &p_event) {
+Transform2D Control::get_transform() const {
+ Transform2D xform = _get_internal_transform();
+ xform[2] += get_position();
+ return xform;
}
-Size2 Control::get_minimum_size() const {
- Vector2 ms;
- if (GDVIRTUAL_CALL(_get_minimum_size, ms)) {
- return ms;
- }
- return Vector2();
-}
+/// Anchors and offsets.
-template <class T>
-T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) {
- ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified.");
-
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- // For each theme resource check the theme types provided and see if p_name exists with any of them.
- for (const StringName &E : p_theme_types) {
- if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
- return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E);
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) {
- return theme_owner_window->theme->get_theme_item(p_data_type, p_name, E);
- }
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
-
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- for (const StringName &E : p_theme_types) {
- if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) {
- return Theme::get_project_default()->get_theme_item(p_data_type, p_name, E);
- }
- }
- }
-
- // Lastly, fall back on the items defined in the default Theme, if they exist.
- for (const StringName &E : p_theme_types) {
- if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) {
- return Theme::get_default()->get_theme_item(p_data_type, p_name, E);
- }
- }
- // If they don't exist, use any type to return the default/empty value.
- return Theme::get_default()->get_theme_item(p_data_type, p_name, p_theme_types[0]);
+void Control::_set_anchor(Side p_side, real_t p_anchor) {
+ set_anchor(p_side, p_anchor);
}
-bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) {
- ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified.");
-
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- // For each theme resource check the theme types provided and see if p_name exists with any of them.
- for (const StringName &E : p_theme_types) {
- if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
- return true;
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) {
- return true;
- }
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
+void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
+ ERR_FAIL_INDEX((int)p_side, 4);
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- for (const StringName &E : p_theme_types) {
- if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) {
- return true;
- }
- }
- }
+ Rect2 parent_rect = get_parent_anchorable_rect();
+ real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
+ real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range;
+ real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range;
- // Lastly, fall back on the items defined in the default Theme, if they exist.
- for (const StringName &E : p_theme_types) {
- if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) {
- return true;
- }
- }
- return false;
-}
+ data.anchor[p_side] = p_anchor;
-void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(data.theme_type_variation) != StringName()) {
- Theme::get_project_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list);
+ if (((p_side == SIDE_LEFT || p_side == SIDE_TOP) && data.anchor[p_side] > data.anchor[(p_side + 2) % 4]) ||
+ ((p_side == SIDE_RIGHT || p_side == SIDE_BOTTOM) && data.anchor[p_side] < data.anchor[(p_side + 2) % 4])) {
+ if (p_push_opposite_anchor) {
+ data.anchor[(p_side + 2) % 4] = data.anchor[p_side];
} else {
- Theme::get_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list);
- }
- } else {
- Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list);
- }
-}
-
-Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
- if (tex) {
- return *tex;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<Ref<Texture2D>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types);
-}
-
-Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const Ref<StyleBox> *style = data.style_override.getptr(p_name);
- if (style) {
- return *style;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<Ref<StyleBox>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
-}
-
-Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const Ref<Font> *font = data.font_override.getptr(p_name);
- if (font) {
- return *font;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<Ref<Font>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types);
-}
-
-int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const int *font_size = data.font_size_override.getptr(p_name);
- if (font_size && (*font_size) > 0) {
- return *font_size;
+ data.anchor[p_side] = data.anchor[(p_side + 2) % 4];
}
}
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
-}
-
-Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const Color *color = data.color_override.getptr(p_name);
- if (color) {
- return *color;
+ if (!p_keep_offset) {
+ data.offset[p_side] = previous_pos - data.anchor[p_side] * parent_range;
+ if (p_push_opposite_anchor) {
+ data.offset[(p_side + 2) % 4] = previous_opposite_pos - data.anchor[(p_side + 2) % 4] * parent_range;
}
}
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<Color>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types);
-}
-
-int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const int *constant = data.constant_override.getptr(p_name);
- if (constant) {
- return *constant;
- }
+ if (is_inside_tree()) {
+ _size_changed();
}
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
-}
-
-bool Control::has_theme_icon_override(const StringName &p_name) const {
- const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
- return tex != nullptr;
-}
-
-bool Control::has_theme_stylebox_override(const StringName &p_name) const {
- const Ref<StyleBox> *style = data.style_override.getptr(p_name);
- return style != nullptr;
-}
-
-bool Control::has_theme_font_override(const StringName &p_name) const {
- const Ref<Font> *font = data.font_override.getptr(p_name);
- return font != nullptr;
-}
-
-bool Control::has_theme_font_size_override(const StringName &p_name) const {
- const int *font_size = data.font_size_override.getptr(p_name);
- return font_size != nullptr;
-}
-
-bool Control::has_theme_color_override(const StringName &p_name) const {
- const Color *color = data.color_override.getptr(p_name);
- return color != nullptr;
-}
-
-bool Control::has_theme_constant_override(const StringName &p_name) const {
- const int *constant = data.constant_override.getptr(p_name);
- return constant != nullptr;
+ update();
}
-bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_icon_override(p_name)) {
- return true;
- }
- }
+real_t Control::get_anchor(Side p_side) const {
+ ERR_FAIL_INDEX_V(int(p_side), 4, 0.0);
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types);
+ return data.anchor[p_side];
}
-bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_stylebox_override(p_name)) {
- return true;
- }
- }
+void Control::set_offset(Side p_side, real_t p_value) {
+ ERR_FAIL_INDEX((int)p_side, 4);
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
+ data.offset[p_side] = p_value;
+ _size_changed();
}
-bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_font_override(p_name)) {
- return true;
- }
- }
+real_t Control::get_offset(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0);
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types);
+ return data.offset[p_side];
}
-bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_font_size_override(p_name)) {
- return true;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
+void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) {
+ set_anchor(p_side, p_anchor, false, p_push_opposite_anchor);
+ set_offset(p_side, p_pos);
}
-bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_color_override(p_name)) {
- return true;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types);
+void Control::set_begin(const Size2 &p_point) {
+ data.offset[0] = p_point.x;
+ data.offset[1] = p_point.y;
+ _size_changed();
}
-bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_constant_override(p_name)) {
- return true;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
+Size2 Control::get_begin() const {
+ return Size2(data.offset[0], data.offset[1]);
}
-float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window) {
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- // For each theme resource see if their assigned theme has the default value defined and valid.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_base_scale()) {
- return theme_owner->data.theme->get_default_base_scale();
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_default_base_scale()) {
- return theme_owner_window->theme->get_default_base_scale();
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
-
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_base_scale()) {
- return Theme::get_project_default()->get_default_base_scale();
- }
- }
-
- // Lastly, fall back on the default Theme.
- if (Theme::get_default()->has_default_base_scale()) {
- return Theme::get_default()->get_default_base_scale();
- }
- return Theme::get_fallback_base_scale();
+void Control::set_end(const Size2 &p_point) {
+ data.offset[2] = p_point.x;
+ data.offset[3] = p_point.y;
+ _size_changed();
}
-float Control::get_theme_default_base_scale() const {
- return fetch_theme_default_base_scale(data.theme_owner, data.theme_owner_window);
+Size2 Control::get_end() const {
+ return Size2(data.offset[2], data.offset[3]);
}
-Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window) {
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- // For each theme resource see if their assigned theme has the default value defined and valid.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_font()) {
- return theme_owner->data.theme->get_default_font();
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_default_font()) {
- return theme_owner_window->theme->get_default_font();
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
-
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_font()) {
- return Theme::get_project_default()->get_default_font();
- }
- }
+void Control::set_h_grow_direction(GrowDirection p_direction) {
+ ERR_FAIL_INDEX((int)p_direction, 3);
- // Lastly, fall back on the default Theme.
- if (Theme::get_default()->has_default_font()) {
- return Theme::get_default()->get_default_font();
- }
- return Theme::get_fallback_font();
+ data.h_grow = p_direction;
+ _size_changed();
}
-Ref<Font> Control::get_theme_default_font() const {
- return fetch_theme_default_font(data.theme_owner, data.theme_owner_window);
+Control::GrowDirection Control::get_h_grow_direction() const {
+ return data.h_grow;
}
-int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window) {
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- // For each theme resource see if their assigned theme has the default value defined and valid.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_font_size()) {
- return theme_owner->data.theme->get_default_font_size();
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_default_font_size()) {
- return theme_owner_window->theme->get_default_font_size();
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
-
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_font_size()) {
- return Theme::get_project_default()->get_default_font_size();
- }
- }
+void Control::set_v_grow_direction(GrowDirection p_direction) {
+ ERR_FAIL_INDEX((int)p_direction, 3);
- // Lastly, fall back on the default Theme.
- if (Theme::get_default()->has_default_font_size()) {
- return Theme::get_default()->get_default_font_size();
- }
- return Theme::get_fallback_font_size();
+ data.v_grow = p_direction;
+ _size_changed();
}
-int Control::get_theme_default_font_size() const {
- return fetch_theme_default_font_size(data.theme_owner, data.theme_owner_window);
+Control::GrowDirection Control::get_v_grow_direction() const {
+ return data.v_grow;
}
-Rect2 Control::get_parent_anchorable_rect() const {
- if (!is_inside_tree()) {
- return Rect2();
- }
-
- Rect2 parent_rect;
- if (data.parent_canvas_item) {
- parent_rect = data.parent_canvas_item->get_anchorable_rect();
- } else {
-#ifdef TOOLS_ENABLED
- Node *edited_root = get_tree()->get_edited_scene_root();
- if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) {
- parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
- } else {
- parent_rect = get_viewport()->get_visible_rect();
- }
+void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) {
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ ERR_FAIL_COND(parent_rect_size.x == 0.0);
+ ERR_FAIL_COND(parent_rect_size.y == 0.0);
-#else
- parent_rect = get_viewport()->get_visible_rect();
-#endif
+ real_t x = p_rect.position.x;
+ if (is_layout_rtl()) {
+ x = parent_rect_size.x - x - p_rect.size.x;
}
-
- return parent_rect;
-}
-
-Size2 Control::get_parent_area_size() const {
- return get_parent_anchorable_rect().size;
+ r_anchors[0] = (x - p_offsets[0]) / parent_rect_size.x;
+ r_anchors[1] = (p_rect.position.y - p_offsets[1]) / parent_rect_size.y;
+ r_anchors[2] = (x + p_rect.size.x - p_offsets[2]) / parent_rect_size.x;
+ r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y;
}
-void Control::_size_changed() {
- Rect2 parent_rect = get_parent_anchorable_rect();
-
- real_t edge_pos[4];
-
- for (int i = 0; i < 4; i++) {
- real_t area = parent_rect.size[i & 1];
- edge_pos[i] = data.offset[i] + (data.anchor[i] * area);
- }
-
- Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]);
- Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache;
-
- Size2 minimum_size = get_combined_minimum_size();
-
- if (minimum_size.width > new_size_cache.width) {
- if (data.h_grow == GROW_DIRECTION_BEGIN) {
- new_pos_cache.x += new_size_cache.width - minimum_size.width;
- } else if (data.h_grow == GROW_DIRECTION_BOTH) {
- new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width);
- }
-
- new_size_cache.width = minimum_size.width;
- }
+void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) {
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ real_t x = p_rect.position.x;
if (is_layout_rtl()) {
- new_pos_cache.x = parent_rect.size.x - new_pos_cache.x - new_size_cache.x;
- }
-
- if (minimum_size.height > new_size_cache.height) {
- if (data.v_grow == GROW_DIRECTION_BEGIN) {
- new_pos_cache.y += new_size_cache.height - minimum_size.height;
- } else if (data.v_grow == GROW_DIRECTION_BOTH) {
- new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height);
- }
-
- new_size_cache.height = minimum_size.height;
- }
-
- bool pos_changed = new_pos_cache != data.pos_cache;
- bool size_changed = new_size_cache != data.size_cache;
-
- data.pos_cache = new_pos_cache;
- data.size_cache = new_size_cache;
-
- if (is_inside_tree()) {
- if (size_changed) {
- notification(NOTIFICATION_RESIZED);
- }
- if (pos_changed || size_changed) {
- item_rect_changed(size_changed);
- _notify_transform();
- }
-
- if (pos_changed && !size_changed) {
- _update_canvas_item_transform(); //move because it won't be updated
- }
+ x = parent_rect_size.x - x - p_rect.size.x;
}
+ r_offsets[0] = x - (p_anchors[0] * parent_rect_size.x);
+ r_offsets[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y);
+ r_offsets[2] = x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x);
+ r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
}
+/// Presets and layout modes.
+
void Control::_set_layout_mode(LayoutMode p_mode) {
bool list_changed = false;
@@ -1557,47 +840,6 @@ Control::LayoutMode Control::_get_layout_mode() const {
return LayoutMode::LAYOUT_MODE_POSITION;
}
-void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
- ERR_FAIL_INDEX((int)p_side, 4);
-
- Rect2 parent_rect = get_parent_anchorable_rect();
- real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
- real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range;
- real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range;
-
- data.anchor[p_side] = p_anchor;
-
- if (((p_side == SIDE_LEFT || p_side == SIDE_TOP) && data.anchor[p_side] > data.anchor[(p_side + 2) % 4]) ||
- ((p_side == SIDE_RIGHT || p_side == SIDE_BOTTOM) && data.anchor[p_side] < data.anchor[(p_side + 2) % 4])) {
- if (p_push_opposite_anchor) {
- data.anchor[(p_side + 2) % 4] = data.anchor[p_side];
- } else {
- data.anchor[p_side] = data.anchor[(p_side + 2) % 4];
- }
- }
-
- if (!p_keep_offset) {
- data.offset[p_side] = previous_pos - data.anchor[p_side] * parent_range;
- if (p_push_opposite_anchor) {
- data.offset[(p_side + 2) % 4] = previous_opposite_pos - data.anchor[(p_side + 2) % 4] * parent_range;
- }
- }
- if (is_inside_tree()) {
- _size_changed();
- }
-
- update();
-}
-
-void Control::_set_anchor(Side p_side, real_t p_anchor) {
- set_anchor(p_side, p_anchor);
-}
-
-void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) {
- set_anchor(p_side, p_anchor, false, p_push_opposite_anchor);
- set_offset(p_side, p_pos);
-}
-
void Control::_set_anchors_layout_preset(int p_preset) {
bool list_changed = false;
@@ -2037,43 +1279,37 @@ void Control::set_grow_direction_preset(LayoutPreset p_preset) {
}
}
-real_t Control::get_anchor(Side p_side) const {
- ERR_FAIL_INDEX_V(int(p_side), 4, 0.0);
+/// Manual positioning.
- return data.anchor[p_side];
+void Control::_set_position(const Size2 &p_point) {
+ set_position(p_point);
}
-void Control::set_offset(Side p_side, real_t p_value) {
- ERR_FAIL_INDEX((int)p_side, 4);
-
- data.offset[p_side] = p_value;
+void Control::set_position(const Size2 &p_point, bool p_keep_offsets) {
+ if (p_keep_offsets) {
+ _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
+ } else {
+ _compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset);
+ }
_size_changed();
}
-void Control::set_begin(const Size2 &p_point) {
- data.offset[0] = p_point.x;
- data.offset[1] = p_point.y;
- _size_changed();
+Size2 Control::get_position() const {
+ return data.pos_cache;
}
-void Control::set_end(const Size2 &p_point) {
- data.offset[2] = p_point.x;
- data.offset[3] = p_point.y;
- _size_changed();
+void Control::_set_global_position(const Point2 &p_point) {
+ set_global_position(p_point);
}
-real_t Control::get_offset(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0);
-
- return data.offset[p_side];
-}
+void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) {
+ Transform2D inv;
-Size2 Control::get_begin() const {
- return Size2(data.offset[0], data.offset[1]);
-}
+ if (data.parent_canvas_item) {
+ inv = data.parent_canvas_item->get_global_transform().affine_inverse();
+ }
-Size2 Control::get_end() const {
- return Size2(data.offset[2], data.offset[3]);
+ set_position(inv.xform(p_point), p_keep_offsets);
}
Point2 Control::get_global_position() const {
@@ -2091,72 +1327,6 @@ Point2 Control::get_screen_position() const {
return global_pos;
}
-void Control::_set_global_position(const Point2 &p_point) {
- set_global_position(p_point);
-}
-
-void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) {
- Transform2D inv;
-
- if (data.parent_canvas_item) {
- inv = data.parent_canvas_item->get_global_transform().affine_inverse();
- }
-
- set_position(inv.xform(p_point), p_keep_offsets);
-}
-
-void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) {
- Size2 parent_rect_size = get_parent_anchorable_rect().size;
- ERR_FAIL_COND(parent_rect_size.x == 0.0);
- ERR_FAIL_COND(parent_rect_size.y == 0.0);
-
- real_t x = p_rect.position.x;
- if (is_layout_rtl()) {
- x = parent_rect_size.x - x - p_rect.size.x;
- }
- r_anchors[0] = (x - p_offsets[0]) / parent_rect_size.x;
- r_anchors[1] = (p_rect.position.y - p_offsets[1]) / parent_rect_size.y;
- r_anchors[2] = (x + p_rect.size.x - p_offsets[2]) / parent_rect_size.x;
- r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y;
-}
-
-void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) {
- Size2 parent_rect_size = get_parent_anchorable_rect().size;
-
- real_t x = p_rect.position.x;
- if (is_layout_rtl()) {
- x = parent_rect_size.x - x - p_rect.size.x;
- }
- r_offsets[0] = x - (p_anchors[0] * parent_rect_size.x);
- r_offsets[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y);
- r_offsets[2] = x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x);
- r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
-}
-
-void Control::_set_position(const Size2 &p_point) {
- set_position(p_point);
-}
-
-void Control::set_position(const Size2 &p_point, bool p_keep_offsets) {
- if (p_keep_offsets) {
- _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
- } else {
- _compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset);
- }
- _size_changed();
-}
-
-void Control::set_rect(const Rect2 &p_rect) {
- for (int i = 0; i < 4; i++) {
- data.anchor[i] = ANCHOR_BEGIN;
- }
-
- _compute_offsets(p_rect, data.anchor, data.offset);
- if (is_inside_tree()) {
- _size_changed();
- }
-}
-
void Control::_set_size(const Size2 &p_size) {
#ifdef DEBUG_ENABLED
if (data.size_warning && (data.anchor[SIDE_LEFT] != data.anchor[SIDE_RIGHT] || data.anchor[SIDE_TOP] != data.anchor[SIDE_BOTTOM])) {
@@ -2184,10 +1354,6 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
_size_changed();
}
-Size2 Control::get_position() const {
- return data.pos_cache;
-}
-
Size2 Control::get_size() const {
return data.size_cache;
}
@@ -2196,6 +1362,21 @@ void Control::reset_size() {
set_size(Size2());
}
+void Control::set_rect(const Rect2 &p_rect) {
+ for (int i = 0; i < 4; i++) {
+ data.anchor[i] = ANCHOR_BEGIN;
+ }
+
+ _compute_offsets(p_rect, data.anchor, data.offset);
+ if (is_inside_tree()) {
+ _size_changed();
+ }
+}
+
+Rect2 Control::get_rect() const {
+ return Rect2(get_position(), get_size());
+}
+
Rect2 Control::get_global_rect() const {
return Rect2(get_global_position(), get_size());
}
@@ -2220,118 +1401,382 @@ Rect2 Control::get_window_rect() const {
return gr;
}
-Rect2 Control::get_rect() const {
- return Rect2(get_position(), get_size());
-}
-
Rect2 Control::get_anchorable_rect() const {
return Rect2(Point2(), get_size());
}
-void Control::begin_bulk_theme_override() {
- data.bulk_theme_override = true;
+void Control::set_scale(const Vector2 &p_scale) {
+ data.scale = p_scale;
+ // Avoid having 0 scale values, can lead to errors in physics and rendering.
+ if (data.scale.x == 0) {
+ data.scale.x = CMP_EPSILON;
+ }
+ if (data.scale.y == 0) {
+ data.scale.y = CMP_EPSILON;
+ }
+ update();
+ _notify_transform();
}
-void Control::end_bulk_theme_override() {
- ERR_FAIL_COND(!data.bulk_theme_override);
+Vector2 Control::get_scale() const {
+ return data.scale;
+}
- data.bulk_theme_override = false;
- _notify_theme_changed();
+void Control::set_rotation(real_t p_radians) {
+ data.rotation = p_radians;
+ update();
+ _notify_transform();
}
-void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) {
- ERR_FAIL_COND(!p_icon.is_valid());
+real_t Control::get_rotation() const {
+ return data.rotation;
+}
- if (data.icon_override.has(p_name)) {
- data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+void Control::set_pivot_offset(const Vector2 &p_pivot) {
+ data.pivot_offset = p_pivot;
+ update();
+ _notify_transform();
+}
+
+Vector2 Control::get_pivot_offset() const {
+ return data.pivot_offset;
+}
+
+/// Sizes.
+
+void Control::_update_minimum_size() {
+ if (!is_inside_tree()) {
+ return;
}
- data.icon_override[p_name] = p_icon;
- data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- _notify_theme_changed();
+ Size2 minsize = get_combined_minimum_size();
+ data.updating_last_minimum_size = false;
+
+ if (minsize != data.last_minimum_size) {
+ data.last_minimum_size = minsize;
+ _size_changed();
+ emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
+ }
}
-void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
- ERR_FAIL_COND(!p_style.is_valid());
+void Control::update_minimum_size() {
+ if (!is_inside_tree() || data.block_minimum_size_adjust) {
+ return;
+ }
- if (data.style_override.has(p_name)) {
- data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ Control *invalidate = this;
+
+ //invalidate cache upwards
+ while (invalidate && invalidate->data.minimum_size_valid) {
+ invalidate->data.minimum_size_valid = false;
+ if (invalidate->is_set_as_top_level()) {
+ break; // do not go further up
+ }
+ if (!invalidate->data.parent && get_parent()) {
+ Window *parent_window = Object::cast_to<Window>(get_parent());
+ if (parent_window && parent_window->is_wrapping_controls()) {
+ parent_window->child_controls_changed();
+ }
+ }
+ invalidate = invalidate->data.parent;
}
- data.style_override[p_name] = p_style;
- data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- _notify_theme_changed();
+ if (!is_visible_in_tree()) {
+ return;
+ }
+
+ if (data.updating_last_minimum_size) {
+ return;
+ }
+
+ data.updating_last_minimum_size = true;
+
+ MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
}
-void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) {
- ERR_FAIL_COND(!p_font.is_valid());
+void Control::set_block_minimum_size_adjust(bool p_block) {
+ data.block_minimum_size_adjust = p_block;
+}
- if (data.font_override.has(p_name)) {
- data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+bool Control::is_minimum_size_adjust_blocked() const {
+ return data.block_minimum_size_adjust;
+}
+
+Size2 Control::get_minimum_size() const {
+ Vector2 ms;
+ if (GDVIRTUAL_CALL(_get_minimum_size, ms)) {
+ return ms;
}
+ return Vector2();
+}
- data.font_override[p_name] = p_font;
- data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- _notify_theme_changed();
+void Control::set_custom_minimum_size(const Size2 &p_custom) {
+ if (p_custom == data.custom_minimum_size) {
+ return;
+ }
+ data.custom_minimum_size = p_custom;
+ update_minimum_size();
}
-void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) {
- data.font_size_override[p_name] = p_font_size;
- _notify_theme_changed();
+Size2 Control::get_custom_minimum_size() const {
+ return data.custom_minimum_size;
}
-void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) {
- data.color_override[p_name] = p_color;
- _notify_theme_changed();
+void Control::_update_minimum_size_cache() {
+ Size2 minsize = get_minimum_size();
+ minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
+ minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
+
+ bool size_changed = false;
+ if (data.minimum_size_cache != minsize) {
+ size_changed = true;
+ }
+
+ data.minimum_size_cache = minsize;
+ data.minimum_size_valid = true;
+
+ if (size_changed) {
+ update_minimum_size();
+ }
}
-void Control::add_theme_constant_override(const StringName &p_name, int p_constant) {
- data.constant_override[p_name] = p_constant;
- _notify_theme_changed();
+Size2 Control::get_combined_minimum_size() const {
+ if (!data.minimum_size_valid) {
+ const_cast<Control *>(this)->_update_minimum_size_cache();
+ }
+ return data.minimum_size_cache;
}
-void Control::remove_theme_icon_override(const StringName &p_name) {
- if (data.icon_override.has(p_name)) {
- data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+void Control::_size_changed() {
+ Rect2 parent_rect = get_parent_anchorable_rect();
+
+ real_t edge_pos[4];
+
+ for (int i = 0; i < 4; i++) {
+ real_t area = parent_rect.size[i & 1];
+ edge_pos[i] = data.offset[i] + (data.anchor[i] * area);
}
- data.icon_override.erase(p_name);
- _notify_theme_changed();
+ Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]);
+ Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache;
+
+ Size2 minimum_size = get_combined_minimum_size();
+
+ if (minimum_size.width > new_size_cache.width) {
+ if (data.h_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.x += new_size_cache.width - minimum_size.width;
+ } else if (data.h_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width);
+ }
+
+ new_size_cache.width = minimum_size.width;
+ }
+
+ if (is_layout_rtl()) {
+ new_pos_cache.x = parent_rect.size.x - new_pos_cache.x - new_size_cache.x;
+ }
+
+ if (minimum_size.height > new_size_cache.height) {
+ if (data.v_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.y += new_size_cache.height - minimum_size.height;
+ } else if (data.v_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height);
+ }
+
+ new_size_cache.height = minimum_size.height;
+ }
+
+ bool pos_changed = new_pos_cache != data.pos_cache;
+ bool size_changed = new_size_cache != data.size_cache;
+
+ data.pos_cache = new_pos_cache;
+ data.size_cache = new_size_cache;
+
+ if (is_inside_tree()) {
+ if (size_changed) {
+ notification(NOTIFICATION_RESIZED);
+ }
+ if (pos_changed || size_changed) {
+ item_rect_changed(size_changed);
+ _notify_transform();
+ }
+
+ if (pos_changed && !size_changed) {
+ _update_canvas_item_transform(); //move because it won't be updated
+ }
+ }
}
-void Control::remove_theme_style_override(const StringName &p_name) {
- if (data.style_override.has(p_name)) {
- data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+void Control::_clear_size_warning() {
+ data.size_warning = false;
+}
+
+// Container sizing.
+
+void Control::set_h_size_flags(int p_flags) {
+ if (data.h_size_flags == p_flags) {
+ return;
}
+ data.h_size_flags = p_flags;
+ emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
+}
- data.style_override.erase(p_name);
- _notify_theme_changed();
+int Control::get_h_size_flags() const {
+ return data.h_size_flags;
}
-void Control::remove_theme_font_override(const StringName &p_name) {
- if (data.font_override.has(p_name)) {
- data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+void Control::set_v_size_flags(int p_flags) {
+ if (data.v_size_flags == p_flags) {
+ return;
}
+ data.v_size_flags = p_flags;
+ emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
+}
- data.font_override.erase(p_name);
- _notify_theme_changed();
+int Control::get_v_size_flags() const {
+ return data.v_size_flags;
}
-void Control::remove_theme_font_size_override(const StringName &p_name) {
- data.font_size_override.erase(p_name);
- _notify_theme_changed();
+void Control::set_stretch_ratio(real_t p_ratio) {
+ if (data.expand == p_ratio) {
+ return;
+ }
+
+ data.expand = p_ratio;
+ emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
}
-void Control::remove_theme_color_override(const StringName &p_name) {
- data.color_override.erase(p_name);
- _notify_theme_changed();
+real_t Control::get_stretch_ratio() const {
+ return data.expand;
}
-void Control::remove_theme_constant_override(const StringName &p_name) {
- data.constant_override.erase(p_name);
- _notify_theme_changed();
+// Input events.
+
+void Control::_call_gui_input(const Ref<InputEvent> &p_event) {
+ emit_signal(SceneStringNames::get_singleton()->gui_input, p_event); //signal should be first, so it's possible to override an event (and then accept it)
+ if (!is_inside_tree() || get_viewport()->is_input_handled()) {
+ return; //input was handled, abort
+ }
+ GDVIRTUAL_CALL(_gui_input, p_event);
+ if (!is_inside_tree() || get_viewport()->is_input_handled()) {
+ return; //input was handled, abort
+ }
+ gui_input(p_event);
+}
+
+void Control::gui_input(const Ref<InputEvent> &p_event) {
+}
+
+void Control::accept_event() {
+ if (is_inside_tree()) {
+ get_viewport()->_gui_accept_event();
+ }
+}
+
+bool Control::has_point(const Point2 &p_point) const {
+ bool ret;
+ if (GDVIRTUAL_CALL(_has_point, p_point, ret)) {
+ return ret;
+ }
+ return Rect2(Point2(), get_size()).has_point(p_point);
+}
+
+void Control::set_mouse_filter(MouseFilter p_filter) {
+ ERR_FAIL_INDEX(p_filter, 3);
+ data.mouse_filter = p_filter;
+ notify_property_list_changed();
+ update_configuration_warnings();
+}
+
+Control::MouseFilter Control::get_mouse_filter() const {
+ return data.mouse_filter;
+}
+
+void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) {
+ data.force_pass_scroll_events = p_force_pass_scroll_events;
+}
+
+bool Control::is_force_pass_scroll_events() const {
+ return data.force_pass_scroll_events;
+}
+
+void Control::warp_mouse(const Point2 &p_position) {
+ ERR_FAIL_COND(!is_inside_tree());
+ get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position));
+}
+
+// Drag and drop handling.
+
+void Control::set_drag_forwarding(Object *p_target) {
+ if (p_target) {
+ data.drag_owner = p_target->get_instance_id();
+ } else {
+ data.drag_owner = ObjectID();
+ }
+}
+
+Variant Control::get_drag_data(const Point2 &p_point) {
+ if (data.drag_owner.is_valid()) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ return obj->call("_get_drag_data_fw", p_point, this);
+ }
+ }
+
+ Variant dd;
+ if (GDVIRTUAL_CALL(_get_drag_data, p_point, dd)) {
+ return dd;
+ }
+
+ return Variant();
+}
+
+bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+ if (data.drag_owner.is_valid()) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ return obj->call("_can_drop_data_fw", p_point, p_data, this);
+ }
+ }
+
+ bool ret;
+ if (GDVIRTUAL_CALL(_can_drop_data, p_point, p_data, ret)) {
+ return ret;
+ }
+ return false;
+}
+
+void Control::drop_data(const Point2 &p_point, const Variant &p_data) {
+ if (data.drag_owner.is_valid()) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ obj->call("_drop_data_fw", p_point, p_data, this);
+ return;
+ }
+ }
+
+ GDVIRTUAL_CALL(_drop_data, p_point, p_data);
+}
+
+void Control::force_drag(const Variant &p_data, Control *p_control) {
+ ERR_FAIL_COND(!is_inside_tree());
+ ERR_FAIL_COND(p_data.get_type() == Variant::NIL);
+
+ get_viewport()->_gui_force_drag(this, p_data, p_control);
+}
+
+void Control::set_drag_preview(Control *p_control) {
+ ERR_FAIL_COND(!is_inside_tree());
+ ERR_FAIL_COND(!get_viewport()->gui_is_dragging());
+ get_viewport()->_gui_set_drag_preview(this, p_control);
+}
+
+bool Control::is_drag_successful() const {
+ return is_inside_tree() && get_viewport()->gui_is_drag_successful();
}
+// Focus.
+
void Control::set_focus_mode(FocusMode p_focus_mode) {
ERR_FAIL_INDEX((int)p_focus_mode, 3);
@@ -2342,6 +1787,41 @@ void Control::set_focus_mode(FocusMode p_focus_mode) {
data.focus_mode = p_focus_mode;
}
+Control::FocusMode Control::get_focus_mode() const {
+ return data.focus_mode;
+}
+
+bool Control::has_focus() const {
+ return is_inside_tree() && get_viewport()->_gui_control_has_focus(this);
+}
+
+void Control::grab_focus() {
+ ERR_FAIL_COND(!is_inside_tree());
+
+ if (data.focus_mode == FOCUS_NONE) {
+ WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus.");
+ return;
+ }
+
+ get_viewport()->_gui_control_grab_focus(this);
+}
+
+void Control::grab_click_focus() {
+ ERR_FAIL_COND(!is_inside_tree());
+
+ get_viewport()->_gui_grab_click_focus(this);
+}
+
+void Control::release_focus() {
+ ERR_FAIL_COND(!is_inside_tree());
+
+ if (!has_focus()) {
+ return;
+ }
+
+ get_viewport()->gui_release_focus();
+}
+
static Control *_next_control(Control *p_from) {
if (p_from->is_set_as_top_level()) {
return nullptr; // Can't go above.
@@ -2520,181 +2000,6 @@ Control *Control::find_prev_valid_focus() const {
return nullptr;
}
-Control::FocusMode Control::get_focus_mode() const {
- return data.focus_mode;
-}
-
-bool Control::has_focus() const {
- return is_inside_tree() && get_viewport()->_gui_control_has_focus(this);
-}
-
-void Control::grab_focus() {
- ERR_FAIL_COND(!is_inside_tree());
-
- if (data.focus_mode == FOCUS_NONE) {
- WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus.");
- return;
- }
-
- get_viewport()->_gui_control_grab_focus(this);
-}
-
-void Control::release_focus() {
- ERR_FAIL_COND(!is_inside_tree());
-
- if (!has_focus()) {
- return;
- }
-
- get_viewport()->gui_release_focus();
-}
-
-bool Control::is_top_level_control() const {
- return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level());
-}
-
-void Control::_propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign) {
- Control *c = Object::cast_to<Control>(p_at);
-
- if (c && c != p_owner && c->data.theme.is_valid()) { // has a theme, this can't be propagated
- return;
- }
-
- Window *w = c == nullptr ? Object::cast_to<Window>(p_at) : nullptr;
-
- if (w && w != p_owner_window && w->theme.is_valid()) { // has a theme, this can't be propagated
- return;
- }
-
- for (int i = 0; i < p_at->get_child_count(); i++) {
- CanvasItem *child = Object::cast_to<CanvasItem>(p_at->get_child(i));
- if (child) {
- _propagate_theme_changed(child, p_owner, p_owner_window, p_assign);
- } else {
- Window *window = Object::cast_to<Window>(p_at->get_child(i));
- if (window) {
- _propagate_theme_changed(window, p_owner, p_owner_window, p_assign);
- }
- }
- }
-
- if (c) {
- if (p_assign) {
- c->data.theme_owner = p_owner;
- c->data.theme_owner_window = p_owner_window;
- }
- c->notification(Control::NOTIFICATION_THEME_CHANGED);
- c->emit_signal(SceneStringNames::get_singleton()->theme_changed);
- }
-
- if (w) {
- if (p_assign) {
- w->theme_owner = p_owner;
- w->theme_owner_window = p_owner_window;
- }
- w->notification(Window::NOTIFICATION_THEME_CHANGED);
- w->emit_signal(SceneStringNames::get_singleton()->theme_changed);
- }
-}
-
-void Control::_theme_changed() {
- _propagate_theme_changed(this, this, nullptr, false);
-}
-
-void Control::_notify_theme_changed() {
- if (!data.bulk_theme_override) {
- notification(NOTIFICATION_THEME_CHANGED);
- }
-}
-
-void Control::set_theme(const Ref<Theme> &p_theme) {
- if (data.theme == p_theme) {
- return;
- }
-
- if (data.theme.is_valid()) {
- data.theme->disconnect("changed", callable_mp(this, &Control::_theme_changed));
- }
-
- data.theme = p_theme;
- if (!p_theme.is_null()) {
- data.theme_owner = this;
- data.theme_owner_window = nullptr;
- _propagate_theme_changed(this, this, nullptr);
- } else {
- Control *parent_c = Object::cast_to<Control>(get_parent());
-
- if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) {
- Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window);
- } else {
- Window *parent_w = cast_to<Window>(get_parent());
- if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) {
- Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window);
- } else {
- Control::_propagate_theme_changed(this, nullptr, nullptr);
- }
- }
- }
-
- if (data.theme.is_valid()) {
- data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), varray(), CONNECT_DEFERRED);
- }
-}
-
-Ref<Theme> Control::get_theme() const {
- return data.theme;
-}
-
-void Control::set_theme_type_variation(const StringName &p_theme_type) {
- data.theme_type_variation = p_theme_type;
- _propagate_theme_changed(this, data.theme_owner, data.theme_owner_window);
-}
-
-StringName Control::get_theme_type_variation() const {
- return data.theme_type_variation;
-}
-
-void Control::set_tooltip(const String &p_tooltip) {
- data.tooltip = p_tooltip;
- update_configuration_warnings();
-}
-
-String Control::get_tooltip(const Point2 &p_pos) const {
- return data.tooltip;
-}
-
-Control *Control::make_custom_tooltip(const String &p_text) const {
- Object *ret = nullptr;
- if (GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret)) {
- return Object::cast_to<Control>(ret);
- }
- return nullptr;
-}
-
-void Control::set_default_cursor_shape(CursorShape p_shape) {
- ERR_FAIL_INDEX(int(p_shape), CURSOR_MAX);
-
- data.default_cursor = p_shape;
-}
-
-Control::CursorShape Control::get_default_cursor_shape() const {
- return data.default_cursor;
-}
-
-Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const {
- return data.default_cursor;
-}
-
-Transform2D Control::get_transform() const {
- Transform2D xform = _get_internal_transform();
- xform[2] += get_position();
- return xform;
-}
-
-String Control::_get_tooltip() const {
- return data.tooltip;
-}
-
void Control::set_focus_neighbor(Side p_side, const NodePath &p_neighbor) {
ERR_FAIL_INDEX((int)p_side, 4);
data.focus_neighbor[p_side] = p_neighbor;
@@ -2861,273 +2166,1011 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons
}
}
-void Control::set_h_size_flags(int p_flags) {
- if (data.h_size_flags == p_flags) {
+// Rendering.
+
+void Control::set_default_cursor_shape(CursorShape p_shape) {
+ ERR_FAIL_INDEX(int(p_shape), CURSOR_MAX);
+
+ data.default_cursor = p_shape;
+}
+
+Control::CursorShape Control::get_default_cursor_shape() const {
+ return data.default_cursor;
+}
+
+Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const {
+ return data.default_cursor;
+}
+
+void Control::set_disable_visibility_clip(bool p_ignore) {
+ data.disable_visibility_clip = p_ignore;
+ update();
+}
+
+bool Control::is_visibility_clip_disabled() const {
+ return data.disable_visibility_clip;
+}
+
+void Control::set_clip_contents(bool p_clip) {
+ data.clip_contents = p_clip;
+ update();
+}
+
+bool Control::is_clipping_contents() {
+ return data.clip_contents;
+}
+
+// Theming.
+
+void Control::_propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign) {
+ Control *c = Object::cast_to<Control>(p_at);
+
+ if (c && c != p_owner && c->data.theme.is_valid()) { // has a theme, this can't be propagated
return;
}
- data.h_size_flags = p_flags;
- emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
+
+ Window *w = c == nullptr ? Object::cast_to<Window>(p_at) : nullptr;
+
+ if (w && w != p_owner_window && w->theme.is_valid()) { // has a theme, this can't be propagated
+ return;
+ }
+
+ for (int i = 0; i < p_at->get_child_count(); i++) {
+ CanvasItem *child = Object::cast_to<CanvasItem>(p_at->get_child(i));
+ if (child) {
+ _propagate_theme_changed(child, p_owner, p_owner_window, p_assign);
+ } else {
+ Window *window = Object::cast_to<Window>(p_at->get_child(i));
+ if (window) {
+ _propagate_theme_changed(window, p_owner, p_owner_window, p_assign);
+ }
+ }
+ }
+
+ if (c) {
+ if (p_assign) {
+ c->data.theme_owner = p_owner;
+ c->data.theme_owner_window = p_owner_window;
+ }
+ c->notification(Control::NOTIFICATION_THEME_CHANGED);
+ c->emit_signal(SceneStringNames::get_singleton()->theme_changed);
+ }
+
+ if (w) {
+ if (p_assign) {
+ w->theme_owner = p_owner;
+ w->theme_owner_window = p_owner_window;
+ }
+ w->notification(Window::NOTIFICATION_THEME_CHANGED);
+ w->emit_signal(SceneStringNames::get_singleton()->theme_changed);
+ }
}
-int Control::get_h_size_flags() const {
- return data.h_size_flags;
+void Control::_theme_changed() {
+ _propagate_theme_changed(this, this, nullptr, false);
}
-void Control::set_v_size_flags(int p_flags) {
- if (data.v_size_flags == p_flags) {
- return;
+void Control::_theme_property_override_changed() {
+ notification(NOTIFICATION_THEME_CHANGED);
+ emit_signal(SceneStringNames::get_singleton()->theme_changed);
+ update_minimum_size(); // Overrides are likely to affect minimum size.
+}
+
+void Control::_notify_theme_changed() {
+ if (!data.bulk_theme_override) {
+ notification(NOTIFICATION_THEME_CHANGED);
}
- data.v_size_flags = p_flags;
- emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
}
-void Control::set_stretch_ratio(real_t p_ratio) {
- if (data.expand == p_ratio) {
+void Control::set_theme(const Ref<Theme> &p_theme) {
+ if (data.theme == p_theme) {
return;
}
- data.expand = p_ratio;
- emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
+ if (data.theme.is_valid()) {
+ data.theme->disconnect("changed", callable_mp(this, &Control::_theme_changed));
+ }
+
+ data.theme = p_theme;
+ if (!p_theme.is_null()) {
+ data.theme_owner = this;
+ data.theme_owner_window = nullptr;
+ _propagate_theme_changed(this, this, nullptr);
+ } else {
+ Control *parent_c = Object::cast_to<Control>(get_parent());
+
+ if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) {
+ Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window);
+ } else {
+ Window *parent_w = cast_to<Window>(get_parent());
+ if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) {
+ Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window);
+ } else {
+ Control::_propagate_theme_changed(this, nullptr, nullptr);
+ }
+ }
+ }
+
+ if (data.theme.is_valid()) {
+ data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), CONNECT_DEFERRED);
+ }
}
-real_t Control::get_stretch_ratio() const {
- return data.expand;
+Ref<Theme> Control::get_theme() const {
+ return data.theme;
}
-void Control::grab_click_focus() {
- ERR_FAIL_COND(!is_inside_tree());
+void Control::set_theme_type_variation(const StringName &p_theme_type) {
+ data.theme_type_variation = p_theme_type;
+ _propagate_theme_changed(this, data.theme_owner, data.theme_owner_window);
+}
- get_viewport()->_gui_grab_click_focus(this);
+StringName Control::get_theme_type_variation() const {
+ return data.theme_type_variation;
}
-void Control::update_minimum_size() {
- if (!is_inside_tree() || data.block_minimum_size_adjust) {
- return;
+/// Theme property lookup.
+
+template <class T>
+T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) {
+ ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified.");
+
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ // For each theme resource check the theme types provided and see if p_name exists with any of them.
+ for (const StringName &E : p_theme_types) {
+ if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
+ return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E);
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) {
+ return theme_owner_window->theme->get_theme_item(p_data_type, p_name, E);
+ }
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
}
- Control *invalidate = this;
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ for (const StringName &E : p_theme_types) {
+ if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) {
+ return Theme::get_project_default()->get_theme_item(p_data_type, p_name, E);
+ }
+ }
+ }
- //invalidate cache upwards
- while (invalidate && invalidate->data.minimum_size_valid) {
- invalidate->data.minimum_size_valid = false;
- if (invalidate->is_set_as_top_level()) {
- break; // do not go further up
+ // Lastly, fall back on the items defined in the default Theme, if they exist.
+ for (const StringName &E : p_theme_types) {
+ if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) {
+ return Theme::get_default()->get_theme_item(p_data_type, p_name, E);
}
- if (!invalidate->data.parent && get_parent()) {
- Window *parent_window = Object::cast_to<Window>(get_parent());
- if (parent_window && parent_window->is_wrapping_controls()) {
- parent_window->child_controls_changed();
+ }
+ // If they don't exist, use any type to return the default/empty value.
+ return Theme::get_default()->get_theme_item(p_data_type, p_name, p_theme_types[0]);
+}
+
+bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) {
+ ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified.");
+
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ // For each theme resource check the theme types provided and see if p_name exists with any of them.
+ for (const StringName &E : p_theme_types) {
+ if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
+ return true;
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) {
+ return true;
+ }
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
}
}
- invalidate = invalidate->data.parent;
}
- if (!is_visible_in_tree()) {
- return;
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ for (const StringName &E : p_theme_types) {
+ if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) {
+ return true;
+ }
+ }
}
- if (data.updating_last_minimum_size) {
- return;
+ // Lastly, fall back on the items defined in the default Theme, if they exist.
+ for (const StringName &E : p_theme_types) {
+ if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) {
+ return true;
+ }
}
+ return false;
+}
- data.updating_last_minimum_size = true;
+void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(data.theme_type_variation) != StringName()) {
+ Theme::get_project_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list);
+ } else {
+ Theme::get_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list);
+ }
+ } else {
+ Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list);
+ }
+}
- MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
+Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
+ if (tex) {
+ return *tex;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<Ref<Texture2D>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types);
}
-int Control::get_v_size_flags() const {
- return data.v_size_flags;
+Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const Ref<StyleBox> *style = data.style_override.getptr(p_name);
+ if (style) {
+ return *style;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<Ref<StyleBox>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
}
-void Control::set_mouse_filter(MouseFilter p_filter) {
- ERR_FAIL_INDEX(p_filter, 3);
- data.mouse_filter = p_filter;
- notify_property_list_changed();
- update_configuration_warnings();
+Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const Ref<Font> *font = data.font_override.getptr(p_name);
+ if (font) {
+ return *font;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<Ref<Font>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types);
}
-Control::MouseFilter Control::get_mouse_filter() const {
- return data.mouse_filter;
+int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const int *font_size = data.font_size_override.getptr(p_name);
+ if (font_size && (*font_size) > 0) {
+ return *font_size;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
}
-void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) {
- data.force_pass_scroll_events = p_force_pass_scroll_events;
+Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const Color *color = data.color_override.getptr(p_name);
+ if (color) {
+ return *color;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<Color>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types);
}
-bool Control::is_force_pass_scroll_events() const {
- return data.force_pass_scroll_events;
+int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const int *constant = data.constant_override.getptr(p_name);
+ if (constant) {
+ return *constant;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
-void Control::warp_mouse(const Point2 &p_position) {
- ERR_FAIL_COND(!is_inside_tree());
- get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position));
+bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_icon_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types);
}
-bool Control::is_text_field() const {
- return false;
+bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_stylebox_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
}
-Array Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
- if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) {
- Array ret;
- if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret)) {
- return ret;
- } else {
- return Array();
+bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_font_override(p_name)) {
+ return true;
}
- } else {
- return TS->parse_structured_text(p_parser_type, p_args, p_text);
}
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types);
}
-void Control::set_rotation(real_t p_radians) {
- data.rotation = p_radians;
- update();
- _notify_transform();
+bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_font_size_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
}
-real_t Control::get_rotation() const {
- return data.rotation;
+bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_color_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types);
}
-void Control::_override_changed() {
- notification(NOTIFICATION_THEME_CHANGED);
- emit_signal(SceneStringNames::get_singleton()->theme_changed);
- update_minimum_size(); // Overrides are likely to affect minimum size.
+bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_constant_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
-void Control::set_pivot_offset(const Vector2 &p_pivot) {
- data.pivot_offset = p_pivot;
- update();
- _notify_transform();
+/// Local property overrides.
+
+void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) {
+ ERR_FAIL_COND(!p_icon.is_valid());
+
+ if (data.icon_override.has(p_name)) {
+ data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
+ }
+
+ data.icon_override[p_name] = p_icon;
+ data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), CONNECT_REFERENCE_COUNTED);
+ _notify_theme_changed();
}
-Vector2 Control::get_pivot_offset() const {
- return data.pivot_offset;
+void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
+ ERR_FAIL_COND(!p_style.is_valid());
+
+ if (data.style_override.has(p_name)) {
+ data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
+ }
+
+ data.style_override[p_name] = p_style;
+ data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), CONNECT_REFERENCE_COUNTED);
+ _notify_theme_changed();
}
-void Control::set_scale(const Vector2 &p_scale) {
- data.scale = p_scale;
- // Avoid having 0 scale values, can lead to errors in physics and rendering.
- if (data.scale.x == 0) {
- data.scale.x = CMP_EPSILON;
+void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) {
+ ERR_FAIL_COND(!p_font.is_valid());
+
+ if (data.font_override.has(p_name)) {
+ data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
- if (data.scale.y == 0) {
- data.scale.y = CMP_EPSILON;
+
+ data.font_override[p_name] = p_font;
+ data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), CONNECT_REFERENCE_COUNTED);
+ _notify_theme_changed();
+}
+
+void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) {
+ data.font_size_override[p_name] = p_font_size;
+ _notify_theme_changed();
+}
+
+void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) {
+ data.color_override[p_name] = p_color;
+ _notify_theme_changed();
+}
+
+void Control::add_theme_constant_override(const StringName &p_name, int p_constant) {
+ data.constant_override[p_name] = p_constant;
+ _notify_theme_changed();
+}
+
+void Control::remove_theme_icon_override(const StringName &p_name) {
+ if (data.icon_override.has(p_name)) {
+ data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
- update();
- _notify_transform();
+
+ data.icon_override.erase(p_name);
+ _notify_theme_changed();
}
-Vector2 Control::get_scale() const {
- return data.scale;
+void Control::remove_theme_style_override(const StringName &p_name) {
+ if (data.style_override.has(p_name)) {
+ data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
+ }
+
+ data.style_override.erase(p_name);
+ _notify_theme_changed();
}
-Control *Control::get_root_parent_control() const {
- const CanvasItem *ci = this;
- const Control *root = this;
+void Control::remove_theme_font_override(const StringName &p_name) {
+ if (data.font_override.has(p_name)) {
+ data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
+ }
- while (ci) {
- const Control *c = Object::cast_to<Control>(ci);
- if (c) {
- root = c;
+ data.font_override.erase(p_name);
+ _notify_theme_changed();
+}
- if (c->data.RI || c->is_top_level_control()) {
- break;
+void Control::remove_theme_font_size_override(const StringName &p_name) {
+ data.font_size_override.erase(p_name);
+ _notify_theme_changed();
+}
+
+void Control::remove_theme_color_override(const StringName &p_name) {
+ data.color_override.erase(p_name);
+ _notify_theme_changed();
+}
+
+void Control::remove_theme_constant_override(const StringName &p_name) {
+ data.constant_override.erase(p_name);
+ _notify_theme_changed();
+}
+
+bool Control::has_theme_icon_override(const StringName &p_name) const {
+ const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
+ return tex != nullptr;
+}
+
+bool Control::has_theme_stylebox_override(const StringName &p_name) const {
+ const Ref<StyleBox> *style = data.style_override.getptr(p_name);
+ return style != nullptr;
+}
+
+bool Control::has_theme_font_override(const StringName &p_name) const {
+ const Ref<Font> *font = data.font_override.getptr(p_name);
+ return font != nullptr;
+}
+
+bool Control::has_theme_font_size_override(const StringName &p_name) const {
+ const int *font_size = data.font_size_override.getptr(p_name);
+ return font_size != nullptr;
+}
+
+bool Control::has_theme_color_override(const StringName &p_name) const {
+ const Color *color = data.color_override.getptr(p_name);
+ return color != nullptr;
+}
+
+bool Control::has_theme_constant_override(const StringName &p_name) const {
+ const int *constant = data.constant_override.getptr(p_name);
+ return constant != nullptr;
+}
+
+/// Default theme properties.
+
+float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_base_scale()) {
+ return theme_owner->data.theme->get_default_base_scale();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_base_scale()) {
+ return theme_owner_window->theme->get_default_base_scale();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
}
}
+ }
- ci = ci->get_parent_item();
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_base_scale()) {
+ return Theme::get_project_default()->get_default_base_scale();
+ }
}
- return const_cast<Control *>(root);
+ // Lastly, fall back on the default Theme.
+ if (Theme::get_default()->has_default_base_scale()) {
+ return Theme::get_default()->get_default_base_scale();
+ }
+ return Theme::get_fallback_base_scale();
}
-void Control::set_block_minimum_size_adjust(bool p_block) {
- data.block_minimum_size_adjust = p_block;
+float Control::get_theme_default_base_scale() const {
+ return fetch_theme_default_base_scale(data.theme_owner, data.theme_owner_window);
}
-bool Control::is_minimum_size_adjust_blocked() const {
- return data.block_minimum_size_adjust;
-}
+Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
-void Control::set_disable_visibility_clip(bool p_ignore) {
- data.disable_visibility_clip = p_ignore;
- update();
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_font()) {
+ return theme_owner->data.theme->get_default_font();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_font()) {
+ return theme_owner_window->theme->get_default_font();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
+ }
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_font()) {
+ return Theme::get_project_default()->get_default_font();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ if (Theme::get_default()->has_default_font()) {
+ return Theme::get_default()->get_default_font();
+ }
+ return Theme::get_fallback_font();
}
-bool Control::is_visibility_clip_disabled() const {
- return data.disable_visibility_clip;
+Ref<Font> Control::get_theme_default_font() const {
+ return fetch_theme_default_font(data.theme_owner, data.theme_owner_window);
}
-void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
- Node::get_argument_options(p_function, p_idx, r_options);
+int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
- if (p_idx == 0) {
- List<StringName> sn;
- String pf = p_function;
- if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") {
- Theme::get_default()->get_color_list(get_class(), &sn);
- } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") {
- Theme::get_default()->get_stylebox_list(get_class(), &sn);
- } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") {
- Theme::get_default()->get_font_list(get_class(), &sn);
- } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") {
- Theme::get_default()->get_font_size_list(get_class(), &sn);
- } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") {
- Theme::get_default()->get_constant_list(get_class(), &sn);
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_font_size()) {
+ return theme_owner->data.theme->get_default_font_size();
}
- sn.sort_custom<StringName::AlphCompare>();
- for (const StringName &name : sn) {
- r_options->push_back(String(name).quote());
+ if (theme_owner_window && theme_owner_window->theme->has_default_font_size()) {
+ return theme_owner_window->theme->get_default_font_size();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
}
}
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_font_size()) {
+ return Theme::get_project_default()->get_default_font_size();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ if (Theme::get_default()->has_default_font_size()) {
+ return Theme::get_default()->get_default_font_size();
+ }
+ return Theme::get_fallback_font_size();
}
-TypedArray<String> Control::get_configuration_warnings() const {
- TypedArray<String> warnings = Node::get_configuration_warnings();
+int Control::get_theme_default_font_size() const {
+ return fetch_theme_default_font_size(data.theme_owner, data.theme_owner_window);
+}
- if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
- warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
+/// Bulk actions.
+
+void Control::begin_bulk_theme_override() {
+ data.bulk_theme_override = true;
+}
+
+void Control::end_bulk_theme_override() {
+ ERR_FAIL_COND(!data.bulk_theme_override);
+
+ data.bulk_theme_override = false;
+ _notify_theme_changed();
+}
+
+// Internationalization.
+
+Array Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
+ if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) {
+ Array ret;
+ if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret)) {
+ return ret;
+ } else {
+ return Array();
+ }
+ } else {
+ return TS->parse_structured_text(p_parser_type, p_args, p_text);
}
+}
- return warnings;
+void Control::set_layout_direction(Control::LayoutDirection p_direction) {
+ ERR_FAIL_INDEX((int)p_direction, 4);
+
+ data.layout_dir = p_direction;
+ data.is_rtl_dirty = true;
+
+ propagate_notification(NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
}
-void Control::set_clip_contents(bool p_clip) {
- data.clip_contents = p_clip;
- update();
+Control::LayoutDirection Control::get_layout_direction() const {
+ return data.layout_dir;
}
-bool Control::is_clipping_contents() {
- return data.clip_contents;
+bool Control::is_layout_rtl() const {
+ if (data.is_rtl_dirty) {
+ const_cast<Control *>(this)->data.is_rtl_dirty = false;
+ if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) {
+ Window *parent_window = get_parent_window();
+ Control *parent_control = get_parent_control();
+ if (parent_control) {
+ const_cast<Control *>(this)->data.is_rtl = parent_control->is_layout_rtl();
+ } else if (parent_window) {
+ const_cast<Control *>(this)->data.is_rtl = parent_window->is_layout_rtl();
+ } else {
+ if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
+ const_cast<Control *>(this)->data.is_rtl = true;
+ } else {
+ String locale = TranslationServer::get_singleton()->get_tool_locale();
+ const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ }
+ }
+ } else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
+ if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
+ const_cast<Control *>(this)->data.is_rtl = true;
+ } else {
+ String locale = TranslationServer::get_singleton()->get_tool_locale();
+ const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ }
+ } else {
+ const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);
+ }
+ }
+ return data.is_rtl;
}
-void Control::set_h_grow_direction(GrowDirection p_direction) {
- ERR_FAIL_INDEX((int)p_direction, 3);
+void Control::set_auto_translate(bool p_enable) {
+ if (p_enable == data.auto_translate) {
+ return;
+ }
- data.h_grow = p_direction;
- _size_changed();
+ data.auto_translate = p_enable;
+
+ notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
-Control::GrowDirection Control::get_h_grow_direction() const {
- return data.h_grow;
+bool Control::is_auto_translating() const {
+ return data.auto_translate;
}
-void Control::set_v_grow_direction(GrowDirection p_direction) {
- ERR_FAIL_INDEX((int)p_direction, 3);
+// Extra properties.
- data.v_grow = p_direction;
- _size_changed();
+void Control::set_tooltip(const String &p_tooltip) {
+ data.tooltip = p_tooltip;
+ update_configuration_warnings();
}
-Control::GrowDirection Control::get_v_grow_direction() const {
- return data.v_grow;
+String Control::_get_tooltip() const {
+ return data.tooltip;
+}
+
+String Control::get_tooltip(const Point2 &p_pos) const {
+ return data.tooltip;
+}
+
+Control *Control::make_custom_tooltip(const String &p_text) const {
+ Object *ret = nullptr;
+ if (GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret)) {
+ return Object::cast_to<Control>(ret);
+ }
+ return nullptr;
+}
+
+// Base object overrides.
+
+void Control::add_child_notify(Node *p_child) {
+ Control *child_c = Object::cast_to<Control>(p_child);
+
+ if (child_c && child_c->data.theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
+ _propagate_theme_changed(child_c, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
+ }
+
+ Window *child_w = Object::cast_to<Window>(p_child);
+
+ if (child_w && child_w->theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
+ _propagate_theme_changed(child_w, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
+ }
+}
+
+void Control::remove_child_notify(Node *p_child) {
+ Control *child_c = Object::cast_to<Control>(p_child);
+
+ if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) {
+ _propagate_theme_changed(child_c, nullptr, nullptr);
+ }
+
+ Window *child_w = Object::cast_to<Window>(p_child);
+
+ if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) {
+ _propagate_theme_changed(child_w, nullptr, nullptr);
+ }
+}
+
+void Control::_notification(int p_notification) {
+ switch (p_notification) {
+ case NOTIFICATION_POST_ENTER_TREE: {
+ data.minimum_size_valid = false;
+ data.is_rtl_dirty = true;
+ _size_changed();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ release_focus();
+ get_viewport()->_gui_remove_control(this);
+ } break;
+
+ case NOTIFICATION_READY: {
+#ifdef DEBUG_ENABLED
+ connect("ready", callable_mp(this, &Control::_clear_size_warning), CONNECT_DEFERRED | CONNECT_ONESHOT);
+#endif
+ } break;
+
+ case NOTIFICATION_ENTER_CANVAS: {
+ data.parent = Object::cast_to<Control>(get_parent());
+ data.parent_window = Object::cast_to<Window>(get_parent());
+ data.is_rtl_dirty = true;
+
+ if (data.theme.is_null()) {
+ if (data.parent && (data.parent->data.theme_owner || data.parent->data.theme_owner_window)) {
+ data.theme_owner = data.parent->data.theme_owner;
+ data.theme_owner_window = data.parent->data.theme_owner_window;
+ notification(NOTIFICATION_THEME_CHANGED);
+ } else if (data.parent_window && (data.parent_window->theme_owner || data.parent_window->theme_owner_window)) {
+ data.theme_owner = data.parent_window->theme_owner;
+ data.theme_owner_window = data.parent_window->theme_owner_window;
+ notification(NOTIFICATION_THEME_CHANGED);
+ }
+ }
+
+ CanvasItem *node = this;
+ bool has_parent_control = false;
+
+ while (!node->is_set_as_top_level()) {
+ CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent());
+ if (!parent) {
+ break;
+ }
+
+ Control *parent_control = Object::cast_to<Control>(parent);
+ if (parent_control) {
+ has_parent_control = true;
+ break;
+ }
+
+ node = parent;
+ }
+
+ if (has_parent_control) {
+ // Do nothing, has a parent control.
+ } else {
+ // Is a regular root control or top_level.
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ data.RI = viewport->_gui_add_root_control(this);
+ }
+
+ data.parent_canvas_item = get_parent_item();
+
+ if (data.parent_canvas_item) {
+ data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed));
+ } else {
+ // Connect viewport.
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ viewport->connect("size_changed", callable_mp(this, &Control::_size_changed));
+ }
+ } break;
+
+ case NOTIFICATION_EXIT_CANVAS: {
+ if (data.parent_canvas_item) {
+ data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed));
+ data.parent_canvas_item = nullptr;
+ } else if (!is_set_as_top_level()) {
+ //disconnect viewport
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed));
+ }
+
+ if (data.RI) {
+ get_viewport()->_gui_remove_root_control(data.RI);
+ data.RI = nullptr;
+ }
+
+ data.parent = nullptr;
+ data.parent_canvas_item = nullptr;
+ data.parent_window = nullptr;
+ data.is_rtl_dirty = true;
+ } break;
+
+ case NOTIFICATION_MOVED_IN_PARENT: {
+ // some parents need to know the order of the children to draw (like TabContainer)
+ // update if necessary
+ if (data.parent) {
+ data.parent->update();
+ }
+ update();
+
+ if (data.RI) {
+ get_viewport()->_gui_set_root_order_dirty();
+ }
+ } break;
+
+ case NOTIFICATION_RESIZED: {
+ emit_signal(SceneStringNames::get_singleton()->resized);
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ _update_canvas_item_transform();
+ RenderingServer::get_singleton()->canvas_item_set_custom_rect(get_canvas_item(), !data.disable_visibility_clip, Rect2(Point2(), get_size()));
+ RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), data.clip_contents);
+ } break;
+
+ case NOTIFICATION_MOUSE_ENTER: {
+ emit_signal(SceneStringNames::get_singleton()->mouse_entered);
+ } break;
+
+ case NOTIFICATION_MOUSE_EXIT: {
+ emit_signal(SceneStringNames::get_singleton()->mouse_exited);
+ } break;
+
+ case NOTIFICATION_FOCUS_ENTER: {
+ emit_signal(SceneStringNames::get_singleton()->focus_entered);
+ update();
+ } break;
+
+ case NOTIFICATION_FOCUS_EXIT: {
+ emit_signal(SceneStringNames::get_singleton()->focus_exited);
+ update();
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ update_minimum_size();
+ update();
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible_in_tree()) {
+ if (get_viewport() != nullptr) {
+ get_viewport()->_gui_hide_control(this);
+ }
+ } else {
+ data.minimum_size_valid = false;
+ _update_minimum_size();
+ _size_changed();
+ }
+ } break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ if (is_inside_tree()) {
+ data.is_rtl_dirty = true;
+ _size_changed();
+ }
+ } break;
+ }
}
void Control::_bind_methods() {
- //ClassDB::bind_method(D_METHOD("_window_resize_event"),&Control::_window_resize_event);
ClassDB::bind_method(D_METHOD("_update_minimum_size"), &Control::_update_minimum_size);
ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 50cf9faeed..9f17eccc3b 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -159,13 +159,16 @@ private:
};
struct Data {
- Point2 pos_cache;
- Size2 size_cache;
- Size2 minimum_size_cache;
- bool minimum_size_valid = false;
+ // Global relations.
- Size2 last_minimum_size;
- bool updating_last_minimum_size = false;
+ List<Control *>::Element *RI = nullptr;
+
+ Control *parent = nullptr;
+ Window *parent_window = nullptr;
+ CanvasItem *parent_canvas_item = nullptr;
+ ObjectID drag_owner;
+
+ // Positioning and sizing.
real_t offset[4] = { 0.0, 0.0, 0.0, 0.0 };
real_t anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN };
@@ -173,49 +176,51 @@ private:
GrowDirection h_grow = GROW_DIRECTION_END;
GrowDirection v_grow = GROW_DIRECTION_END;
- LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
- bool is_rtl_dirty = true;
- bool is_rtl = false;
-
- bool auto_translate = true;
-
real_t rotation = 0.0;
Vector2 scale = Vector2(1, 1);
Vector2 pivot_offset;
+
+ Point2 pos_cache;
+ Size2 size_cache;
+ Size2 minimum_size_cache;
+ bool minimum_size_valid = false;
+
+ Size2 last_minimum_size;
+ bool updating_last_minimum_size = false;
+ bool block_minimum_size_adjust = false;
+
bool size_warning = true;
+ // Container sizing.
+
int h_size_flags = SIZE_FILL;
int v_size_flags = SIZE_FILL;
real_t expand = 1.0;
Point2 custom_minimum_size;
+ // Input events and rendering.
+
MouseFilter mouse_filter = MOUSE_FILTER_STOP;
bool force_pass_scroll_events = true;
bool clip_contents = false;
-
- bool block_minimum_size_adjust = false;
bool disable_visibility_clip = false;
- Control *parent = nullptr;
- ObjectID drag_owner;
- Ref<Theme> theme;
- Control *theme_owner = nullptr;
- Window *theme_owner_window = nullptr;
- Window *parent_window = nullptr;
- StringName theme_type_variation;
-
- String tooltip;
CursorShape default_cursor = CURSOR_ARROW;
- List<Control *>::Element *RI = nullptr;
-
- CanvasItem *parent_canvas_item = nullptr;
+ // Focus.
NodePath focus_neighbor[4];
NodePath focus_next;
NodePath focus_prev;
+ // Theming.
+
+ Ref<Theme> theme;
+ Control *theme_owner = nullptr;
+ Window *theme_owner_window = nullptr;
+ StringName theme_type_variation;
+
bool bulk_theme_override = false;
Theme::ThemeIconMap icon_override;
Theme::ThemeStyleMap style_override;
@@ -224,50 +229,69 @@ private:
Theme::ThemeColorMap color_override;
Theme::ThemeConstantMap constant_override;
+ // Internationalization.
+
+ LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
+ bool is_rtl_dirty = true;
+ bool is_rtl = false;
+
+ bool auto_translate = true;
+
+ // Extra properties.
+
+ String tooltip;
+
} data;
+ // Dynamic properties.
+
static constexpr unsigned properties_managed_by_container_count = 12;
static String properties_managed_by_container[properties_managed_by_container_count];
- void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
- Control *_get_focus_neighbor(Side p_side, int p_count = 0);
+ // Global relations.
+
+ friend class Viewport;
+ friend class Window;
+
+ // Positioning and sizing.
+
+ void _update_canvas_item_transform();
+ Transform2D _get_internal_transform() const;
void _set_anchor(Side p_side, real_t p_anchor);
void _set_position(const Point2 &p_point);
void _set_global_position(const Point2 &p_point);
void _set_size(const Size2 &p_size);
+ void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
+ void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
+
void _set_layout_mode(LayoutMode p_mode);
LayoutMode _get_layout_mode() const;
-
void _set_anchors_layout_preset(int p_preset);
int _get_anchors_layout_preset() const;
- void _theme_changed();
- void _notify_theme_changed();
-
+ void _update_minimum_size_cache();
void _update_minimum_size();
+ void _size_changed();
void _clear_size_warning();
- void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
- void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
-
- void _size_changed();
- String _get_tooltip() const;
+ // Input events.
- void _override_changed();
+ void _call_gui_input(const Ref<InputEvent> &p_event);
- void _update_canvas_item_transform();
+ // Focus.
- Transform2D _get_internal_transform() const;
+ void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
+ Control *_get_focus_neighbor(Side p_side, int p_count = 0);
- friend class Viewport;
+ // Theming.
- void _call_gui_input(const Ref<InputEvent> &p_event);
+ void _theme_changed();
+ void _theme_property_override_changed();
+ void _notify_theme_changed();
- void _update_minimum_size_cache();
- friend class Window;
static void _propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign = true);
template <class T>
@@ -275,24 +299,31 @@ private:
static bool has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types);
_FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const;
-protected:
- virtual void add_child_notify(Node *p_child) override;
- virtual void remove_child_notify(Node *p_child) override;
+ // Extra properties.
- //virtual void _window_gui_input(InputEvent p_event);
+ String _get_tooltip() const;
- virtual Array structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
+protected:
+ // Dynamic properties.
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
-
virtual void _validate_property(PropertyInfo &property) const override;
+ // Internationalization.
+
+ virtual Array structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
+
+ // Base object overrides.
+
+ virtual void add_child_notify(Node *p_child) override;
+ virtual void remove_child_notify(Node *p_child) override;
+
void _notification(int p_notification);
static void _bind_methods();
- //bind helpers
+ // Exposed virtual methods.
GDVIRTUAL1RC(bool, _has_point, Vector2)
GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
@@ -307,8 +338,6 @@ protected:
public:
enum {
- /* NOTIFICATION_DRAW=30,
- NOTIFICATION_VISIBILITY_CHANGED=38*/
NOTIFICATION_RESIZED = 40,
NOTIFICATION_MOUSE_ENTER = 41,
NOTIFICATION_MOUSE_EXIT = 42,
@@ -318,10 +347,11 @@ public:
NOTIFICATION_SCROLL_BEGIN = 47,
NOTIFICATION_SCROLL_END = 48,
NOTIFICATION_LAYOUT_DIRECTION_CHANGED = 49,
-
};
- /* EDITOR */
+ // Editor plugin interoperability.
+
+ // TODO: Decouple controls from their editor plugin and get rid of this.
#ifdef TOOLS_ENABLED
virtual Dictionary _edit_get_state() const override;
virtual void _edit_set_state(const Dictionary &p_state) override;
@@ -347,56 +377,50 @@ public:
virtual Size2 _edit_get_minimum_size() const override;
#endif
- virtual void gui_input(const Ref<InputEvent> &p_event);
+ // Editor integration.
- void accept_event();
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
+ TypedArray<String> get_configuration_warnings() const override;
- virtual Size2 get_minimum_size() const;
- virtual Size2 get_combined_minimum_size() const;
- virtual bool has_point(const Point2 &p_point) const;
- virtual void set_drag_forwarding(Object *p_target);
- virtual Variant get_drag_data(const Point2 &p_point);
- virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
- virtual void drop_data(const Point2 &p_point, const Variant &p_data);
- void set_drag_preview(Control *p_control);
- void force_drag(const Variant &p_data, Control *p_control);
- bool is_drag_successful() const;
+ virtual bool is_text_field() const;
- void set_custom_minimum_size(const Size2 &p_custom);
- Size2 get_custom_minimum_size() const;
+ // Global relations.
+
+ bool is_top_level_control() const;
Control *get_parent_control() const;
Window *get_parent_window() const;
+ Control *get_root_parent_control() const;
- void set_layout_direction(LayoutDirection p_direction);
- LayoutDirection get_layout_direction() const;
- virtual bool is_layout_rtl() const;
-
- void set_auto_translate(bool p_enable);
- bool is_auto_translating() const;
- _FORCE_INLINE_ String atr(const String p_string) const { return is_auto_translating() ? tr(p_string) : p_string; };
+ Size2 get_parent_area_size() const;
+ Rect2 get_parent_anchorable_rect() const;
- /* POSITIONING */
+ // Positioning and sizing.
- void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true);
- void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
- void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
- void set_grow_direction_preset(LayoutPreset p_preset);
+ virtual Transform2D get_transform() const override;
void set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true);
real_t get_anchor(Side p_side) const;
-
void set_offset(Side p_side, real_t p_value);
real_t get_offset(Side p_side) const;
-
void set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor = true);
- void set_begin(const Point2 &p_point); // helper
- void set_end(const Point2 &p_point); // helper
-
+ // TODO: Rename to set_begin/end_offsets ?
+ void set_begin(const Point2 &p_point);
Point2 get_begin() const;
+ void set_end(const Point2 &p_point);
Point2 get_end() const;
+ void set_h_grow_direction(GrowDirection p_direction);
+ GrowDirection get_h_grow_direction() const;
+ void set_v_grow_direction(GrowDirection p_direction);
+ GrowDirection get_v_grow_direction() const;
+
+ void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true);
+ void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
+ void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
+ void set_grow_direction_preset(LayoutPreset p_preset);
+
void set_position(const Point2 &p_point, bool p_keep_offsets = false);
void set_global_position(const Point2 &p_point, bool p_keep_offsets = false);
Point2 get_position() const;
@@ -407,52 +431,72 @@ public:
Size2 get_size() const;
void reset_size();
+ void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting.
Rect2 get_rect() const;
Rect2 get_global_rect() const;
Rect2 get_screen_rect() const;
Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the rendering server
Rect2 get_anchorable_rect() const override;
- void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting.
-
+ void set_scale(const Vector2 &p_scale);
+ Vector2 get_scale() const;
void set_rotation(real_t p_radians);
real_t get_rotation() const;
-
- void set_h_grow_direction(GrowDirection p_direction);
- GrowDirection get_h_grow_direction() const;
-
- void set_v_grow_direction(GrowDirection p_direction);
- GrowDirection get_v_grow_direction() const;
-
void set_pivot_offset(const Vector2 &p_pivot);
Vector2 get_pivot_offset() const;
- void set_scale(const Vector2 &p_scale);
- Vector2 get_scale() const;
+ void update_minimum_size();
- void set_theme(const Ref<Theme> &p_theme);
- Ref<Theme> get_theme() const;
+ void set_block_minimum_size_adjust(bool p_block);
+ bool is_minimum_size_adjust_blocked() const;
- void set_theme_type_variation(const StringName &p_theme_type);
- StringName get_theme_type_variation() const;
+ virtual Size2 get_minimum_size() const;
+ virtual Size2 get_combined_minimum_size() const;
+
+ void set_custom_minimum_size(const Size2 &p_custom);
+ Size2 get_custom_minimum_size() const;
+
+ // Container sizing.
void set_h_size_flags(int p_flags);
int get_h_size_flags() const;
-
void set_v_size_flags(int p_flags);
int get_v_size_flags() const;
-
void set_stretch_ratio(real_t p_ratio);
real_t get_stretch_ratio() const;
- void update_minimum_size();
+ // Input events.
+
+ virtual void gui_input(const Ref<InputEvent> &p_event);
+ void accept_event();
+
+ virtual bool has_point(const Point2 &p_point) const;
+
+ void set_mouse_filter(MouseFilter p_filter);
+ MouseFilter get_mouse_filter() const;
+
+ void set_force_pass_scroll_events(bool p_force_pass_scroll_events);
+ bool is_force_pass_scroll_events() const;
+
+ void warp_mouse(const Point2 &p_position);
+
+ // Drag and drop handling.
+
+ virtual void set_drag_forwarding(Object *p_target);
+ virtual Variant get_drag_data(const Point2 &p_point);
+ virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
+ virtual void drop_data(const Point2 &p_point, const Variant &p_data);
+ void set_drag_preview(Control *p_control);
+ void force_drag(const Variant &p_data, Control *p_control);
+ bool is_drag_successful() const;
- /* FOCUS */
+ // Focus.
void set_focus_mode(FocusMode p_focus_mode);
FocusMode get_focus_mode() const;
bool has_focus() const;
void grab_focus();
+ void grab_click_focus();
void release_focus();
Control *find_next_valid_focus() const;
@@ -466,13 +510,25 @@ public:
void set_focus_previous(const NodePath &p_prev);
NodePath get_focus_previous() const;
- void set_mouse_filter(MouseFilter p_filter);
- MouseFilter get_mouse_filter() const;
+ // Rendering.
- void set_force_pass_scroll_events(bool p_force_pass_scroll_events);
- bool is_force_pass_scroll_events() const;
+ void set_default_cursor_shape(CursorShape p_shape);
+ CursorShape get_default_cursor_shape() const;
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
+
+ void set_clip_contents(bool p_clip);
+ bool is_clipping_contents();
- /* SKINNING */
+ void set_disable_visibility_clip(bool p_ignore);
+ bool is_visibility_clip_disabled() const;
+
+ // Theming.
+
+ void set_theme(const Ref<Theme> &p_theme);
+ Ref<Theme> get_theme() const;
+
+ void set_theme_type_variation(const StringName &p_theme_type);
+ StringName get_theme_type_variation() const;
void begin_bulk_theme_override();
void end_bulk_theme_override();
@@ -520,44 +576,23 @@ public:
Ref<Font> get_theme_default_font() const;
int get_theme_default_font_size() const;
- /* TOOLTIP */
-
- void set_tooltip(const String &p_tooltip);
- virtual String get_tooltip(const Point2 &p_pos) const;
- virtual Control *make_custom_tooltip(const String &p_text) const;
-
- /* CURSOR */
-
- void set_default_cursor_shape(CursorShape p_shape);
- CursorShape get_default_cursor_shape() const;
- virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
-
- virtual Transform2D get_transform() const override;
-
- bool is_top_level_control() const;
+ // Internationalization.
- Size2 get_parent_area_size() const;
- Rect2 get_parent_anchorable_rect() const;
-
- void grab_click_focus();
-
- void warp_mouse(const Point2 &p_position);
-
- virtual bool is_text_field() const;
-
- Control *get_root_parent_control() const;
-
- void set_clip_contents(bool p_clip);
- bool is_clipping_contents();
+ void set_layout_direction(LayoutDirection p_direction);
+ LayoutDirection get_layout_direction() const;
+ virtual bool is_layout_rtl() const;
- void set_block_minimum_size_adjust(bool p_block);
- bool is_minimum_size_adjust_blocked() const;
+ void set_auto_translate(bool p_enable);
+ bool is_auto_translating() const;
+ _FORCE_INLINE_ String atr(const String p_string) const {
+ return is_auto_translating() ? tr(p_string) : p_string;
+ };
- void set_disable_visibility_clip(bool p_ignore);
- bool is_visibility_clip_disabled() const;
+ // Extra properties.
- virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
- TypedArray<String> get_configuration_warnings() const override;
+ void set_tooltip(const String &p_tooltip);
+ virtual String get_tooltip(const Point2 &p_pos) const;
+ virtual Control *make_custom_tooltip(const String &p_text) const;
Control() {}
};
@@ -574,4 +609,4 @@ VARIANT_ENUM_CAST(Control::LayoutMode);
VARIANT_ENUM_CAST(Control::LayoutDirection);
VARIANT_ENUM_CAST(Control::TextDirection);
-#endif
+#endif // CONTROL_H
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 44e2bb89eb..942b7d0bf9 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -261,7 +261,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin
}
if (!p_action.is_empty()) {
- button->connect("pressed", callable_mp(this, &AcceptDialog::_custom_action), varray(p_action));
+ button->connect("pressed", callable_mp(this, &AcceptDialog::_custom_action).bind(p_action));
}
return button;
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index 711361de88..9ebf5ddfb2 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -120,4 +120,4 @@ public:
ConfirmationDialog();
};
-#endif
+#endif // DIALOGS_H
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 65bc359e2e..e26976a402 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -1083,9 +1083,9 @@ FileDialog::FileDialog() {
_update_drives();
connect("confirmed", callable_mp(this, &FileDialog::_action_pressed));
- tree->connect("multi_selected", callable_mp(this, &FileDialog::_tree_multi_selected), varray(), CONNECT_DEFERRED);
- tree->connect("cell_selected", callable_mp(this, &FileDialog::_tree_selected), varray(), CONNECT_DEFERRED);
- tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated), varray());
+ tree->connect("multi_selected", callable_mp(this, &FileDialog::_tree_multi_selected), CONNECT_DEFERRED);
+ tree->connect("cell_selected", callable_mp(this, &FileDialog::_tree_selected), CONNECT_DEFERRED);
+ tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated));
tree->connect("nothing_selected", callable_mp(this, &FileDialog::deselect_all));
dir->connect("text_submitted", callable_mp(this, &FileDialog::_dir_submitted));
file->connect("text_submitted", callable_mp(this, &FileDialog::_file_submitted));
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 017c9d8d4f..4945094086 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -196,4 +196,4 @@ public:
VARIANT_ENUM_CAST(FileDialog::FileMode);
VARIANT_ENUM_CAST(FileDialog::Access);
-#endif
+#endif // FILE_DIALOG_H
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index e30759aa3e..f51031765c 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -382,9 +382,9 @@ void GraphEdit::add_child_notify(Node *p_child) {
GraphNode *gn = Object::cast_to<GraphNode>(p_child);
if (gn) {
gn->set_scale(Vector2(zoom, zoom));
- gn->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved), varray(gn));
- gn->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated), varray(gn));
- gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised), varray(gn));
+ gn->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved).bind(gn));
+ gn->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(gn));
+ gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised).bind(gn));
gn->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update));
gn->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update));
_graph_node_moved(gn);
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 02e90e4717..beb17ec4cf 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -365,4 +365,4 @@ public:
VARIANT_ENUM_CAST(GraphEdit::PanningScheme);
-#endif // GRAPHEdit_H
+#endif // GRAPH_EDIT_H
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index c7d87da0b5..21bd22759c 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ITEMLIST_H
-#define ITEMLIST_H
+#ifndef ITEM_LIST_H
+#define ITEM_LIST_H
#include "scene/gui/control.h"
#include "scene/gui/scroll_bar.h"
@@ -259,4 +259,4 @@ public:
VARIANT_ENUM_CAST(ItemList::SelectMode);
VARIANT_ENUM_CAST(ItemList::IconMode);
-#endif // ITEMLIST_H
+#endif // ITEM_LIST_H
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 8094812203..e7f48beb00 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -657,7 +657,7 @@ void Label::set_label_settings(const Ref<LabelSettings> &p_settings) {
}
settings = p_settings;
if (settings.is_valid()) {
- settings->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate), varray(), CONNECT_REFERENCE_COUNTED);
+ settings->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate), CONNECT_REFERENCE_COUNTED);
}
_invalidate();
}
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 3734fce1bb..cab5b36d68 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -140,4 +140,4 @@ public:
~Label();
};
-#endif
+#endif // LABEL_H
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 557da35bfd..6aa1694f1f 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -336,4 +336,4 @@ public:
VARIANT_ENUM_CAST(LineEdit::MenuItems);
-#endif
+#endif // LINE_EDIT_H
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index 54a31f06ce..12a6a7618f 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LINKBUTTON_H
-#define LINKBUTTON_H
+#ifndef LINK_BUTTON_H
+#define LINK_BUTTON_H
#include "scene/gui/base_button.h"
#include "scene/resources/text_line.h"
@@ -86,4 +86,4 @@ public:
VARIANT_ENUM_CAST(LinkButton::UnderlineMode);
-#endif // LINKBUTTON_H
+#endif // LINK_BUTTON_H
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 316fee53fe..069a31d9d2 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -239,8 +239,8 @@ MenuButton::MenuButton(const String &p_text) :
popup = memnew(PopupMenu);
popup->hide();
add_child(popup, false, INTERNAL_MODE_FRONT);
- popup->connect("about_to_popup", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(true));
- popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(false));
+ popup->connect("about_to_popup", callable_mp(this, &MenuButton::_popup_visibility_changed).bind(true));
+ popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed).bind(false));
}
MenuButton::~MenuButton() {
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 0a6b46c796..97c0d21f1e 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -71,4 +71,4 @@ public:
~MenuButton();
};
-#endif
+#endif // MENU_BUTTON_H
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index a86f2bdbc1..a10ec1db06 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -481,7 +481,7 @@ OptionButton::OptionButton(const String &p_text) :
add_child(popup, false, INTERNAL_MODE_FRONT);
popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected));
popup->connect("id_focused", callable_mp(this, &OptionButton::_focused));
- popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(false));
+ popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed).bind(false));
}
OptionButton::~OptionButton() {
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 7896132626..5665296699 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -105,4 +105,4 @@ public:
~OptionButton();
};
-#endif
+#endif // OPTION_BUTTON_H
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index b53c8be50f..70eb8722d0 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -79,4 +79,4 @@ public:
PopupPanel();
};
-#endif
+#endif // POPUP_H
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index daa38b0e6d..e203793c2e 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -260,4 +260,4 @@ public:
~PopupMenu();
};
-#endif
+#endif // POPUP_MENU_H
diff --git a/scene/gui/range.h b/scene/gui/range.h
index 1274821bd1..87bd0d88af 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -106,4 +106,4 @@ public:
~Range();
};
-#endif
+#endif // RANGE_H
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 8f2eb7b3fb..8e424977c4 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -224,13 +224,21 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
for (int i = 0; i < spans; i++) {
ItemText *it = reinterpret_cast<ItemText *>((uint64_t)TS->shaped_get_span_meta(t, i));
if (it) {
- Ref<Font> font = _find_font(it);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(it);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(it);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(it);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features());
for (int j = 0; j < TextServer::SPACING_MAX; j++) {
@@ -483,13 +491,21 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
l.dc_ol_color = dc->ol_color;
} break;
case ITEM_NEWLINE: {
- Ref<Font> font = _find_font(it);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(it);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(it);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(it);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
l.text_buf->add_string("\n", font, font_size);
text += "\n";
@@ -498,13 +514,21 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
} break;
case ITEM_TEXT: {
ItemText *t = static_cast<ItemText *>(it);
- Ref<Font> font = _find_font(it);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(it);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(it);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(it);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
String lang = _find_language(it);
String tx = t->text;
@@ -750,13 +774,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
if (!prefix.is_empty()) {
- Ref<Font> font = _find_font(l.from);
- if (font.is_null()) {
- font = get_theme_font(SNAME("normal_font"));
+ Ref<Font> font = get_theme_font(SNAME("normal_font"));
+ int font_size = get_theme_font_size(SNAME("normal_font_size"));
+
+ ItemFont *font_it = _find_font(l.from);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(l.from);
- if (font_size == -1) {
- font_size = get_theme_font_size(SNAME("normal_font_size"));
+ ItemFontSize *font_size_it = _find_font_size(l.from);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
if (rtl) {
float offx = 0.0f;
@@ -1519,13 +1551,20 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
int stop = text_rect_begin;
*r_click_item = _find_indentable(it);
while (*r_click_item) {
- Ref<Font> font = _find_font(*r_click_item);
- if (!font.is_valid()) {
- font = get_theme_font(SNAME("normal_font"));
+ Ref<Font> font = get_theme_font(SNAME("normal_font"));
+ int font_size = get_theme_font_size(SNAME("normal_font_size"));
+ ItemFont *font_it = _find_font(*r_click_item);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(*r_click_item);
- if (font_size == -1) {
- font_size = get_theme_font_size(SNAME("normal_font_size"));
+ ItemFontSize *font_size_it = _find_font_size(*r_click_item);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
if (rtl) {
stop += tab_size * font->get_char_size(' ', font_size).width;
@@ -2109,34 +2148,34 @@ RichTextLabel::Item *RichTextLabel::_find_indentable(Item *p_item) {
return indentable;
}
-Ref<Font> RichTextLabel::_find_font(Item *p_item) {
+RichTextLabel::ItemFont *RichTextLabel::_find_font(Item *p_item) {
Item *fontitem = p_item;
while (fontitem) {
if (fontitem->type == ITEM_FONT) {
ItemFont *fi = static_cast<ItemFont *>(fontitem);
- return fi->font;
+ return fi;
}
fontitem = fontitem->parent;
}
- return Ref<Font>();
+ return nullptr;
}
-int RichTextLabel::_find_font_size(Item *p_item) {
+RichTextLabel::ItemFontSize *RichTextLabel::_find_font_size(Item *p_item) {
Item *sizeitem = p_item;
while (sizeitem) {
if (sizeitem->type == ITEM_FONT_SIZE) {
ItemFontSize *fi = static_cast<ItemFontSize *>(sizeitem);
- return fi->font_size;
+ return fi;
}
sizeitem = sizeitem->parent;
}
- return -1;
+ return nullptr;
}
int RichTextLabel::_find_outline_size(Item *p_item, int p_default) {
@@ -2222,24 +2261,40 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int
while (item) {
if (item->type == ITEM_INDENT) {
- Ref<Font> font = _find_font(item);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(item);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(item);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(item);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
margin += tab_size * font->get_char_size(' ', font_size).width;
} else if (item->type == ITEM_LIST) {
- Ref<Font> font = _find_font(item);
- if (font.is_null()) {
- font = p_base_font;
+ Ref<Font> font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(item);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- int font_size = _find_font_size(item);
- if (font_size == -1) {
- font_size = p_base_font_size;
+ ItemFontSize *font_size_it = _find_font_size(item);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
margin += tab_size * font->get_char_size(' ', font_size).width;
}
@@ -2918,7 +2973,7 @@ void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font
_add_item(item, false);
}
-void RichTextLabel::push_font(const Ref<Font> &p_font) {
+void RichTextLabel::push_font(const Ref<Font> &p_font, int p_size) {
_stop_thread();
MutexLock data_lock(data_mutex);
@@ -2927,6 +2982,7 @@ void RichTextLabel::push_font(const Ref<Font> &p_font) {
ItemFont *item = memnew(ItemFont);
item->font = p_font;
+ item->font_size = p_size;
_add_item(item, true);
}
@@ -2934,35 +2990,35 @@ void RichTextLabel::push_normal() {
Ref<Font> normal_font = get_theme_font(SNAME("normal_font"));
ERR_FAIL_COND(normal_font.is_null());
- push_font(normal_font);
+ push_font(normal_font, get_theme_font_size(SNAME("normal_font_size")));
}
void RichTextLabel::push_bold() {
Ref<Font> bold_font = get_theme_font(SNAME("bold_font"));
ERR_FAIL_COND(bold_font.is_null());
- push_font(bold_font);
+ push_font(bold_font, get_theme_font_size(SNAME("bold_font_size")));
}
void RichTextLabel::push_bold_italics() {
Ref<Font> bold_italics_font = get_theme_font(SNAME("bold_italics_font"));
ERR_FAIL_COND(bold_italics_font.is_null());
- push_font(bold_italics_font);
+ push_font(bold_italics_font, get_theme_font_size(SNAME("bold_italics_font_size")));
}
void RichTextLabel::push_italics() {
Ref<Font> italics_font = get_theme_font(SNAME("italics_font"));
ERR_FAIL_COND(italics_font.is_null());
- push_font(italics_font);
+ push_font(italics_font, get_theme_font_size(SNAME("italics_font_size")));
}
void RichTextLabel::push_mono() {
Ref<Font> mono_font = get_theme_font(SNAME("mono_font"));
ERR_FAIL_COND(mono_font.is_null());
- push_font(mono_font);
+ push_font(mono_font, get_theme_font_size(SNAME("mono_font_size")));
}
void RichTextLabel::push_font_size(int p_font_size) {
@@ -3552,9 +3608,9 @@ void RichTextLabel::append_text(const String &p_bbcode) {
//use bold font
in_bold = true;
if (in_italics) {
- push_font(bold_italics_font);
+ push_font(bold_italics_font, get_theme_font_size(SNAME("bold_italics_font_size")));
} else {
- push_font(bold_font);
+ push_font(bold_font, get_theme_font_size(SNAME("bold_font_size")));
}
pos = brk_end + 1;
tag_stack.push_front(tag);
@@ -3562,15 +3618,15 @@ void RichTextLabel::append_text(const String &p_bbcode) {
//use italics font
in_italics = true;
if (in_bold) {
- push_font(bold_italics_font);
+ push_font(bold_italics_font, get_theme_font_size(SNAME("bold_italics_font_size")));
} else {
- push_font(italics_font);
+ push_font(italics_font, get_theme_font_size(SNAME("italics_font_size")));
}
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "code") {
//use monospace font
- push_font(mono_font);
+ push_font(mono_font, get_theme_font_size(SNAME("mono_font_size")));
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("table=")) {
@@ -3999,9 +4055,16 @@ void RichTextLabel::append_text(const String &p_bbcode) {
String fnt_ftr = tag.substr(18, tag.length());
Vector<String> subtag = fnt_ftr.split(",");
if (subtag.size() > 0) {
- Ref<Font> font = _find_font(current);
- if (font.is_null()) {
- font = normal_font;
+ Ref<Font> font = normal_font;
+ int font_size = 0;
+ ItemFont *font_it = _find_font(current);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
Ref<FontVariation> fc;
fc.instantiate();
@@ -4016,7 +4079,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
fc->set_opentype_features(features);
- push_font(fc);
+ push_font(fc, font_size);
}
pos = brk_end + 1;
tag_stack.push_front("opentype_features");
@@ -4037,6 +4100,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
Ref<FontVariation> fc;
fc.instantiate();
+ int fnt_size = 0;
for (int i = 1; i < subtag.size(); i++) {
Vector<String> subtag_a = subtag[i].split("=", true, 2);
if (subtag_a.size() == 2) {
@@ -4047,10 +4111,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
fc->set_base_font(font_data);
}
} else if (subtag_a[0] == "size" || subtag_a[0] == "s") {
- int fnt_size = subtag_a[1].to_int();
- if (fnt_size > 0) {
- push_font_size(fnt_size);
- }
+ fnt_size = subtag_a[1].to_int();
} else if (subtag_a[0] == "glyph_spacing" || subtag_a[0] == "gl") {
int spacing = subtag_a[1].to_int();
fc->set_spacing(TextServer::SPACING_GLYPH, spacing);
@@ -4101,7 +4162,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
}
- push_font(fc);
+ push_font(fc, fnt_size);
pos = brk_end + 1;
tag_stack.push_front("font");
@@ -4929,7 +4990,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER));
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
- ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
+ ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font);
ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size);
ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal);
ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index c123f38c01..e5f0469c01 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -178,6 +178,7 @@ private:
struct ItemFont : public Item {
Ref<Font> font;
+ int font_size = 0;
ItemFont() { type = ITEM_FONT; }
};
@@ -462,8 +463,8 @@ private:
Item *_find_indentable(Item *p_item);
Item *_get_item_at_pos(Item *p_item_from, Item *p_item_to, int p_position);
void _find_frame(Item *p_item, ItemFrame **r_frame, int *r_line);
- int _find_font_size(Item *p_item);
- Ref<Font> _find_font(Item *p_item);
+ ItemFontSize *_find_font_size(Item *p_item);
+ ItemFont *_find_font(Item *p_item);
int _find_outline_size(Item *p_item, int p_default);
ItemList *_find_list_item(Item *p_item);
ItemDropcap *_find_dc_item(Item *p_item);
@@ -518,7 +519,7 @@ public:
void add_newline();
bool remove_line(const int p_line);
void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0));
- void push_font(const Ref<Font> &p_font);
+ void push_font(const Ref<Font> &p_font, int p_size = 0);
void push_font_size(int p_font_size);
void push_outline_size(int p_font_size);
void push_normal();
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index f387f91a8b..48c57d9b1b 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -303,7 +303,7 @@ void ScrollBar::_notification(int p_what) {
if (drag_node) {
drag_node->connect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input));
- drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), varray(), CONNECT_ONESHOT);
+ drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), CONNECT_ONESHOT);
}
} break;
@@ -595,7 +595,7 @@ void ScrollBar::set_drag_node(const NodePath &p_path) {
if (drag_node) {
drag_node->connect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input));
- drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), varray(), CONNECT_ONESHOT);
+ drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), CONNECT_ONESHOT);
}
}
}
diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h
index 651edd1a74..1823f86a67 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -128,4 +128,4 @@ public:
ScrollBar(VERTICAL) { set_h_size_flags(0); }
};
-#endif
+#endif // SCROLL_BAR_H
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index b9fcf64db6..7c8690538d 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -116,4 +116,4 @@ public:
VARIANT_ENUM_CAST(ScrollContainer::ScrollMode);
-#endif
+#endif // SCROLL_CONTAINER_H
diff --git a/scene/gui/separator.h b/scene/gui/separator.h
index 1621bb3351..e6578a4d04 100644
--- a/scene/gui/separator.h
+++ b/scene/gui/separator.h
@@ -60,4 +60,4 @@ public:
HSeparator();
};
-#endif
+#endif // SEPARATOR_H
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index a4733c455f..b7bef37e17 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -272,7 +272,7 @@ void SpinBox::set_update_on_text_changed(bool p_enabled) {
update_on_text_changed = p_enabled;
if (p_enabled) {
- line_edit->connect("text_changed", callable_mp(this, &SpinBox::_text_changed), Vector<Variant>(), CONNECT_DEFERRED);
+ line_edit->connect("text_changed", callable_mp(this, &SpinBox::_text_changed), CONNECT_DEFERRED);
} else {
line_edit->disconnect("text_changed", callable_mp(this, &SpinBox::_text_changed));
}
@@ -323,8 +323,8 @@ SpinBox::SpinBox() {
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
line_edit->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
- line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), Vector<Variant>(), CONNECT_DEFERRED);
- line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), Vector<Variant>(), CONNECT_DEFERRED);
+ line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), CONNECT_DEFERRED);
+ line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), CONNECT_DEFERRED);
line_edit->connect("gui_input", callable_mp(this, &SpinBox::_line_edit_input));
range_click_timer = memnew(Timer);
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index 55b7802aa4..5b488fb79e 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VIEWPORTCONTAINER_H
-#define VIEWPORTCONTAINER_H
+#ifndef SUBVIEWPORT_CONTAINER_H
+#define SUBVIEWPORT_CONTAINER_H
#include "scene/gui/container.h"
@@ -63,4 +63,4 @@ public:
SubViewportContainer();
};
-#endif // VIEWPORTCONTAINER_H
+#endif // SUBVIEWPORT_CONTAINER_H
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 06553cd0f6..4bc8b8e197 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -443,8 +443,10 @@ void TextEdit::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
- _update_caches();
- _update_wrap_at_column(true);
+ if (is_inside_tree()) {
+ _update_caches();
+ _update_wrap_at_column(true);
+ }
} break;
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
@@ -1055,7 +1057,7 @@ void TextEdit::_notification(int p_what) {
const Variant *argp[] = { &args[0], &args[1], &args[2] };
Callable::CallError ce;
Variant ret;
- gutter.custom_draw_callback.call(argp, 3, ret, ce);
+ gutter.custom_draw_callback.callp(argp, 3, ret, ce);
}
} break;
}
@@ -2789,7 +2791,7 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
const Variant *argp[] = { &args[0] };
Callable::CallError ce;
Variant ret;
- tooltip_callback.call(argp, 1, ret, ce);
+ tooltip_callback.callp(argp, 1, ret, ce);
ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, "", "Failed to call custom tooltip.");
return ret;
}
diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h
index 5762949acd..9f6f7c1515 100644
--- a/scene/gui/texture_button.h
+++ b/scene/gui/texture_button.h
@@ -101,4 +101,5 @@ public:
};
VARIANT_ENUM_CAST(TextureButton::StretchMode);
+
#endif // TEXTURE_BUTTON_H
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 2c4cba4954..1eb6c5a554 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -723,7 +723,12 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) {
TreeItem *TreeItem::get_child(int p_idx) {
_create_children_cache();
+
+ if (p_idx < 0) {
+ p_idx += children_cache.size();
+ }
ERR_FAIL_INDEX_V(p_idx, children_cache.size(), nullptr);
+
return children_cache.get(p_idx);
}
@@ -1844,15 +1849,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
p_item->set_meta("__focus_rect", Rect2(r.position, r.size));
- if (rtl) {
- r.position.x = get_size().width - r.position.x - r.size.x;
- }
-
- if (p_item->cells[i].selected) {
- if (has_focus()) {
- cache.selected_focus->draw(ci, r);
- } else {
- cache.selected->draw(ci, r);
+ if (select_mode != SELECT_ROW) {
+ if (rtl) {
+ r.position.x = get_size().width - r.position.x - r.size.x;
+ }
+ if (p_item->cells[i].selected) {
+ if (has_focus()) {
+ cache.selected_focus->draw(ci, r);
+ } else {
+ cache.selected->draw(ci, r);
+ }
}
}
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 1690e7ac57..f0819e2980 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -719,4 +719,5 @@ public:
VARIANT_ENUM_CAST(Tree::SelectMode);
VARIANT_ENUM_CAST(Tree::DropModeFlags);
-#endif
+
+#endif // TREE_H
diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp
index 892d0aba29..3b7f499a07 100644
--- a/scene/gui/view_panner.cpp
+++ b/scene/gui/view_panner.cpp
@@ -135,7 +135,7 @@ void ViewPanner::callback_helper(Callable p_callback, Vector<Variant> p_args) {
Variant result;
Callable::CallError ce;
- p_callback.call(argptr, p_args.size(), result, ce);
+ p_callback.callp(argptr, p_args.size(), result, ce);
}
void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) {
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index e298805aca..ce204c6aeb 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -64,9 +64,6 @@ void CanvasItem::_propagate_visibility_changed(bool p_parent_visible_in_tree) {
if (!visible) {
return;
}
- if (p_parent_visible_in_tree && first_draw) { // Avoid propagating it twice.
- first_draw = false;
- }
_handle_visibility_change(p_parent_visible_in_tree);
}
@@ -133,10 +130,6 @@ void CanvasItem::_update_callback() {
RenderingServer::get_singleton()->canvas_item_clear(get_canvas_item());
//todo updating = true - only allow drawing here
if (is_visible_in_tree()) {
- if (first_draw) {
- notification(NOTIFICATION_VISIBILITY_CHANGED);
- first_draw = false;
- }
drawing = true;
current_item_drawn = this;
notification(NOTIFICATION_DRAW);
@@ -268,7 +261,6 @@ void CanvasItem::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
ERR_FAIL_COND(!is_inside_tree());
- first_draw = true;
Node *parent = get_parent();
if (parent) {
@@ -307,6 +299,10 @@ void CanvasItem::_notification(int p_what) {
}
}
+ RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, is_visible_in_tree()); // The visibility of the parent may change.
+ if (is_visible_in_tree()) {
+ notification(NOTIFICATION_VISIBILITY_CHANGED); // Considered invisible until entered.
+ }
_enter_canvas();
_update_texture_filter_changed(false);
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index f5df6512ee..38e0be1683 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -83,7 +83,6 @@ private:
int light_mask = 1;
Window *window = nullptr;
- bool first_draw = false;
bool visible = true;
bool parent_visible_in_tree = false;
bool clip_children = false;
diff --git a/scene/main/http_request.h b/scene/main/http_request.h
index 49b4b1b30c..4b32188377 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef HTTPREQUEST_H
-#define HTTPREQUEST_H
+#ifndef HTTP_REQUEST_H
+#define HTTP_REQUEST_H
#include "core/io/http_client.h"
#include "core/os/thread.h"
@@ -162,4 +162,4 @@ public:
VARIANT_ENUM_CAST(HTTPRequest::Result);
-#endif // HTTPREQUEST_H
+#endif // HTTP_REQUEST_H
diff --git a/scene/main/multiplayer_api.cpp b/scene/main/multiplayer_api.cpp
new file mode 100644
index 0000000000..95574042a8
--- /dev/null
+++ b/scene/main/multiplayer_api.cpp
@@ -0,0 +1,416 @@
+/*************************************************************************/
+/* multiplayer_api.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_api.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "core/io/marshalls.h"
+
+#include <stdint.h>
+
+#ifdef DEBUG_ENABLED
+#include "core/os/os.h"
+#endif
+
+StringName MultiplayerAPI::default_interface = StringName();
+
+void MultiplayerAPI::set_default_interface(const StringName &p_interface) {
+ ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface));
+ default_interface = p_interface;
+}
+
+StringName MultiplayerAPI::get_default_interface() {
+ return default_interface;
+}
+
+Ref<MultiplayerAPI> MultiplayerAPI::create_default_interface() {
+ if (default_interface != StringName()) {
+ return Ref<MultiplayerAPI>(Object::cast_to<MultiplayerAPI>(ClassDB::instantiate(default_interface)));
+ }
+ return Ref<MultiplayerAPI>(memnew(MultiplayerAPIExtension));
+}
+
+// The variant is compressed and encoded; The first byte contains all the meta
+// information and the format is:
+// - The first LSB 5 bits are used for the variant type.
+// - The next two bits are used to store the encoding mode.
+// - The most significant is used to store the boolean value.
+#define VARIANT_META_TYPE_MASK 0x1F
+#define VARIANT_META_EMODE_MASK 0x60
+#define VARIANT_META_BOOL_MASK 0x80
+#define ENCODE_8 0 << 5
+#define ENCODE_16 1 << 5
+#define ENCODE_32 2 << 5
+#define ENCODE_64 3 << 5
+Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) {
+ // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
+ CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
+
+ uint8_t *buf = r_buffer;
+ r_len = 0;
+ uint8_t encode_mode = 0;
+
+ switch (p_variant.get_type()) {
+ case Variant::BOOL: {
+ if (buf) {
+ // We still have 1 free bit in the meta, so let's use it.
+ buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
+ buf[0] |= encode_mode | p_variant.get_type();
+ }
+ r_len += 1;
+ } break;
+ case Variant::INT: {
+ if (buf) {
+ // Reserve the first byte for the meta.
+ buf += 1;
+ }
+ r_len += 1;
+ int64_t val = p_variant;
+ if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
+ // Use 8 bit
+ encode_mode = ENCODE_8;
+ if (buf) {
+ buf[0] = val;
+ }
+ r_len += 1;
+ } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
+ // Use 16 bit
+ encode_mode = ENCODE_16;
+ if (buf) {
+ encode_uint16(val, buf);
+ }
+ r_len += 2;
+ } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
+ // Use 32 bit
+ encode_mode = ENCODE_32;
+ if (buf) {
+ encode_uint32(val, buf);
+ }
+ r_len += 4;
+ } else {
+ // Use 64 bit
+ encode_mode = ENCODE_64;
+ if (buf) {
+ encode_uint64(val, buf);
+ }
+ r_len += 8;
+ }
+ // Store the meta
+ if (buf) {
+ buf -= 1;
+ buf[0] = encode_mode | p_variant.get_type();
+ }
+ } break;
+ default:
+ // Any other case is not yet compressed.
+ Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding);
+ if (err != OK) {
+ return err;
+ }
+ if (r_buffer) {
+ // The first byte is not used by the marshalling, so store the type
+ // so we know how to decompress and decode this variant.
+ r_buffer[0] = p_variant.get_type();
+ }
+ }
+
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) {
+ const uint8_t *buf = p_buffer;
+ int len = p_len;
+
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
+ uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
+
+ ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
+
+ switch (type) {
+ case Variant::BOOL: {
+ bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
+ r_variant = val;
+ if (r_len) {
+ *r_len = 1;
+ }
+ } break;
+ case Variant::INT: {
+ buf += 1;
+ len -= 1;
+ if (r_len) {
+ *r_len = 1;
+ }
+ if (encode_mode == ENCODE_8) {
+ // 8 bits.
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ int8_t val = buf[0];
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 1;
+ }
+ } else if (encode_mode == ENCODE_16) {
+ // 16 bits.
+ ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
+ int16_t val = decode_uint16(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 2;
+ }
+ } else if (encode_mode == ENCODE_32) {
+ // 32 bits.
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ int32_t val = decode_uint32(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 4;
+ }
+ } else {
+ // 64 bits.
+ ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
+ int64_t val = decode_uint64(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 8;
+ }
+ }
+ } break;
+ default:
+ Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int size = 0;
+
+ if (p_count == 0) {
+ if (r_raw) {
+ *r_raw = true;
+ }
+ return OK;
+ }
+
+ // Try raw encoding optimization.
+ if (r_raw && p_count == 1) {
+ *r_raw = false;
+ const Variant &v = *(p_variants[0]);
+ if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ *r_raw = true;
+ const PackedByteArray pba = v;
+ if (p_buffer) {
+ memcpy(p_buffer, pba.ptr(), pba.size());
+ }
+ r_len += pba.size();
+ } else {
+ encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+ }
+
+ // Regular encoding.
+ for (int i = 0; i < p_count; i++) {
+ const Variant &v = *(p_variants[i]);
+ encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int argc = r_variants.size();
+ if (argc == 0 && p_raw) {
+ return OK;
+ }
+ ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
+ if (p_raw) {
+ r_len = p_len;
+ PackedByteArray pba;
+ pba.resize(p_len);
+ memcpy(pba.ptrw(), p_buffer, p_len);
+ r_variants.write[0] = pba;
+ return OK;
+ }
+
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
+
+ int vlen;
+ Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
+ r_len += vlen;
+ }
+ return OK;
+}
+
+Error MultiplayerAPI::_rpc_bind(int p_peer, Object *p_object, const StringName &p_method, Array p_args) {
+ Vector<Variant> args;
+ Vector<const Variant *> argsp;
+ args.resize(p_args.size());
+ argsp.resize(p_args.size());
+ Variant *ptr = args.ptrw();
+ const Variant **pptr = argsp.ptrw();
+ for (int i = 0; i < p_args.size(); i++) {
+ ptr[i] = p_args[i];
+ pptr[i] = &ptr[i];
+ }
+ return rpcp(p_object, p_peer, p_method, argsp.size() ? argsp.ptrw() : nullptr, argsp.size());
+}
+
+void MultiplayerAPI::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id);
+ ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server);
+ ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id);
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
+ ClassDB::bind_method(D_METHOD("rpc", "peer", "object", "method", "arguments"), &MultiplayerAPI::_rpc_bind, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("object_configuration_add", "object", "configuration"), &MultiplayerAPI::object_configuration_add);
+ ClassDB::bind_method(D_METHOD("object_configuration_remove", "object", "configuration"), &MultiplayerAPI::object_configuration_remove);
+
+ ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer");
+
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("set_default_interface", "interface_name"), &MultiplayerAPI::set_default_interface);
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("get_default_interface"), &MultiplayerAPI::get_default_interface);
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("create_default_interface"), &MultiplayerAPI::create_default_interface);
+
+ ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("connected_to_server"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+
+ BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(RPC_MODE_ANY_PEER);
+ BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY);
+}
+
+/// MultiplayerAPIExtension
+
+Error MultiplayerAPIExtension::poll() {
+ int err;
+ if (GDVIRTUAL_CALL(_poll, err)) {
+ return (Error)err;
+ }
+ return OK;
+}
+
+void MultiplayerAPIExtension::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
+ GDVIRTUAL_CALL(_set_multiplayer_peer, p_peer);
+}
+
+Ref<MultiplayerPeer> MultiplayerAPIExtension::get_multiplayer_peer() {
+ Ref<MultiplayerPeer> peer;
+ if (GDVIRTUAL_CALL(_get_multiplayer_peer, peer)) {
+ return peer;
+ }
+ return nullptr;
+}
+
+int MultiplayerAPIExtension::get_unique_id() {
+ int id;
+ if (GDVIRTUAL_CALL(_get_unique_id, id)) {
+ return id;
+ }
+ return 1;
+}
+
+Vector<int> MultiplayerAPIExtension::get_peer_ids() {
+ Vector<int> ids;
+ if (GDVIRTUAL_CALL(_get_peer_ids, ids)) {
+ return ids;
+ }
+ return Vector<int>();
+}
+
+Error MultiplayerAPIExtension::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ if (!GDVIRTUAL_IS_OVERRIDDEN(_rpc)) {
+ return ERR_UNAVAILABLE;
+ }
+ Array args;
+ for (int i = 0; i < p_argcount; i++) {
+ args.push_back(*p_arg[i]);
+ }
+ int ret;
+ if (GDVIRTUAL_CALL(_rpc, p_peer_id, p_obj, p_method, args, ret)) {
+ return (Error)ret;
+ }
+ return FAILED;
+}
+
+int MultiplayerAPIExtension::get_remote_sender_id() {
+ int id;
+ if (GDVIRTUAL_CALL(_get_remote_sender_id, id)) {
+ return id;
+ }
+ return 0;
+}
+
+Error MultiplayerAPIExtension::object_configuration_add(Object *p_object, Variant p_config) {
+ int err;
+ if (GDVIRTUAL_CALL(_object_configuration_add, p_object, p_config, err)) {
+ return (Error)err;
+ }
+ return ERR_UNAVAILABLE;
+}
+
+Error MultiplayerAPIExtension::object_configuration_remove(Object *p_object, Variant p_config) {
+ int err;
+ if (GDVIRTUAL_CALL(_object_configuration_remove, p_object, p_config, err)) {
+ return (Error)err;
+ }
+ return ERR_UNAVAILABLE;
+}
+
+void MultiplayerAPIExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_poll);
+ GDVIRTUAL_BIND(_set_multiplayer_peer, "multiplayer_peer");
+ GDVIRTUAL_BIND(_get_multiplayer_peer);
+ GDVIRTUAL_BIND(_get_unique_id);
+ GDVIRTUAL_BIND(_get_peer_ids);
+ GDVIRTUAL_BIND(_rpc, "peer", "object", "method", "args");
+ GDVIRTUAL_BIND(_get_remote_sender_id);
+ GDVIRTUAL_BIND(_object_configuration_add, "object", "configuration");
+ GDVIRTUAL_BIND(_object_configuration_remove, "object", "configuration");
+}
diff --git a/scene/main/multiplayer_api.h b/scene/main/multiplayer_api.h
new file mode 100644
index 0000000000..c1d90d651e
--- /dev/null
+++ b/scene/main/multiplayer_api.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* multiplayer_api.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_API_H
+#define MULTIPLAYER_API_H
+
+#include "core/object/ref_counted.h"
+#include "scene/main/multiplayer_peer.h"
+
+class MultiplayerAPI : public RefCounted {
+ GDCLASS(MultiplayerAPI, RefCounted);
+
+private:
+ static StringName default_interface;
+
+protected:
+ static void _bind_methods();
+ Error _rpc_bind(int p_peer, Object *p_obj, const StringName &p_method, Array args = Array());
+
+public:
+ enum RPCMode {
+ RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
+ RPC_MODE_ANY_PEER, // Any peer can call this RPC
+ RPC_MODE_AUTHORITY, // Only the node's multiplayer authority (server by default) can call this RPC
+ };
+
+ static Ref<MultiplayerAPI> create_default_interface();
+ static void set_default_interface(const StringName &p_interface);
+ static StringName get_default_interface();
+
+ static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding);
+ static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding);
+ static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false);
+ static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false);
+
+ virtual Error poll() = 0;
+ virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) = 0;
+ virtual Ref<MultiplayerPeer> get_multiplayer_peer() = 0;
+ virtual int get_unique_id() = 0;
+ virtual Vector<int> get_peer_ids() = 0;
+
+ virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) = 0;
+ virtual int get_remote_sender_id() = 0;
+
+ virtual Error object_configuration_add(Object *p_object, Variant p_config) = 0;
+ virtual Error object_configuration_remove(Object *p_object, Variant p_config) = 0;
+
+ bool has_multiplayer_peer() { return get_multiplayer_peer().is_valid(); }
+ bool is_server() { return get_unique_id() == MultiplayerPeer::TARGET_PEER_SERVER; }
+
+ MultiplayerAPI() {}
+ virtual ~MultiplayerAPI() {}
+};
+
+VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
+
+class MultiplayerAPIExtension : public MultiplayerAPI {
+ GDCLASS(MultiplayerAPIExtension, MultiplayerAPI);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Error poll() override;
+ virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override;
+ virtual Ref<MultiplayerPeer> get_multiplayer_peer() override;
+ virtual int get_unique_id() override;
+ virtual Vector<int> get_peer_ids() override;
+
+ virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override;
+ virtual int get_remote_sender_id() override;
+
+ virtual Error object_configuration_add(Object *p_object, Variant p_config) override;
+ virtual Error object_configuration_remove(Object *p_object, Variant p_config) override;
+
+ // Extensions
+ GDVIRTUAL0R(int, _poll);
+ GDVIRTUAL1(_set_multiplayer_peer, Ref<MultiplayerPeer>);
+ GDVIRTUAL0R(Ref<MultiplayerPeer>, _get_multiplayer_peer);
+ GDVIRTUAL0RC(int, _get_unique_id);
+ GDVIRTUAL0RC(PackedInt32Array, _get_peer_ids);
+ GDVIRTUAL4R(int, _rpc, int, Object *, StringName, Array);
+ GDVIRTUAL0RC(int, _get_remote_sender_id);
+ GDVIRTUAL2R(int, _object_configuration_add, Object *, Variant);
+ GDVIRTUAL2R(int, _object_configuration_remove, Object *, Variant);
+};
+
+#endif // MULTIPLAYER_API_H
diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp
new file mode 100644
index 0000000000..aad5baccab
--- /dev/null
+++ b/scene/main/multiplayer_peer.cpp
@@ -0,0 +1,303 @@
+/*************************************************************************/
+/* multiplayer_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_peer.h"
+
+#include "core/os/os.h"
+
+uint32_t MultiplayerPeer::generate_unique_id() const {
+ uint32_t hash = 0;
+
+ while (hash == 0 || hash == 1) {
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_ticks_usec());
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_unix_time(), hash);
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
+ hash = hash_murmur3_one_32(
+ (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
+ hash = hash_murmur3_one_32(
+ (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
+
+ hash = hash_fmix32(hash);
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
+ }
+
+ return hash;
+}
+
+void MultiplayerPeer::set_transfer_channel(int p_channel) {
+ transfer_channel = p_channel;
+}
+
+int MultiplayerPeer::get_transfer_channel() const {
+ return transfer_channel;
+}
+
+void MultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
+ transfer_mode = p_mode;
+}
+
+MultiplayerPeer::TransferMode MultiplayerPeer::get_transfer_mode() const {
+ return transfer_mode;
+}
+
+void MultiplayerPeer::set_refuse_new_connections(bool p_enable) {
+ refuse_connections = p_enable;
+}
+
+bool MultiplayerPeer::is_refusing_new_connections() const {
+ return refuse_connections;
+}
+
+void MultiplayerPeer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel);
+ ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel);
+ ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode);
+ ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode);
+ ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
+
+ ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer);
+
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
+
+ ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
+ ClassDB::bind_method(D_METHOD("generate_unique_id"), &MultiplayerPeer::generate_unique_id);
+
+ ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections);
+ ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel");
+
+ BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
+ BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
+ BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);
+
+ BIND_CONSTANT(TARGET_PEER_BROADCAST);
+ BIND_CONSTANT(TARGET_PEER_SERVER);
+
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE);
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED);
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE);
+
+ ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+ ADD_SIGNAL(MethodInfo("connection_succeeded"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+}
+
+/*************/
+
+int MultiplayerPeerExtension::get_available_packet_count() const {
+ int count;
+ if (GDVIRTUAL_CALL(_get_available_packet_count, count)) {
+ return count;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_available_packet_count is unimplemented!");
+ return -1;
+}
+
+Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
+ return (Error)err;
+ }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_get_packet_script)) {
+ if (!GDVIRTUAL_CALL(_get_packet_script, script_buffer)) {
+ return FAILED;
+ }
+
+ if (script_buffer.size() == 0) {
+ return Error::ERR_UNAVAILABLE;
+ }
+
+ *r_buffer = script_buffer.ptr();
+ r_buffer_size = script_buffer.size();
+
+ return Error::OK;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!");
+ return FAILED;
+}
+
+Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
+ return (Error)err;
+ }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) {
+ PackedByteArray a;
+ a.resize(p_buffer_size);
+ memcpy(a.ptrw(), p_buffer, p_buffer_size);
+
+ if (!GDVIRTUAL_CALL(_put_packet_script, a, err)) {
+ return FAILED;
+ }
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!");
+ return FAILED;
+}
+
+int MultiplayerPeerExtension::get_max_packet_size() const {
+ int size;
+ if (GDVIRTUAL_CALL(_get_max_packet_size, size)) {
+ return size;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_max_packet_size is unimplemented!");
+ return 0;
+}
+
+void MultiplayerPeerExtension::set_transfer_channel(int p_channel) {
+ if (GDVIRTUAL_CALL(_set_transfer_channel, p_channel)) {
+ return;
+ }
+ MultiplayerPeer::set_transfer_channel(p_channel);
+}
+
+int MultiplayerPeerExtension::get_transfer_channel() const {
+ int channel;
+ if (GDVIRTUAL_CALL(_get_transfer_channel, channel)) {
+ return channel;
+ }
+ return MultiplayerPeer::get_transfer_channel();
+}
+
+void MultiplayerPeerExtension::set_transfer_mode(TransferMode p_mode) {
+ if (GDVIRTUAL_CALL(_set_transfer_mode, p_mode)) {
+ return;
+ }
+ MultiplayerPeer::set_transfer_mode(p_mode);
+}
+
+MultiplayerPeer::TransferMode MultiplayerPeerExtension::get_transfer_mode() const {
+ int mode;
+ if (GDVIRTUAL_CALL(_get_transfer_mode, mode)) {
+ return (MultiplayerPeer::TransferMode)mode;
+ }
+ return MultiplayerPeer::get_transfer_mode();
+}
+
+void MultiplayerPeerExtension::set_target_peer(int p_peer_id) {
+ if (GDVIRTUAL_CALL(_set_target_peer, p_peer_id)) {
+ return;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_set_target_peer is unimplemented!");
+}
+
+int MultiplayerPeerExtension::get_packet_peer() const {
+ int peer;
+ if (GDVIRTUAL_CALL(_get_packet_peer, peer)) {
+ return peer;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_peer is unimplemented!");
+ return 0;
+}
+
+bool MultiplayerPeerExtension::is_server() const {
+ bool server;
+ if (GDVIRTUAL_CALL(_is_server, server)) {
+ return server;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_is_server is unimplemented!");
+ return false;
+}
+
+void MultiplayerPeerExtension::poll() {
+ int err;
+ if (GDVIRTUAL_CALL(_poll, err)) {
+ return;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_poll is unimplemented!");
+}
+
+int MultiplayerPeerExtension::get_unique_id() const {
+ int id;
+ if (GDVIRTUAL_CALL(_get_unique_id, id)) {
+ return id;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_unique_id is unimplemented!");
+ return 0;
+}
+
+void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) {
+ if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) {
+ return;
+ }
+ MultiplayerPeer::set_refuse_new_connections(p_enable);
+}
+
+bool MultiplayerPeerExtension::is_refusing_new_connections() const {
+ bool refusing;
+ if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) {
+ return refusing;
+ }
+ return MultiplayerPeer::is_refusing_new_connections();
+}
+
+MultiplayerPeer::ConnectionStatus MultiplayerPeerExtension::get_connection_status() const {
+ int status;
+ if (GDVIRTUAL_CALL(_get_connection_status, status)) {
+ return (ConnectionStatus)status;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_connection_status is unimplemented!");
+ return CONNECTION_DISCONNECTED;
+}
+
+void MultiplayerPeerExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
+ GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
+ GDVIRTUAL_BIND(_get_available_packet_count);
+ GDVIRTUAL_BIND(_get_max_packet_size);
+
+ GDVIRTUAL_BIND(_get_packet_script)
+ GDVIRTUAL_BIND(_put_packet_script, "p_buffer");
+
+ GDVIRTUAL_BIND(_set_transfer_channel, "p_channel");
+ GDVIRTUAL_BIND(_get_transfer_channel);
+
+ GDVIRTUAL_BIND(_set_transfer_mode, "p_mode");
+ GDVIRTUAL_BIND(_get_transfer_mode);
+
+ GDVIRTUAL_BIND(_set_target_peer, "p_peer");
+
+ GDVIRTUAL_BIND(_get_packet_peer);
+ GDVIRTUAL_BIND(_is_server);
+ GDVIRTUAL_BIND(_poll);
+ GDVIRTUAL_BIND(_get_unique_id);
+ GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable");
+ GDVIRTUAL_BIND(_is_refusing_new_connections);
+ GDVIRTUAL_BIND(_get_connection_status);
+}
diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h
new file mode 100644
index 0000000000..8a012d7520
--- /dev/null
+++ b/scene/main/multiplayer_peer.h
@@ -0,0 +1,157 @@
+/*************************************************************************/
+/* multiplayer_peer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_PEER_H
+#define MULTIPLAYER_PEER_H
+
+#include "core/io/packet_peer.h"
+
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
+class MultiplayerPeer : public PacketPeer {
+ GDCLASS(MultiplayerPeer, PacketPeer);
+
+public:
+ enum TransferMode {
+ TRANSFER_MODE_UNRELIABLE,
+ TRANSFER_MODE_UNRELIABLE_ORDERED,
+ TRANSFER_MODE_RELIABLE
+ };
+
+protected:
+ static void _bind_methods();
+
+private:
+ int transfer_channel = 0;
+ TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
+ bool refuse_connections = false;
+
+public:
+ enum {
+ TARGET_PEER_BROADCAST = 0,
+ TARGET_PEER_SERVER = 1
+ };
+
+ enum ConnectionStatus {
+ CONNECTION_DISCONNECTED,
+ CONNECTION_CONNECTING,
+ CONNECTION_CONNECTED,
+ };
+
+ virtual void set_transfer_channel(int p_channel);
+ virtual int get_transfer_channel() const;
+ virtual void set_transfer_mode(TransferMode p_mode);
+ virtual TransferMode get_transfer_mode() const;
+ virtual void set_refuse_new_connections(bool p_enable);
+ virtual bool is_refusing_new_connections() const;
+
+ virtual void set_target_peer(int p_peer_id) = 0;
+
+ virtual int get_packet_peer() const = 0;
+
+ virtual bool is_server() const = 0;
+
+ virtual void poll() = 0;
+
+ virtual int get_unique_id() const = 0;
+
+ virtual ConnectionStatus get_connection_status() const = 0;
+
+ uint32_t generate_unique_id() const;
+
+ MultiplayerPeer() {}
+};
+
+VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus);
+VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode);
+
+class MultiplayerPeerExtension : public MultiplayerPeer {
+ GDCLASS(MultiplayerPeerExtension, MultiplayerPeer);
+
+protected:
+ static void _bind_methods();
+
+ PackedByteArray script_buffer;
+
+public:
+ /* PacketPeer */
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual int get_max_packet_size() const override;
+
+ /* MultiplayerPeer */
+ virtual void set_transfer_channel(int p_channel) override;
+ virtual int get_transfer_channel() const override;
+ virtual void set_transfer_mode(TransferMode p_mode) override;
+ virtual TransferMode get_transfer_mode() const override;
+ virtual void set_target_peer(int p_peer_id) override;
+
+ virtual int get_packet_peer() const override;
+
+ virtual bool is_server() const override;
+
+ virtual void poll() override;
+
+ virtual int get_unique_id() const override;
+
+ virtual void set_refuse_new_connections(bool p_enable) override;
+ virtual bool is_refusing_new_connections() const override;
+
+ virtual ConnectionStatus get_connection_status() const override;
+
+ /* PacketPeer GDExtension */
+ GDVIRTUAL0RC(int, _get_available_packet_count);
+ GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
+ GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int);
+ GDVIRTUAL0RC(int, _get_max_packet_size);
+
+ /* PacketPeer GDScript */
+ GDVIRTUAL0R(PackedByteArray, _get_packet_script);
+ GDVIRTUAL1R(int, _put_packet_script, PackedByteArray);
+
+ /* MultiplayerPeer GDExtension */
+ GDVIRTUAL1(_set_transfer_channel, int);
+ GDVIRTUAL0RC(int, _get_transfer_channel);
+ GDVIRTUAL1(_set_transfer_mode, int);
+ GDVIRTUAL0RC(int, _get_transfer_mode);
+ GDVIRTUAL1(_set_target_peer, int);
+ GDVIRTUAL0RC(int, _get_packet_peer);
+ GDVIRTUAL0RC(bool, _is_server);
+ GDVIRTUAL0R(int, _poll);
+ GDVIRTUAL0RC(int, _get_unique_id);
+ GDVIRTUAL1(_set_refuse_new_connections, bool);
+ GDVIRTUAL0RC(bool, _is_refusing_new_connections);
+ GDVIRTUAL0RC(int, _get_connection_status);
+};
+
+#endif // MULTIPLAYER_PEER_H
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index b4701637a4..6617bd1726 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -33,12 +33,12 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
-#include "core/multiplayer/multiplayer_api.h"
#include "core/object/message_queue.h"
#include "core/string/print_string.h"
#include "instance_placeholder.h"
#include "scene/animation/tween.h"
#include "scene/debugger/scene_debugger.h"
+#include "scene/main/multiplayer_api.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
#include "viewport.h"
@@ -582,35 +582,30 @@ bool Node::is_multiplayer_authority() const {
/***** RPC CONFIG ********/
-uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
- for (int i = 0; i < data.rpc_methods.size(); i++) {
- if (data.rpc_methods[i].name == p_method) {
- Multiplayer::RPCConfig &nd = data.rpc_methods.write[i];
- nd.rpc_mode = p_rpc_mode;
- nd.transfer_mode = p_transfer_mode;
- nd.call_local = p_call_local;
- nd.channel = p_channel;
- return i | (1 << 15);
- }
+void Node::rpc_config(const StringName &p_method, const Variant &p_config) {
+ if (data.rpc_config.get_type() != Variant::DICTIONARY) {
+ data.rpc_config = Dictionary();
+ }
+ Dictionary node_config = data.rpc_config;
+ if (p_config.get_type() == Variant::NIL) {
+ node_config.erase(p_method);
+ } else {
+ ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY);
+ node_config[p_method] = p_config;
}
- // New method
- Multiplayer::RPCConfig nd;
- nd.name = p_method;
- nd.rpc_mode = p_rpc_mode;
- nd.transfer_mode = p_transfer_mode;
- nd.channel = p_channel;
- nd.call_local = p_call_local;
- data.rpc_methods.push_back(nd);
- return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15);
+}
+
+const Variant Node::get_node_rpc_config() const {
+ return data.rpc_config;
}
/***** RPC FUNCTIONS ********/
-void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Error Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 1;
- return;
+ return ERR_INVALID_PARAMETER;
}
Variant::Type type = p_args[0]->get_type();
@@ -618,28 +613,28 @@ void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- return;
+ return ERR_INVALID_PARAMETER;
}
StringName method = (*p_args[0]).operator StringName();
- rpcp(0, method, &p_args[1], p_argcount - 1);
-
+ Error err = rpcp(0, method, &p_args[1], p_argcount - 1);
r_error.error = Callable::CallError::CALL_OK;
+ return err;
}
-void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 2) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 2;
- return;
+ return ERR_INVALID_PARAMETER;
}
if (p_args[0]->get_type() != Variant::INT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::INT;
- return;
+ return ERR_INVALID_PARAMETER;
}
Variant::Type type = p_args[1]->get_type();
@@ -647,20 +642,35 @@ void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallEr
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
- return;
+ return ERR_INVALID_PARAMETER;
}
int peer_id = *p_args[0];
StringName method = (*p_args[1]).operator StringName();
- rpcp(peer_id, method, &p_args[2], p_argcount - 2);
-
+ Error err = rpcp(peer_id, method, &p_args[2], p_argcount - 2);
r_error.error = Callable::CallError::CALL_OK;
+ return err;
}
-void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
- ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
+template <typename... VarArgs>
+Error Node::rpc(const StringName &p_method, VarArgs... p_args) {
+ return rpc_id(0, p_method, p_args...);
+}
+
+template <typename... VarArgs>
+Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+}
+
+Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
+ return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
@@ -670,10 +680,6 @@ Ref<MultiplayerAPI> Node::get_multiplayer() const {
return get_tree()->get_multiplayer(get_path());
}
-Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
- return data.rpc_methods;
-}
-
//////////// end of rpc
bool Node::can_process_notification(int p_what) const {
@@ -2404,7 +2410,7 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
if (copy && copytarget) {
const Callable copy_callable = Callable(copytarget, E.callable.get_method());
if (!copy->is_connected(E.signal.get_name(), copy_callable)) {
- copy->connect(E.signal.get_name(), copy_callable, E.binds, E.flags);
+ copy->connect(E.signal.get_name(), copy_callable, E.flags);
}
}
}
@@ -2490,7 +2496,7 @@ void Node::_replace_connections_target(Node *p_new_target) {
c.signal.get_object()->disconnect(c.signal.get_name(), Callable(this, c.callable.get_method()));
bool valid = p_new_target->has_method(c.callable.get_method()) || Ref<Script>(p_new_target->get_script()).is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.callable.get_method());
ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", c.signal.get_object()->get_class(), c.signal.get_name(), c.callable.get_object()->get_class(), c.callable.get_method()));
- c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.binds, c.flags);
+ c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.flags);
}
}
}
@@ -2888,7 +2894,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority);
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
- ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("rpc_config", "method", "config"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);
diff --git a/scene/main/node.h b/scene/main/node.h
index 3c4727f11c..0645c68eb9 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -127,7 +127,7 @@ private:
Node *process_owner = nullptr;
int multiplayer_authority = 1; // Server by default.
- Vector<Multiplayer::RPCConfig> rpc_methods;
+ Variant rpc_config;
// Variables used to properly sort the node when processing, ignored otherwise.
// TODO: Should move all the stuff below to bits.
@@ -183,8 +183,8 @@ private:
TypedArray<Node> _get_children(bool p_include_internal = true) const;
Array _get_groups() const;
- void _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- void _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
_FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; }
_FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; }
@@ -491,30 +491,16 @@ public:
int get_multiplayer_authority() const;
bool is_multiplayer_authority() const;
- uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local = false, Multiplayer::TransferMode p_transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0); // config a local method for RPC
- Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const;
+ void rpc_config(const StringName &p_method, const Variant &p_config); // config a local method for RPC
+ const Variant get_node_rpc_config() const;
template <typename... VarArgs>
- void rpc(const StringName &p_method, VarArgs... p_args) {
- Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
- const Variant *argptrs[sizeof...(p_args) + 1];
- for (uint32_t i = 0; i < sizeof...(p_args); i++) {
- argptrs[i] = &args[i];
- }
- rpcp(0, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
- }
+ Error rpc(const StringName &p_method, VarArgs... p_args);
template <typename... VarArgs>
- void rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) {
- Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
- const Variant *argptrs[sizeof...(p_args) + 1];
- for (uint32_t i = 0; i < sizeof...(p_args); i++) {
- argptrs[i] = &args[i];
- }
- rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
- }
+ Error rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args);
- void rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ Error rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
Ref<MultiplayerAPI> get_multiplayer() const;
@@ -526,4 +512,4 @@ VARIANT_ENUM_CAST(Node::DuplicateFlags);
typedef HashSet<Node *, Node::Comparator> NodeSet;
-#endif
+#endif // NODE_H
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 66482f65dc..644fb3e9cc 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -37,7 +37,6 @@
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_loader.h"
-#include "core/multiplayer/multiplayer_api.h"
#include "core/object/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -45,6 +44,7 @@
#include "node.h"
#include "scene/animation/tween.h"
#include "scene/debugger/scene_debugger.h"
+#include "scene/main/multiplayer_api.h"
#include "scene/main/viewport.h"
#include "scene/resources/font.h"
#include "scene/resources/material.h"
@@ -1213,19 +1213,17 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePat
if (p_root_path.is_empty()) {
ERR_FAIL_COND(!p_multiplayer.is_valid());
if (multiplayer.is_valid()) {
- multiplayer->set_root_path(NodePath());
+ multiplayer->object_configuration_remove(nullptr, NodePath("/" + root->get_name()));
}
multiplayer = p_multiplayer;
- multiplayer->set_root_path("/" + root->get_name());
+ multiplayer->object_configuration_add(nullptr, NodePath("/" + root->get_name()));
} else {
+ if (custom_multiplayers.has(p_root_path)) {
+ custom_multiplayers[p_root_path]->object_configuration_remove(nullptr, p_root_path);
+ }
if (p_multiplayer.is_valid()) {
custom_multiplayers[p_root_path] = p_multiplayer;
- p_multiplayer->set_root_path(p_root_path);
- } else {
- if (custom_multiplayers.has(p_root_path)) {
- custom_multiplayers[p_root_path]->set_root_path(NodePath());
- custom_multiplayers.erase(p_root_path);
- }
+ p_multiplayer->object_configuration_add(nullptr, p_root_path);
}
}
}
@@ -1415,7 +1413,7 @@ SceneTree::SceneTree() {
#endif // _3D_DISABLED
// Initialize network state.
- set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
+ set_multiplayer(MultiplayerAPI::create_default_interface());
root->set_as_audio_listener_2d(true);
current_scene = nullptr;
diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp
index 0049359cad..a621aea9c8 100644
--- a/scene/main/shader_globals_override.cpp
+++ b/scene/main/shader_globals_override.cpp
@@ -64,9 +64,9 @@ bool ShaderGlobalsOverride::_set(const StringName &p_name, const Variant &p_valu
if (active) {
if (o->override.get_type() == Variant::OBJECT) {
RID tex_rid = p_value;
- RS::get_singleton()->global_variable_set_override(*r, tex_rid);
+ RS::get_singleton()->global_shader_uniform_set_override(*r, tex_rid);
} else {
- RS::get_singleton()->global_variable_set_override(*r, p_value);
+ RS::get_singleton()->global_shader_uniform_set_override(*r, p_value);
}
}
o->in_use = p_value.get_type() != Variant::NIL;
@@ -93,13 +93,13 @@ bool ShaderGlobalsOverride::_get(const StringName &p_name, Variant &r_ret) const
void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const {
Vector<StringName> variables;
- variables = RS::get_singleton()->global_variable_get_list();
+ variables = RS::get_singleton()->global_shader_uniform_get_list();
for (int i = 0; i < variables.size(); i++) {
PropertyInfo pinfo;
pinfo.name = "params/" + variables[i];
pinfo.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
- switch (RS::get_singleton()->global_variable_get_type(variables[i])) {
+ switch (RS::get_singleton()->global_shader_uniform_get_type(variables[i])) {
case RS::GLOBAL_VAR_TYPE_BOOL: {
pinfo.type = Variant::BOOL;
} break;
@@ -155,7 +155,7 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const
pinfo.type = Variant::VECTOR3;
} break;
case RS::GLOBAL_VAR_TYPE_VEC4: {
- pinfo.type = Variant::QUATERNION;
+ pinfo.type = Variant::VECTOR4;
} break;
case RS::GLOBAL_VAR_TYPE_RECT2: {
pinfo.type = Variant::RECT2;
@@ -169,15 +169,15 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const
case RS::GLOBAL_VAR_TYPE_MAT3: {
pinfo.type = Variant::BASIS;
} break;
+ case RS::GLOBAL_VAR_TYPE_MAT4: {
+ pinfo.type = Variant::PROJECTION;
+ } break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
pinfo.type = Variant::TRANSFORM2D;
} break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
pinfo.type = Variant::TRANSFORM3D;
} break;
- case RS::GLOBAL_VAR_TYPE_MAT4: {
- pinfo.type = Variant::PACKED_INT32_ARRAY;
- } break;
case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
@@ -234,9 +234,9 @@ void ShaderGlobalsOverride::_activate() {
if (o->in_use && o->override.get_type() != Variant::NIL) {
if (o->override.get_type() == Variant::OBJECT) {
RID tex_rid = o->override;
- RS::get_singleton()->global_variable_set_override(E.key, tex_rid);
+ RS::get_singleton()->global_shader_uniform_set_override(E.key, tex_rid);
} else {
- RS::get_singleton()->global_variable_set_override(E.key, o->override);
+ RS::get_singleton()->global_shader_uniform_set_override(E.key, o->override);
}
}
@@ -258,7 +258,7 @@ void ShaderGlobalsOverride::_notification(int p_what) {
for (const KeyValue<StringName, Override> &E : overrides) {
const Override *o = &E.value;
if (o->in_use) {
- RS::get_singleton()->global_variable_set_override(E.key, Variant());
+ RS::get_singleton()->global_shader_uniform_set_override(E.key, Variant());
}
}
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index c2fa1ace8d..4dd4c8419c 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1234,13 +1234,23 @@ void Viewport::_gui_show_tooltip() {
Rect2i vr = window->get_usable_parent_rect();
if (r.size.x + r.position.x > vr.size.x + vr.position.x) {
- r.position.x = vr.position.x + vr.size.x - r.size.x;
+ // Place it in the opposite direction. If it fails, just hug the border.
+ r.position.x = gui.tooltip_pos.x - r.size.x - tooltip_offset.x;
+
+ if (r.position.x < vr.position.x) {
+ r.position.x = vr.position.x + vr.size.x - r.size.x;
+ }
} else if (r.position.x < vr.position.x) {
r.position.x = vr.position.x;
}
if (r.size.y + r.position.y > vr.size.y + vr.position.y) {
- r.position.y = vr.position.y + vr.size.y - r.size.y;
+ // Same as above.
+ r.position.y = gui.tooltip_pos.y - r.size.y - tooltip_offset.y;
+
+ if (r.position.y < vr.position.y) {
+ r.position.y = vr.position.y + vr.size.y - r.size.y;
+ }
} else if (r.position.y < vr.position.y) {
r.position.y = vr.position.y;
}
@@ -3621,17 +3631,17 @@ float Viewport::get_fsr_sharpness() const {
return fsr_sharpness;
}
-void Viewport::set_fsr_mipmap_bias(float p_fsr_mipmap_bias) {
- if (fsr_mipmap_bias == p_fsr_mipmap_bias) {
+void Viewport::set_texture_mipmap_bias(float p_texture_mipmap_bias) {
+ if (texture_mipmap_bias == p_texture_mipmap_bias) {
return;
}
- fsr_mipmap_bias = p_fsr_mipmap_bias;
- RS::get_singleton()->viewport_set_fsr_mipmap_bias(viewport, p_fsr_mipmap_bias);
+ texture_mipmap_bias = p_texture_mipmap_bias;
+ RS::get_singleton()->viewport_set_texture_mipmap_bias(viewport, p_texture_mipmap_bias);
}
-float Viewport::get_fsr_mipmap_bias() const {
- return fsr_mipmap_bias;
+float Viewport::get_texture_mipmap_bias() const {
+ return texture_mipmap_bias;
}
#endif // _3D_DISABLED
@@ -3773,8 +3783,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &Viewport::set_fsr_sharpness);
ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &Viewport::get_fsr_sharpness);
- ClassDB::bind_method(D_METHOD("set_fsr_mipmap_bias", "fsr_mipmap_bias"), &Viewport::set_fsr_mipmap_bias);
- ClassDB::bind_method(D_METHOD("get_fsr_mipmap_bias"), &Viewport::get_fsr_mipmap_bias);
+ ClassDB::bind_method(D_METHOD("set_texture_mipmap_bias", "texture_mipmap_bias"), &Viewport::set_texture_mipmap_bias);
+ ClassDB::bind_method(D_METHOD("get_texture_mipmap_bias"), &Viewport::get_texture_mipmap_bias);
ClassDB::bind_method(D_METHOD("set_vrs_mode", "mode"), &Viewport::set_vrs_mode);
ClassDB::bind_method(D_METHOD("get_vrs_mode"), &Viewport::get_vrs_mode);
@@ -3804,7 +3814,7 @@ void Viewport::_bind_methods() {
ADD_GROUP("Scaling 3D", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.1"), "set_fsr_mipmap_bias", "get_fsr_mipmap_bias");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
#endif
ADD_GROUP("Variable Rate Shading", "vrs_");
@@ -3978,8 +3988,8 @@ Viewport::Viewport() {
float fsr_sharpness = GLOBAL_GET("rendering/scaling_3d/fsr_sharpness");
set_fsr_sharpness(fsr_sharpness);
- float fsr_mipmap_bias = GLOBAL_GET("rendering/scaling_3d/fsr_mipmap_bias");
- set_fsr_mipmap_bias(fsr_mipmap_bias);
+ float texture_mipmap_bias = GLOBAL_GET("rendering/textures/default_filters/texture_mipmap_bias");
+ set_texture_mipmap_bias(texture_mipmap_bias);
#endif // _3D_DISABLED
set_sdf_oversize(sdf_oversize); // Set to server.
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index a43e3f3ee2..4221baff06 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -304,7 +304,7 @@ private:
Scaling3DMode scaling_3d_mode = SCALING_3D_MODE_BILINEAR;
float scaling_3d_scale = 1.0;
float fsr_sharpness = 0.2f;
- float fsr_mipmap_bias = 0.0f;
+ float texture_mipmap_bias = 0.0f;
bool use_debanding = false;
float mesh_lod_threshold = 1.0;
bool use_occlusion_culling = false;
@@ -540,8 +540,8 @@ public:
void set_fsr_sharpness(float p_fsr_sharpness);
float get_fsr_sharpness() const;
- void set_fsr_mipmap_bias(float p_fsr_mipmap_bias);
- float get_fsr_mipmap_bias() const;
+ void set_texture_mipmap_bias(float p_texture_mipmap_bias);
+ float get_texture_mipmap_bias() const;
void set_use_debanding(bool p_use_debanding);
bool is_using_debanding() const;
@@ -779,4 +779,4 @@ VARIANT_ENUM_CAST(Viewport::RenderInfoType);
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureFilter);
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureRepeat);
-#endif
+#endif // VIEWPORT_H
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 73e8f537d9..bd72858ae6 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1516,7 +1516,7 @@ void Window::_validate_property(PropertyInfo &property) const {
Transform2D Window::get_screen_transform() const {
Transform2D embedder_transform = Transform2D();
if (_get_embedder()) {
- embedder_transform.translate(get_position());
+ embedder_transform.translate_local(get_position());
embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform;
}
return embedder_transform * Viewport::get_screen_transform();
diff --git a/scene/multiplayer/SCsub b/scene/multiplayer/SCsub
deleted file mode 100644
index fc61250247..0000000000
--- a/scene/multiplayer/SCsub
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-
-env.add_source_files(env.scene_sources, "*.cpp")
diff --git a/scene/multiplayer/multiplayer_spawner.cpp b/scene/multiplayer/multiplayer_spawner.cpp
deleted file mode 100644
index 8363d05e54..0000000000
--- a/scene/multiplayer/multiplayer_spawner.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/*************************************************************************/
-/* multiplayer_spawner.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "multiplayer_spawner.h"
-
-#include "core/io/marshalls.h"
-#include "core/multiplayer/multiplayer_api.h"
-#include "scene/main/window.h"
-#include "scene/scene_string_names.h"
-
-#ifdef TOOLS_ENABLED
-/* This is editor only */
-bool MultiplayerSpawner::_set(const StringName &p_name, const Variant &p_value) {
- if (p_name == "_spawnable_scene_count") {
- spawnable_scenes.resize(p_value);
- notify_property_list_changed();
- return true;
- } else {
- String ns = p_name;
- if (ns.begins_with("scenes/")) {
- uint32_t index = ns.get_slicec('/', 1).to_int();
- ERR_FAIL_UNSIGNED_INDEX_V(index, spawnable_scenes.size(), false);
- spawnable_scenes[index].path = p_value;
- return true;
- }
- }
- return false;
-}
-
-bool MultiplayerSpawner::_get(const StringName &p_name, Variant &r_ret) const {
- if (p_name == "_spawnable_scene_count") {
- r_ret = spawnable_scenes.size();
- return true;
- } else {
- String ns = p_name;
- if (ns.begins_with("scenes/")) {
- uint32_t index = ns.get_slicec('/', 1).to_int();
- ERR_FAIL_UNSIGNED_INDEX_V(index, spawnable_scenes.size(), false);
- r_ret = spawnable_scenes[index].path;
- return true;
- }
- }
- return false;
-}
-
-void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Auto Spawn List,scenes/"));
- List<String> exts;
- ResourceLoader::get_recognized_extensions_for_type("PackedScene", &exts);
- String ext_hint;
- for (const String &E : exts) {
- if (!ext_hint.is_empty()) {
- ext_hint += ",";
- }
- ext_hint += "*." + E;
- }
- for (uint32_t i = 0; i < spawnable_scenes.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::STRING, "scenes/" + itos(i), PROPERTY_HINT_FILE, ext_hint, PROPERTY_USAGE_EDITOR));
- }
-}
-#endif
-void MultiplayerSpawner::add_spawnable_scene(const String &p_path) {
- SpawnableScene sc;
- sc.path = p_path;
- if (Engine::get_singleton()->is_editor_hint()) {
- ERR_FAIL_COND(!FileAccess::exists(p_path));
- } else {
- sc.cache = ResourceLoader::load(p_path);
- ERR_FAIL_COND_MSG(sc.cache.is_null(), "Invalid spawnable scene: " + p_path);
- }
- spawnable_scenes.push_back(sc);
-}
-int MultiplayerSpawner::get_spawnable_scene_count() const {
- return spawnable_scenes.size();
-}
-String MultiplayerSpawner::get_spawnable_scene(int p_idx) const {
- return spawnable_scenes[p_idx].path;
-}
-void MultiplayerSpawner::clear_spawnable_scenes() {
- spawnable_scenes.clear();
-}
-
-Vector<String> MultiplayerSpawner::_get_spawnable_scenes() const {
- Vector<String> ss;
- ss.resize(spawnable_scenes.size());
- for (int i = 0; i < ss.size(); i++) {
- ss.write[i] = spawnable_scenes[i].path;
- }
- return ss;
-}
-
-void MultiplayerSpawner::_set_spawnable_scenes(const Vector<String> &p_scenes) {
- clear_spawnable_scenes();
- for (int i = 0; i < p_scenes.size(); i++) {
- add_spawnable_scene(p_scenes[i]);
- }
-}
-
-void MultiplayerSpawner::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_spawnable_scene", "path"), &MultiplayerSpawner::add_spawnable_scene);
- ClassDB::bind_method(D_METHOD("get_spawnable_scene_count"), &MultiplayerSpawner::get_spawnable_scene_count);
- ClassDB::bind_method(D_METHOD("get_spawnable_scene", "path"), &MultiplayerSpawner::get_spawnable_scene);
- ClassDB::bind_method(D_METHOD("clear_spawnable_scenes"), &MultiplayerSpawner::clear_spawnable_scenes);
-
- ClassDB::bind_method(D_METHOD("_get_spawnable_scenes"), &MultiplayerSpawner::_get_spawnable_scenes);
- ClassDB::bind_method(D_METHOD("_set_spawnable_scenes", "scenes"), &MultiplayerSpawner::_set_spawnable_scenes);
-
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_spawnable_scenes", PROPERTY_HINT_NONE, "", (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL)), "_set_spawnable_scenes", "_get_spawnable_scenes");
-
- ClassDB::bind_method(D_METHOD("spawn", "data"), &MultiplayerSpawner::spawn, DEFVAL(Variant()));
-
- ClassDB::bind_method(D_METHOD("get_spawn_path"), &MultiplayerSpawner::get_spawn_path);
- ClassDB::bind_method(D_METHOD("set_spawn_path", "path"), &MultiplayerSpawner::set_spawn_path);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "spawn_path", PROPERTY_HINT_NONE, ""), "set_spawn_path", "get_spawn_path");
-
- ClassDB::bind_method(D_METHOD("get_spawn_limit"), &MultiplayerSpawner::get_spawn_limit);
- ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
-
- GDVIRTUAL_BIND(_spawn_custom, "data");
-
- ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
-}
-
-void MultiplayerSpawner::_update_spawn_node() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
- if (spawn_node.is_valid()) {
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(spawn_node));
- if (node && node->is_connected("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added))) {
- node->disconnect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
- }
- }
- Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
- if (node) {
- spawn_node = node->get_instance_id();
- if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) {
- node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
- }
- } else {
- spawn_node = ObjectID();
- }
-}
-
-void MultiplayerSpawner::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_POST_ENTER_TREE: {
- _update_spawn_node();
- } break;
-
- case NOTIFICATION_EXIT_TREE: {
- _update_spawn_node();
-
- for (const KeyValue<ObjectID, SpawnInfo> &E : tracked_nodes) {
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key));
- ERR_CONTINUE(!node);
- node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit));
- // This is unlikely, but might still crash the engine.
- if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) {
- node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready));
- }
- get_multiplayer()->despawn(node, this);
- }
- tracked_nodes.clear();
- } break;
- }
-}
-
-void MultiplayerSpawner::_node_added(Node *p_node) {
- if (!get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority()) {
- return;
- }
- if (tracked_nodes.has(p_node->get_instance_id())) {
- return;
- }
- const Node *parent = get_spawn_node();
- if (!parent || p_node->get_parent() != parent) {
- return;
- }
- int id = find_spawnable_scene_index_from_path(p_node->get_scene_file_path());
- if (id == INVALID_ID) {
- return;
- }
- const String name = p_node->get_name();
- ERR_FAIL_COND_MSG(name.validate_node_name() != name, vformat("Unable to auto-spawn node with reserved name: %s. Make sure to add your replicated scenes via 'add_child(node, true)' to produce valid names.", name));
- _track(p_node, Variant(), id);
-}
-
-NodePath MultiplayerSpawner::get_spawn_path() const {
- return spawn_path;
-}
-
-void MultiplayerSpawner::set_spawn_path(const NodePath &p_path) {
- spawn_path = p_path;
- _update_spawn_node();
-}
-
-void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_scene_id) {
- ObjectID oid = p_node->get_instance_id();
- if (!tracked_nodes.has(oid)) {
- tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id);
- p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit), varray(p_node->get_instance_id()), CONNECT_ONESHOT);
- p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready), varray(p_node->get_instance_id()), CONNECT_ONESHOT);
- }
-}
-
-void MultiplayerSpawner::_node_ready(ObjectID p_id) {
- get_multiplayer()->spawn(ObjectDB::get_instance(p_id), this);
-}
-
-void MultiplayerSpawner::_node_exit(ObjectID p_id) {
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
- ERR_FAIL_COND(!node);
- if (tracked_nodes.has(p_id)) {
- tracked_nodes.erase(p_id);
- get_multiplayer()->despawn(node, this);
- }
-}
-
-int MultiplayerSpawner::find_spawnable_scene_index_from_path(const String &p_scene) const {
- for (uint32_t i = 0; i < spawnable_scenes.size(); i++) {
- if (spawnable_scenes[i].path == p_scene) {
- return i;
- }
- }
- return INVALID_ID;
-}
-
-int MultiplayerSpawner::find_spawnable_scene_index_from_object(const ObjectID &p_id) const {
- const SpawnInfo *info = tracked_nodes.getptr(p_id);
- return info ? info->id : INVALID_ID;
-}
-
-const Variant MultiplayerSpawner::get_spawn_argument(const ObjectID &p_id) const {
- const SpawnInfo *info = tracked_nodes.getptr(p_id);
- return info ? info->args : Variant();
-}
-
-Node *MultiplayerSpawner::instantiate_scene(int p_id) {
- ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
- ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_id, spawnable_scenes.size(), nullptr);
- Ref<PackedScene> scene = spawnable_scenes[p_id].cache;
- ERR_FAIL_COND_V(scene.is_null(), nullptr);
- return scene->instantiate();
-}
-
-Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) {
- ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
- Object *obj = nullptr;
- Node *node = nullptr;
- if (GDVIRTUAL_CALL(_spawn_custom, p_data, obj)) {
- node = Object::cast_to<Node>(obj);
- }
- return node;
-}
-
-Node *MultiplayerSpawner::spawn(const Variant &p_data) {
- ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr);
- ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
- ERR_FAIL_COND_V_MSG(!GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom), nullptr, "Custom spawn requires the '_spawn_custom' virtual method to be implemented via script.");
-
- Node *parent = get_spawn_node();
- ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node.");
-
- Node *node = instantiate_custom(p_data);
- ERR_FAIL_COND_V_MSG(!node, nullptr, "The '_spawn_custom' implementation must return a valid Node.");
-
- _track(node, p_data);
- parent->add_child(node, true);
- return node;
-}
diff --git a/scene/multiplayer/multiplayer_spawner.h b/scene/multiplayer/multiplayer_spawner.h
deleted file mode 100644
index 2c0eb9a2f0..0000000000
--- a/scene/multiplayer/multiplayer_spawner.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*************************************************************************/
-/* multiplayer_spawner.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef MULTIPLAYER_SPAWNER_H
-#define MULTIPLAYER_SPAWNER_H
-
-#include "scene/main/node.h"
-
-#include "core/templates/local_vector.h"
-#include "core/variant/typed_array.h"
-#include "scene/resources/packed_scene.h"
-#include "scene/resources/scene_replication_config.h"
-
-class MultiplayerSpawner : public Node {
- GDCLASS(MultiplayerSpawner, Node);
-
-public:
- enum {
- INVALID_ID = 0xFF,
- };
-
-private:
- struct SpawnableScene {
- String path;
- Ref<PackedScene> cache;
- };
-
- LocalVector<SpawnableScene> spawnable_scenes;
-
- HashSet<ResourceUID::ID> spawnable_ids;
- NodePath spawn_path;
-
- struct SpawnInfo {
- Variant args;
- int id = INVALID_ID;
- SpawnInfo(Variant p_args, int p_id) {
- id = p_id;
- args = p_args;
- }
- SpawnInfo() {}
- };
-
- ObjectID spawn_node;
- HashMap<ObjectID, SpawnInfo> tracked_nodes;
- uint32_t spawn_limit = 0;
-
- void _update_spawn_node();
- void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID);
- void _node_added(Node *p_node);
- void _node_exit(ObjectID p_id);
- void _node_ready(ObjectID p_id);
-
- Vector<String> _get_spawnable_scenes() const;
- void _set_spawnable_scenes(const Vector<String> &p_scenes);
-
-protected:
- static void _bind_methods();
- void _notification(int p_what);
-
-#ifdef TOOLS_ENABLED
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-#endif
-public:
- Node *get_spawn_node() const { return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; }
-
- void add_spawnable_scene(const String &p_path);
- int get_spawnable_scene_count() const;
- String get_spawnable_scene(int p_idx) const;
- void clear_spawnable_scenes();
-
- NodePath get_spawn_path() const;
- void set_spawn_path(const NodePath &p_path);
- uint32_t get_spawn_limit() const { return spawn_limit; }
- void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
-
- const Variant get_spawn_argument(const ObjectID &p_id) const;
- int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
- int find_spawnable_scene_index_from_path(const String &p_path) const;
- Node *spawn(const Variant &p_data = Variant());
- Node *instantiate_custom(const Variant &p_data);
- Node *instantiate_scene(int p_idx);
-
- GDVIRTUAL1R(Object *, _spawn_custom, const Variant &);
-
- MultiplayerSpawner() {}
-};
-
-#endif // MULTIPLAYER_SPAWNER_H
diff --git a/scene/multiplayer/multiplayer_synchronizer.cpp b/scene/multiplayer/multiplayer_synchronizer.cpp
deleted file mode 100644
index e1b7433968..0000000000
--- a/scene/multiplayer/multiplayer_synchronizer.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*************************************************************************/
-/* multiplayer_synchronizer.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "multiplayer_synchronizer.h"
-
-#include "core/config/engine.h"
-#include "core/multiplayer/multiplayer_api.h"
-
-Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath &p_path) {
- if (p_path.get_name_count() == 0) {
- return p_obj;
- }
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V_MSG(!node || !node->has_node(p_path), nullptr, vformat("Node '%s' not found.", p_path));
- return node->get_node(p_path);
-}
-
-void MultiplayerSynchronizer::_stop() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
- Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
- if (node) {
- get_multiplayer()->replication_stop(node, this);
- }
-}
-
-void MultiplayerSynchronizer::_start() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
- Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
- if (node) {
- get_multiplayer()->replication_start(node, this);
- _update_process();
- }
-}
-
-void MultiplayerSynchronizer::_update_process() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
- Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
- if (!node) {
- return;
- }
- set_process_internal(false);
- set_physics_process_internal(false);
- if (!visibility_filters.size()) {
- return;
- }
- switch (visibility_update_mode) {
- case VISIBILITY_PROCESS_IDLE:
- set_process_internal(true);
- break;
- case VISIBILITY_PROCESS_PHYSICS:
- set_physics_process_internal(true);
- break;
- case VISIBILITY_PROCESS_NONE:
- break;
- }
-}
-
-Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs) {
- ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
- r_variant.resize(p_properties.size());
- r_variant_ptrs.resize(r_variant.size());
- int i = 0;
- for (const NodePath &prop : p_properties) {
- bool valid = false;
- const Object *obj = _get_prop_target(p_obj, prop);
- ERR_FAIL_COND_V(!obj, FAILED);
- r_variant.write[i] = obj->get(prop.get_concatenated_subnames(), &valid);
- r_variant_ptrs.write[i] = &r_variant[i];
- ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
- i++;
- }
- return OK;
-}
-
-Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state) {
- ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
- int i = 0;
- for (const NodePath &prop : p_properties) {
- Object *obj = _get_prop_target(p_obj, prop);
- ERR_FAIL_COND_V(!obj, FAILED);
- obj->set(prop.get_concatenated_subnames(), p_state[i]);
- i += 1;
- }
- return OK;
-}
-
-bool MultiplayerSynchronizer::is_visibility_public() const {
- return peer_visibility.has(0);
-}
-
-void MultiplayerSynchronizer::set_visibility_public(bool p_visible) {
- set_visibility_for(0, p_visible);
-}
-
-bool MultiplayerSynchronizer::is_visible_to(int p_peer) {
- if (visibility_filters.size()) {
- Variant arg = p_peer;
- const Variant *argv[1] = { &arg };
- for (Callable filter : visibility_filters) {
- Variant ret;
- Callable::CallError err;
- filter.call(argv, 1, ret, err);
- ERR_FAIL_COND_V(err.error != Callable::CallError::CALL_OK || ret.get_type() != Variant::BOOL, false);
- if (!ret.operator bool()) {
- return false;
- }
- }
- }
- return peer_visibility.has(0) || peer_visibility.has(p_peer);
-}
-
-void MultiplayerSynchronizer::add_visibility_filter(Callable p_callback) {
- visibility_filters.insert(p_callback);
- _update_process();
-}
-
-void MultiplayerSynchronizer::remove_visibility_filter(Callable p_callback) {
- visibility_filters.erase(p_callback);
- _update_process();
-}
-
-void MultiplayerSynchronizer::set_visibility_for(int p_peer, bool p_visible) {
- if (peer_visibility.has(p_peer) == p_visible) {
- return;
- }
- if (p_visible) {
- peer_visibility.insert(p_peer);
- } else {
- peer_visibility.erase(p_peer);
- }
- update_visibility(p_peer);
-}
-
-bool MultiplayerSynchronizer::get_visibility_for(int p_peer) const {
- return peer_visibility.has(p_peer);
-}
-
-void MultiplayerSynchronizer::set_visibility_update_mode(VisibilityUpdateMode p_mode) {
- visibility_update_mode = p_mode;
- _update_process();
-}
-
-MultiplayerSynchronizer::VisibilityUpdateMode MultiplayerSynchronizer::get_visibility_update_mode() const {
- return visibility_update_mode;
-}
-
-void MultiplayerSynchronizer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path);
- ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path);
-
- ClassDB::bind_method(D_METHOD("set_replication_interval", "milliseconds"), &MultiplayerSynchronizer::set_replication_interval);
- ClassDB::bind_method(D_METHOD("get_replication_interval"), &MultiplayerSynchronizer::get_replication_interval);
-
- ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config);
- ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config);
-
- ClassDB::bind_method(D_METHOD("set_visibility_update_mode", "mode"), &MultiplayerSynchronizer::set_visibility_update_mode);
- ClassDB::bind_method(D_METHOD("get_visibility_update_mode"), &MultiplayerSynchronizer::get_visibility_update_mode);
- ClassDB::bind_method(D_METHOD("update_visibility", "for_peer"), &MultiplayerSynchronizer::update_visibility, DEFVAL(0));
-
- ClassDB::bind_method(D_METHOD("set_visibility_public", "visible"), &MultiplayerSynchronizer::set_visibility_public);
- ClassDB::bind_method(D_METHOD("is_visibility_public"), &MultiplayerSynchronizer::is_visibility_public);
-
- ClassDB::bind_method(D_METHOD("add_visibility_filter", "filter"), &MultiplayerSynchronizer::add_visibility_filter);
- ClassDB::bind_method(D_METHOD("remove_visibility_filter", "filter"), &MultiplayerSynchronizer::remove_visibility_filter);
- ClassDB::bind_method(D_METHOD("set_visibility_for", "peer", "visible"), &MultiplayerSynchronizer::set_visibility_for);
- ClassDB::bind_method(D_METHOD("get_visibility_for", "peer"), &MultiplayerSynchronizer::get_visibility_for);
-
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR), "set_replication_config", "get_replication_config");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,None"), "set_visibility_update_mode", "get_visibility_update_mode");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "public_visibility"), "set_visibility_public", "is_visibility_public");
-
- BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_IDLE);
- BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_PHYSICS);
- BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_NONE);
-
- ADD_SIGNAL(MethodInfo("visibility_changed", PropertyInfo(Variant::INT, "for_peer")));
-}
-
-void MultiplayerSynchronizer::_notification(int p_what) {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
- if (root_path.is_empty()) {
- return;
- }
-
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- _start();
- } break;
-
- case NOTIFICATION_EXIT_TREE: {
- _stop();
- } break;
-
- case NOTIFICATION_INTERNAL_PROCESS:
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- update_visibility(0);
- } break;
- }
-}
-
-void MultiplayerSynchronizer::set_replication_interval(double p_interval) {
- ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)");
- interval_msec = uint64_t(p_interval * 1000);
-}
-
-double MultiplayerSynchronizer::get_replication_interval() const {
- return double(interval_msec) / 1000.0;
-}
-
-uint64_t MultiplayerSynchronizer::get_replication_interval_msec() const {
- return interval_msec;
-}
-
-void MultiplayerSynchronizer::set_replication_config(Ref<SceneReplicationConfig> p_config) {
- replication_config = p_config;
-}
-
-Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() {
- return replication_config;
-}
-
-void MultiplayerSynchronizer::update_visibility(int p_for_peer) {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
- Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
- if (node && get_multiplayer()->has_multiplayer_peer() && is_multiplayer_authority()) {
- emit_signal(SNAME("visibility_changed"), p_for_peer);
- }
-}
-
-void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) {
- _stop();
- root_path = p_path;
- _start();
-}
-
-NodePath MultiplayerSynchronizer::get_root_path() const {
- return root_path;
-}
-
-void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_recursive) {
- Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
- if (!node) {
- Node::set_multiplayer_authority(p_peer_id, p_recursive);
- return;
- }
- get_multiplayer()->replication_stop(node, this);
- Node::set_multiplayer_authority(p_peer_id, p_recursive);
- get_multiplayer()->replication_start(node, this);
-}
-
-MultiplayerSynchronizer::MultiplayerSynchronizer() {
- // Publicly visible by default.
- peer_visibility.insert(0);
-}
diff --git a/scene/multiplayer/multiplayer_synchronizer.h b/scene/multiplayer/multiplayer_synchronizer.h
deleted file mode 100644
index 59f02b84c1..0000000000
--- a/scene/multiplayer/multiplayer_synchronizer.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*************************************************************************/
-/* multiplayer_synchronizer.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef MULTIPLAYER_SYNCHRONIZER_H
-#define MULTIPLAYER_SYNCHRONIZER_H
-
-#include "scene/main/node.h"
-
-#include "scene/resources/scene_replication_config.h"
-
-class MultiplayerSynchronizer : public Node {
- GDCLASS(MultiplayerSynchronizer, Node);
-
-public:
- enum VisibilityUpdateMode {
- VISIBILITY_PROCESS_IDLE,
- VISIBILITY_PROCESS_PHYSICS,
- VISIBILITY_PROCESS_NONE,
- };
-
-private:
- Ref<SceneReplicationConfig> replication_config;
- NodePath root_path = NodePath(".."); // Start with parent, like with AnimationPlayer.
- uint64_t interval_msec = 0;
- VisibilityUpdateMode visibility_update_mode = VISIBILITY_PROCESS_IDLE;
- HashSet<Callable> visibility_filters;
- HashSet<int> peer_visibility;
-
- static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
- void _start();
- void _stop();
- void _update_process();
-
-protected:
- static void _bind_methods();
- void _notification(int p_what);
-
-public:
- static Error get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs);
- static Error set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state);
-
- void set_replication_interval(double p_interval);
- double get_replication_interval() const;
- uint64_t get_replication_interval_msec() const;
-
- void set_replication_config(Ref<SceneReplicationConfig> p_config);
- Ref<SceneReplicationConfig> get_replication_config();
-
- void set_root_path(const NodePath &p_path);
- NodePath get_root_path() const;
- virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true) override;
-
- bool is_visibility_public() const;
- void set_visibility_public(bool p_public);
- bool is_visible_to(int p_peer);
- void set_visibility_for(int p_peer, bool p_visible);
- bool get_visibility_for(int p_peer) const;
- void update_visibility(int p_for_peer);
- void set_visibility_update_mode(VisibilityUpdateMode p_mode);
- void add_visibility_filter(Callable p_callback);
- void remove_visibility_filter(Callable p_callback);
- VisibilityUpdateMode get_visibility_update_mode() const;
-
- MultiplayerSynchronizer();
-};
-
-VARIANT_ENUM_CAST(MultiplayerSynchronizer::VisibilityUpdateMode);
-#endif // MULTIPLAYER_SYNCHRONIZER_H
diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp
deleted file mode 100644
index 79a7dc2d5a..0000000000
--- a/scene/multiplayer/scene_cache_interface.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/*************************************************************************/
-/* scene_cache_interface.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "scene_cache_interface.h"
-
-#include "core/io/marshalls.h"
-#include "scene/main/node.h"
-#include "scene/main/window.h"
-
-MultiplayerCacheInterface *SceneCacheInterface::_create(MultiplayerAPI *p_multiplayer) {
- return memnew(SceneCacheInterface(p_multiplayer));
-}
-
-void SceneCacheInterface::make_default() {
- MultiplayerAPI::create_default_cache_interface = _create;
-}
-
-void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) {
- if (p_connected) {
- path_get_cache.insert(p_id, PathGetCache());
- } else {
- // Cleanup get cache.
- path_get_cache.erase(p_id);
- // Cleanup sent cache.
- // Some refactoring is needed to make this faster and do paths GC.
- for (const KeyValue<NodePath, PathSentCache> &E : path_send_cache) {
- PathSentCache *psc = path_send_cache.getptr(E.key);
- psc->confirmed_peers.erase(p_id);
- }
- }
-}
-
-void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
- ERR_FAIL_COND(!root_node);
- ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
- int ofs = 1;
-
- String methods_md5;
- methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
- ofs += 33;
-
- int id = decode_uint32(&p_packet[ofs]);
- ofs += 4;
-
- String paths;
- paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
-
- NodePath path = paths;
-
- if (!path_get_cache.has(p_from)) {
- path_get_cache[p_from] = PathGetCache();
- }
-
- Node *node = root_node->get_node(path);
- ERR_FAIL_COND(node == nullptr);
- const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5;
- if (valid_rpc_checksum == false) {
- ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
- }
-
- PathGetCache::NodeInfo ni;
- ni.path = path;
-
- path_get_cache[p_from].nodes[id] = ni;
-
- // Encode path to send ack.
- CharString pname = String(path).utf8();
- int len = encode_cstring(pname.get_data(), nullptr);
-
- Vector<uint8_t> packet;
-
- packet.resize(1 + 1 + len);
- packet.write[0] = MultiplayerAPI::NETWORK_COMMAND_CONFIRM_PATH;
- packet.write[1] = valid_rpc_checksum;
- encode_cstring(pname.get_data(), &packet.write[2]);
-
- Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
- ERR_FAIL_COND(multiplayer_peer.is_null());
-
-#ifdef DEBUG_ENABLED
- multiplayer->profile_bandwidth("out", packet.size());
-#endif
-
- multiplayer_peer->set_transfer_channel(0);
- multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
- multiplayer_peer->set_target_peer(p_from);
- multiplayer_peer->put_packet(packet.ptr(), packet.size());
-}
-
-void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
-
- const bool valid_rpc_checksum = p_packet[1];
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
-
- NodePath path = paths;
-
- if (valid_rpc_checksum == false) {
- ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
- }
-
- PathSentCache *psc = path_send_cache.getptr(path);
- ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
-
- HashMap<int, bool>::Iterator E = psc->confirmed_peers.find(p_from);
- ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path.");
- E->value = true;
-}
-
-Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers) {
- // Encode function name.
- const CharString path = String(p_path).utf8();
- const int path_len = encode_cstring(path.get_data(), nullptr);
-
- // Extract MD5 from rpc methods list.
- const String methods_md5 = multiplayer->get_rpc_md5(p_node);
- const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
-
- Vector<uint8_t> packet;
- packet.resize(1 + 4 + path_len + methods_md5_len);
- int ofs = 0;
-
- packet.write[ofs] = MultiplayerAPI::NETWORK_COMMAND_SIMPLIFY_PATH;
- ofs += 1;
-
- ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
-
- ofs += encode_uint32(psc->id, &packet.write[ofs]);
-
- ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
-
- Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
- ERR_FAIL_COND_V(multiplayer_peer.is_null(), ERR_BUG);
-
-#ifdef DEBUG_ENABLED
- multiplayer->profile_bandwidth("out", packet.size() * p_peers.size());
-#endif
-
- Error err = OK;
- for (int peer_id : p_peers) {
- multiplayer_peer->set_target_peer(peer_id);
- multiplayer_peer->set_transfer_channel(0);
- multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
- err = multiplayer_peer->put_packet(packet.ptr(), packet.size());
- ERR_FAIL_COND_V(err != OK, err);
- // Insert into confirmed, but as false since it was not confirmed.
- psc->confirmed_peers.insert(peer_id, false);
- }
- return err;
-}
-
-bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) {
- const PathSentCache *psc = path_send_cache.getptr(p_path);
- ERR_FAIL_COND_V(!psc, false);
- HashMap<int, bool>::ConstIterator F = psc->confirmed_peers.find(p_peer);
- ERR_FAIL_COND_V(!F, false); // Should never happen.
- return F->value;
-}
-
-int SceneCacheInterface::make_object_cache(Object *p_obj) {
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node, -1);
- NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
- // See if the path is cached.
- PathSentCache *psc = path_send_cache.getptr(for_path);
- if (!psc) {
- // Path is not cached, create.
- path_send_cache[for_path] = PathSentCache();
- psc = path_send_cache.getptr(for_path);
- psc->id = last_send_cache_id++;
- }
- return psc->id;
-}
-
-bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) {
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node, false);
-
- r_id = make_object_cache(p_obj);
- ERR_FAIL_COND_V(r_id < 0, false);
- NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
- PathSentCache *psc = path_send_cache.getptr(for_path);
-
- bool has_all_peers = true;
- List<int> peers_to_add; // If one is missing, take note to add it.
-
- if (p_peer_id > 0) {
- // Fast single peer check.
- HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(p_peer_id);
- if (!F) {
- peers_to_add.push_back(p_peer_id); // Need to also be notified.
- has_all_peers = false;
- } else if (!F->value) {
- has_all_peers = false;
- }
- } else {
- // Long and painful.
- for (const int &E : multiplayer->get_connected_peers()) {
- if (p_peer_id < 0 && E == -p_peer_id) {
- continue; // Continue, excluded.
- }
- if (p_peer_id > 0 && E != p_peer_id) {
- continue; // Continue, not for this peer.
- }
-
- HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(E);
- if (!F) {
- peers_to_add.push_back(E); // Need to also be notified.
- has_all_peers = false;
- } else if (!F->value) {
- has_all_peers = false;
- }
- }
- }
-
- if (peers_to_add.size()) {
- _send_confirm_path(node, for_path, psc, peers_to_add);
- }
-
- return has_all_peers;
-}
-
-Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) {
- Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
- ERR_FAIL_COND_V(!root_node, nullptr);
- HashMap<int, PathGetCache>::Iterator E = path_get_cache.find(p_from);
- ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from));
-
- HashMap<int, PathGetCache::NodeInfo>::Iterator F = E->value.nodes.find(p_cache_id);
- ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_cache_id, p_from));
-
- PathGetCache::NodeInfo *ni = &F->value;
- Node *node = root_node->get_node(ni->path);
- if (!node) {
- ERR_PRINT("Failed to get cached path: " + String(ni->path) + ".");
- }
- return node;
-}
-
-void SceneCacheInterface::clear() {
- path_get_cache.clear();
- path_send_cache.clear();
- last_send_cache_id = 1;
-}
diff --git a/scene/multiplayer/scene_cache_interface.h b/scene/multiplayer/scene_cache_interface.h
deleted file mode 100644
index 6bfd683cf4..0000000000
--- a/scene/multiplayer/scene_cache_interface.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*************************************************************************/
-/* scene_cache_interface.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef SCENE_CACHE_INTERFACE_H
-#define SCENE_CACHE_INTERFACE_H
-
-#include "core/multiplayer/multiplayer_api.h"
-
-class SceneCacheInterface : public MultiplayerCacheInterface {
- GDCLASS(SceneCacheInterface, MultiplayerCacheInterface);
-
-private:
- MultiplayerAPI *multiplayer = nullptr;
-
- //path sent caches
- struct PathSentCache {
- HashMap<int, bool> confirmed_peers;
- int id;
- };
-
- //path get caches
- struct PathGetCache {
- struct NodeInfo {
- NodePath path;
- ObjectID instance;
- };
-
- HashMap<int, NodeInfo> nodes;
- };
-
- HashMap<NodePath, PathSentCache> path_send_cache;
- HashMap<int, PathGetCache> path_get_cache;
- int last_send_cache_id = 1;
-
-protected:
- Error _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers);
- static MultiplayerCacheInterface *_create(MultiplayerAPI *p_multiplayer);
-
-public:
- static void make_default();
-
- virtual void clear() override;
- virtual void on_peer_change(int p_id, bool p_connected) override;
- virtual void process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) override;
- virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) override;
-
- // Returns true if all peers have cached path.
- virtual bool send_object_cache(Object *p_obj, int p_target, int &p_id) override;
- virtual int make_object_cache(Object *p_obj) override;
- virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) override;
- virtual bool is_cache_confirmed(NodePath p_path, int p_peer) override;
-
- SceneCacheInterface(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; }
-};
-
-#endif // SCENE_CACHE_INTERFACE_H
diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp
deleted file mode 100644
index c616c5bb85..0000000000
--- a/scene/multiplayer/scene_replication_interface.cpp
+++ /dev/null
@@ -1,533 +0,0 @@
-/*************************************************************************/
-/* scene_replication_interface.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "scene_replication_interface.h"
-
-#include "core/io/marshalls.h"
-#include "scene/main/node.h"
-#include "scene/multiplayer/multiplayer_spawner.h"
-#include "scene/multiplayer/multiplayer_synchronizer.h"
-
-#define MAKE_ROOM(m_amount) \
- if (packet_cache.size() < m_amount) \
- packet_cache.resize(m_amount);
-
-MultiplayerReplicationInterface *SceneReplicationInterface::_create(MultiplayerAPI *p_multiplayer) {
- return memnew(SceneReplicationInterface(p_multiplayer));
-}
-
-void SceneReplicationInterface::make_default() {
- MultiplayerAPI::create_default_replication_interface = _create;
-}
-
-void SceneReplicationInterface::_free_remotes(int p_id) {
- const HashMap<uint32_t, ObjectID> remotes = rep_state->peer_get_remotes(p_id);
- for (const KeyValue<uint32_t, ObjectID> &E : remotes) {
- Node *node = rep_state->get_node(E.value);
- ERR_CONTINUE(!node);
- node->queue_delete();
- }
-}
-
-void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) {
- if (p_connected) {
- rep_state->on_peer_change(p_id, p_connected);
- for (const ObjectID &oid : rep_state->get_spawned_nodes()) {
- _update_spawn_visibility(p_id, oid);
- }
- for (const ObjectID &oid : rep_state->get_synced_nodes()) {
- MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
- ERR_CONTINUE(!sync); // ERR_BUG
- if (sync->is_multiplayer_authority()) {
- _update_sync_visibility(p_id, oid);
- }
- }
- } else {
- _free_remotes(p_id);
- rep_state->on_peer_change(p_id, p_connected);
- }
-}
-
-void SceneReplicationInterface::on_reset() {
- for (int pid : rep_state->get_peers()) {
- _free_remotes(pid);
- }
- rep_state->reset();
-}
-
-void SceneReplicationInterface::on_network_process() {
- uint64_t msec = OS::get_singleton()->get_ticks_msec();
- for (int peer : rep_state->get_peers()) {
- _send_sync(peer, msec);
- }
-}
-
-Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
- MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
- ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER);
- Error err = rep_state->config_add_spawn(node, spawner);
- ERR_FAIL_COND_V(err != OK, err);
- const ObjectID oid = node->get_instance_id();
- if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
- rep_state->ensure_net_id(oid);
- _update_spawn_visibility(0, oid);
- }
- ERR_FAIL_COND_V(err != OK, err);
- return OK;
-}
-
-Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
- MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
- ERR_FAIL_COND_V(!p_obj || !spawner, ERR_INVALID_PARAMETER);
- // Forcibly despawn to all peers that knowns me.
- int len = 0;
- Error err = _make_despawn_packet(node, len);
- ERR_FAIL_COND_V(err != OK, ERR_BUG);
- const ObjectID oid = p_obj->get_instance_id();
- for (int pid : rep_state->get_peers()) {
- if (!rep_state->is_peer_spawn(pid, oid)) {
- continue;
- }
- _send_raw(packet_cache.ptr(), len, pid, true);
- }
- // Also remove spawner tracking from the replication state.
- return rep_state->config_del_spawn(node, spawner);
-}
-
-Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) {
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
- MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
- ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
-
- // Add to synchronizer list and setup visibility.
- rep_state->config_add_sync(node, sync);
- const ObjectID oid = node->get_instance_id();
- sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed), varray(oid));
- if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
- _update_sync_visibility(0, oid);
- }
-
- // Try to apply initial state if spawning (hack to apply if before ready).
- if (pending_spawn == p_obj->get_instance_id()) {
- pending_spawn = ObjectID(); // Make sure this only happens once.
- const List<NodePath> props = sync->get_replication_config()->get_spawn_properties();
- Vector<Variant> vars;
- vars.resize(props.size());
- int consumed;
- Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed);
- ERR_FAIL_COND_V(err, err);
- err = MultiplayerSynchronizer::set_state(props, node, vars);
- ERR_FAIL_COND_V(err, err);
- }
- return OK;
-}
-
-Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_config) {
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
- MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
- ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
- sync->disconnect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed));
- return rep_state->config_del_sync(node, sync);
-}
-
-void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_oid) {
- if (rep_state->is_spawned_node(p_oid)) {
- _update_spawn_visibility(p_peer, p_oid);
- }
- if (rep_state->is_synced_node(p_oid)) {
- _update_sync_visibility(p_peer, p_oid);
- }
-}
-
-Error SceneReplicationInterface::_update_sync_visibility(int p_peer, const ObjectID &p_oid) {
- MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
- ERR_FAIL_COND_V(!sync || !sync->is_multiplayer_authority(), ERR_BUG);
- bool is_visible = sync->is_visible_to(p_peer);
- if (p_peer == 0) {
- for (int pid : rep_state->get_peers()) {
- // Might be visible to this specific peer.
- is_visible = is_visible || sync->is_visible_to(pid);
- if (rep_state->is_peer_sync(pid, p_oid) == is_visible) {
- continue;
- }
- if (is_visible) {
- rep_state->peer_add_sync(pid, p_oid);
- } else {
- rep_state->peer_del_sync(pid, p_oid);
- }
- }
- return OK;
- } else {
- if (is_visible == rep_state->is_peer_sync(p_peer, p_oid)) {
- return OK;
- }
- if (is_visible) {
- return rep_state->peer_add_sync(p_peer, p_oid);
- } else {
- return rep_state->peer_del_sync(p_peer, p_oid);
- }
- }
-}
-
-Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const ObjectID &p_oid) {
- MultiplayerSpawner *spawner = rep_state->get_spawner(p_oid);
- MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_oid));
- ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG);
- bool is_visible = !sync || sync->is_visible_to(p_peer);
- // Spawn (and despawn) when needed.
- HashSet<int> to_spawn;
- HashSet<int> to_despawn;
- if (p_peer) {
- if (is_visible == rep_state->is_peer_spawn(p_peer, p_oid)) {
- return OK;
- }
- if (is_visible) {
- to_spawn.insert(p_peer);
- } else {
- to_despawn.insert(p_peer);
- }
- } else {
- // Check visibility for each peers.
- for (int pid : rep_state->get_peers()) {
- bool peer_visible = is_visible || sync->is_visible_to(pid);
- if (peer_visible == rep_state->is_peer_spawn(pid, p_oid)) {
- continue;
- }
- if (peer_visible) {
- to_spawn.insert(pid);
- } else {
- to_despawn.insert(pid);
- }
- }
- }
- if (to_spawn.size()) {
- int len = 0;
- _make_spawn_packet(node, len);
- for (int pid : to_spawn) {
- int path_id;
- multiplayer->send_object_cache(spawner, pid, path_id);
- _send_raw(packet_cache.ptr(), len, pid, true);
- rep_state->peer_add_spawn(pid, p_oid);
- }
- }
- if (to_despawn.size()) {
- int len = 0;
- _make_despawn_packet(node, len);
- for (int pid : to_despawn) {
- rep_state->peer_del_spawn(pid, p_oid);
- _send_raw(packet_cache.ptr(), len, pid, true);
- }
- }
- return OK;
-}
-
-Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) {
- ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
- ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
-
-#ifdef DEBUG_ENABLED
- multiplayer->profile_bandwidth("out", p_size);
-#endif
-
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer);
- peer->set_transfer_channel(0);
- peer->set_transfer_mode(p_reliable ? Multiplayer::TRANSFER_MODE_RELIABLE : Multiplayer::TRANSFER_MODE_UNRELIABLE);
- return peer->put_packet(p_buffer, p_size);
-}
-
-Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
- ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
-
- const ObjectID oid = p_node->get_instance_id();
- MultiplayerSpawner *spawner = rep_state->get_spawner(oid);
- ERR_FAIL_COND_V(!spawner || !p_node, ERR_BUG);
-
- uint32_t nid = rep_state->get_net_id(oid);
- ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED);
-
- // Prepare custom arg and scene_id
- uint8_t scene_id = spawner->find_spawnable_scene_index_from_object(oid);
- bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
- Variant spawn_arg = spawner->get_spawn_argument(oid);
- int spawn_arg_size = 0;
- if (is_custom) {
- Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false);
- ERR_FAIL_COND_V(err, err);
- }
-
- // Prepare spawn state.
- int state_size = 0;
- Vector<Variant> state_vars;
- Vector<const Variant *> state_varp;
- MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid);
- if (synchronizer) {
- ERR_FAIL_COND_V(synchronizer->get_replication_config().is_null(), ERR_BUG);
- const List<NodePath> props = synchronizer->get_replication_config()->get_spawn_properties();
- Error err = MultiplayerSynchronizer::get_state(props, p_node, state_vars, state_varp);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state.");
- err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), nullptr, state_size);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state.");
- }
-
- // Encode scene ID, path ID, net ID, node name.
- int path_id = multiplayer->make_object_cache(spawner);
- CharString cname = p_node->get_name().operator String().utf8();
- int nlen = encode_cstring(cname.get_data(), nullptr);
- MAKE_ROOM(1 + 1 + 4 + 4 + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size);
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = (uint8_t)MultiplayerAPI::NETWORK_COMMAND_SPAWN;
- ptr[1] = scene_id;
- int ofs = 2;
- ofs += encode_uint32(path_id, &ptr[ofs]);
- ofs += encode_uint32(nid, &ptr[ofs]);
- ofs += encode_uint32(nlen, &ptr[ofs]);
- ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
- // Write args
- if (is_custom) {
- ofs += encode_uint32(spawn_arg_size, &ptr[ofs]);
- Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, &ptr[ofs], spawn_arg_size, false);
- ERR_FAIL_COND_V(err, err);
- ofs += spawn_arg_size;
- }
- // Write state.
- if (state_size) {
- Error err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), &ptr[ofs], state_size);
- ERR_FAIL_COND_V(err, err);
- ofs += state_size;
- }
- r_len = ofs;
- return OK;
-}
-
-Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) {
- const ObjectID oid = p_node->get_instance_id();
- MAKE_ROOM(5);
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = (uint8_t)MultiplayerAPI::NETWORK_COMMAND_DESPAWN;
- int ofs = 1;
- uint32_t nid = rep_state->get_net_id(oid);
- ofs += encode_uint32(nid, &ptr[ofs]);
- r_len = ofs;
- return OK;
-}
-
-Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
- ERR_FAIL_COND_V_MSG(p_buffer_len < 14, ERR_INVALID_DATA, "Invalid spawn packet received");
- int ofs = 1; // The spawn/despawn command.
- uint8_t scene_id = p_buffer[ofs];
- ofs += 1;
- uint32_t node_target = decode_uint32(&p_buffer[ofs]);
- ofs += 4;
- MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(multiplayer->get_cached_object(p_from, node_target));
- ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST);
- ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED);
-
- uint32_t net_id = decode_uint32(&p_buffer[ofs]);
- ofs += 4;
- uint32_t name_len = decode_uint32(&p_buffer[ofs]);
- ofs += 4;
- ERR_FAIL_COND_V_MSG(name_len > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d", p_buffer_len, ofs + name_len));
- ERR_FAIL_COND_V_MSG(name_len < 1, ERR_INVALID_DATA, "Zero spawn name size.");
-
- // We need to make sure no trickery happens here, but we want to allow autogenerated ("@") node names.
- const String name = String::utf8((const char *)&p_buffer[ofs], name_len);
- ERR_FAIL_COND_V_MSG(name.validate_node_name() != name, ERR_INVALID_DATA, vformat("Invalid node name received: '%s'. Make sure to add nodes via 'add_child(node, true)' remotely.", name));
- ofs += name_len;
-
- // Check that we can spawn.
- Node *parent = spawner->get_node_or_null(spawner->get_spawn_path());
- ERR_FAIL_COND_V(!parent, ERR_UNCONFIGURED);
- ERR_FAIL_COND_V(parent->has_node(name), ERR_INVALID_DATA);
-
- Node *node = nullptr;
- if (scene_id == MultiplayerSpawner::INVALID_ID) {
- // Custom spawn.
- ERR_FAIL_COND_V(p_buffer_len - ofs < 4, ERR_INVALID_DATA);
- uint32_t arg_size = decode_uint32(&p_buffer[ofs]);
- ofs += 4;
- ERR_FAIL_COND_V(arg_size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA);
- Variant v;
- Error err = MultiplayerAPI::decode_and_decompress_variant(v, &p_buffer[ofs], arg_size, nullptr, false);
- ERR_FAIL_COND_V(err != OK, err);
- ofs += arg_size;
- node = spawner->instantiate_custom(v);
- } else {
- // Scene based spawn.
- node = spawner->instantiate_scene(scene_id);
- }
- ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED);
- node->set_name(name);
- rep_state->peer_add_remote(p_from, net_id, node, spawner);
- // The initial state will be applied during the sync config (i.e. before _ready).
- int state_len = p_buffer_len - ofs;
- if (state_len) {
- pending_spawn = node->get_instance_id();
- pending_buffer = &p_buffer[ofs];
- pending_buffer_size = state_len;
- }
- parent->add_child(node);
- pending_spawn = ObjectID();
- pending_buffer = nullptr;
- pending_buffer_size = 0;
- return OK;
-}
-
-Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
- ERR_FAIL_COND_V_MSG(p_buffer_len < 5, ERR_INVALID_DATA, "Invalid spawn packet received");
- int ofs = 1; // The spawn/despawn command.
- uint32_t net_id = decode_uint32(&p_buffer[ofs]);
- ofs += 4;
- Node *node = nullptr;
- Error err = rep_state->peer_del_remote(p_from, net_id, &node);
- ERR_FAIL_COND_V(err != OK, err);
- ERR_FAIL_COND_V(!node, ERR_BUG);
- if (node->get_parent() != nullptr) {
- node->get_parent()->remove_child(node);
- }
- node->queue_delete();
- return OK;
-}
-
-void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
- const HashSet<ObjectID> &to_sync = rep_state->get_peer_sync_nodes(p_peer);
- if (to_sync.is_empty()) {
- return;
- }
- MAKE_ROOM(sync_mtu);
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC;
- int ofs = 1;
- ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &ptr[1]);
- // Can only send updates for already notified nodes.
- // This is a lazy implementation, we could optimize much more here with by grouping by replication config.
- for (const ObjectID &oid : to_sync) {
- if (!rep_state->update_sync_time(oid, p_msec)) {
- continue; // nothing to sync.
- }
- MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
- ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid());
- Node *node = rep_state->get_node(oid);
- ERR_CONTINUE(!node);
- uint32_t net_id = rep_state->get_net_id(oid);
- if (net_id == 0 || (net_id & 0x80000000)) {
- int path_id = 0;
- bool verified = multiplayer->send_object_cache(sync, p_peer, path_id);
- ERR_CONTINUE_MSG(path_id < 0, "This should never happen!");
- if (net_id == 0) {
- // First time path based ID.
- net_id = path_id | 0x80000000;
- rep_state->set_net_id(oid, net_id | 0x80000000);
- }
- if (!verified) {
- // The path based sync is not yet confirmed, skipping.
- continue;
- }
- }
- int size;
- Vector<Variant> vars;
- Vector<const Variant *> varp;
- const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
- Error err = MultiplayerSynchronizer::get_state(props, node, vars, varp);
- ERR_CONTINUE_MSG(err != OK, "Unable to retrieve sync state.");
- err = MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), nullptr, size);
- ERR_CONTINUE_MSG(err != OK, "Unable to encode sync state.");
- // TODO Handle single state above MTU.
- ERR_CONTINUE_MSG(size > 3 + 4 + 4 + sync_mtu, vformat("Node states bigger then MTU will not be sent (%d > %d): %s", size, sync_mtu, node->get_path()));
- if (ofs + 4 + 4 + size > sync_mtu) {
- // Send what we got, and reset write.
- _send_raw(packet_cache.ptr(), ofs, p_peer, false);
- ofs = 3;
- }
- if (size) {
- ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]);
- ofs += encode_uint32(size, &ptr[ofs]);
- MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size);
- ofs += size;
- }
- }
- if (ofs > 3) {
- // Got some left over to send.
- _send_raw(packet_cache.ptr(), ofs, p_peer, false);
- }
-}
-
-Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
- ERR_FAIL_COND_V_MSG(p_buffer_len < 11, ERR_INVALID_DATA, "Invalid sync packet received");
- uint16_t time = decode_uint16(&p_buffer[1]);
- int ofs = 3;
- rep_state->peer_sync_recv(p_from, time);
- while (ofs + 8 < p_buffer_len) {
- uint32_t net_id = decode_uint32(&p_buffer[ofs]);
- ofs += 4;
- uint32_t size = decode_uint32(&p_buffer[ofs]);
- ofs += 4;
- Node *node = nullptr;
- if (net_id & 0x80000000) {
- MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_cached_object(p_from, net_id & 0x7FFFFFFF));
- ERR_FAIL_COND_V(!sync || sync->get_multiplayer_authority() != p_from, ERR_UNAUTHORIZED);
- node = sync->get_node(sync->get_root_path());
- } else {
- node = rep_state->peer_get_remote(p_from, net_id);
- }
- if (!node) {
- // Not received yet.
- ofs += size;
- continue;
- }
- const ObjectID oid = node->get_instance_id();
- if (!rep_state->update_last_node_sync(oid, time)) {
- // State is too old.
- ofs += size;
- continue;
- }
- MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
- ERR_FAIL_COND_V(!sync, ERR_BUG);
- ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_BUG);
- const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
- Vector<Variant> vars;
- vars.resize(props.size());
- int consumed;
- Error err = MultiplayerAPI::decode_and_decompress_variants(vars, &p_buffer[ofs], size, consumed);
- ERR_FAIL_COND_V(err, err);
- err = MultiplayerSynchronizer::set_state(props, node, vars);
- ERR_FAIL_COND_V(err, err);
- ofs += size;
- }
- return OK;
-}
diff --git a/scene/multiplayer/scene_replication_interface.h b/scene/multiplayer/scene_replication_interface.h
deleted file mode 100644
index ad3a3be979..0000000000
--- a/scene/multiplayer/scene_replication_interface.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*************************************************************************/
-/* scene_replication_interface.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef SCENE_TREE_REPLICATOR_INTERFACE_H
-#define SCENE_TREE_REPLICATOR_INTERFACE_H
-
-#include "core/multiplayer/multiplayer_api.h"
-
-#include "scene/multiplayer/scene_replication_state.h"
-
-class SceneReplicationInterface : public MultiplayerReplicationInterface {
- GDCLASS(SceneReplicationInterface, MultiplayerReplicationInterface);
-
-private:
- void _send_sync(int p_peer, uint64_t p_msec);
- Error _make_spawn_packet(Node *p_node, int &r_len);
- Error _make_despawn_packet(Node *p_node, int &r_len);
- Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable);
-
- void _visibility_changed(int p_peer, ObjectID p_oid);
- Error _update_sync_visibility(int p_peer, const ObjectID &p_oid);
- Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid);
- void _free_remotes(int p_peer);
-
- Ref<SceneReplicationState> rep_state;
- MultiplayerAPI *multiplayer = nullptr;
- PackedByteArray packet_cache;
- int sync_mtu = 1350; // Highly dependent on underlying protocol.
-
- // An hack to apply the initial state before ready.
- ObjectID pending_spawn;
- const uint8_t *pending_buffer = nullptr;
- int pending_buffer_size = 0;
-
-protected:
- static MultiplayerReplicationInterface *_create(MultiplayerAPI *p_multiplayer);
-
-public:
- static void make_default();
-
- virtual void on_reset() override;
- virtual void on_peer_change(int p_id, bool p_connected) override;
-
- virtual Error on_spawn(Object *p_obj, Variant p_config) override;
- virtual Error on_despawn(Object *p_obj, Variant p_config) override;
- virtual Error on_replication_start(Object *p_obj, Variant p_config) override;
- virtual Error on_replication_stop(Object *p_obj, Variant p_config) override;
- virtual void on_network_process() override;
-
- virtual Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override;
- virtual Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override;
- virtual Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override;
-
- SceneReplicationInterface(MultiplayerAPI *p_multiplayer) {
- rep_state.instantiate();
- multiplayer = p_multiplayer;
- }
-};
-
-#endif // SCENE_TREE_REPLICATOR_INTERFACE_H
diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp
deleted file mode 100644
index f6a51ff9c7..0000000000
--- a/scene/multiplayer/scene_replication_state.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-/*************************************************************************/
-/* scene_replication_state.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "scene/multiplayer/scene_replication_state.h"
-
-#include "core/multiplayer/multiplayer_api.h"
-#include "scene/multiplayer/multiplayer_spawner.h"
-#include "scene/multiplayer/multiplayer_synchronizer.h"
-#include "scene/scene_string_names.h"
-
-SceneReplicationState::TrackedNode &SceneReplicationState::_track(const ObjectID &p_id) {
- if (!tracked_nodes.has(p_id)) {
- tracked_nodes[p_id] = TrackedNode(p_id);
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
- node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack), varray(p_id), Node::CONNECT_ONESHOT);
- }
- return tracked_nodes[p_id];
-}
-
-void SceneReplicationState::_untrack(const ObjectID &p_id) {
- if (tracked_nodes.has(p_id)) {
- uint32_t net_id = tracked_nodes[p_id].net_id;
- uint32_t peer = tracked_nodes[p_id].remote_peer;
- tracked_nodes.erase(p_id);
- // If it was spawned by a remote, remove it from the received nodes.
- if (peer && peers_info.has(peer)) {
- peers_info[peer].recv_nodes.erase(net_id);
- }
- // If we spawned or synced it, we need to remove it from any peer it was sent to.
- if (net_id || peer == 0) {
- for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.sync_nodes.erase(p_id);
- E.value.spawn_nodes.erase(p_id);
- }
- }
- }
-}
-
-const HashMap<uint32_t, ObjectID> SceneReplicationState::peer_get_remotes(int p_peer) const {
- return peers_info.has(p_peer) ? peers_info[p_peer].recv_nodes : HashMap<uint32_t, ObjectID>();
-}
-
-bool SceneReplicationState::update_last_node_sync(const ObjectID &p_id, uint16_t p_time) {
- TrackedNode *tnode = tracked_nodes.getptr(p_id);
- ERR_FAIL_COND_V(!tnode, false);
- if (p_time <= tnode->last_sync && tnode->last_sync - p_time < 32767) {
- return false;
- }
- tnode->last_sync = p_time;
- return true;
-}
-
-bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_msec) {
- TrackedNode *tnode = tracked_nodes.getptr(p_id);
- ERR_FAIL_COND_V(!tnode, false);
- MultiplayerSynchronizer *sync = get_synchronizer(p_id);
- if (!sync) {
- return false;
- }
- if (tnode->last_sync_msec == p_msec) {
- return true;
- }
- if (p_msec >= tnode->last_sync_msec + sync->get_replication_interval_msec()) {
- tnode->last_sync_msec = p_msec;
- return true;
- }
- return false;
-}
-
-uint32_t SceneReplicationState::get_net_id(const ObjectID &p_id) const {
- const TrackedNode *tnode = tracked_nodes.getptr(p_id);
- ERR_FAIL_COND_V(!tnode, 0);
- return tnode->net_id;
-}
-
-void SceneReplicationState::set_net_id(const ObjectID &p_id, uint32_t p_net_id) {
- TrackedNode *tnode = tracked_nodes.getptr(p_id);
- ERR_FAIL_COND(!tnode);
- tnode->net_id = p_net_id;
-}
-
-uint32_t SceneReplicationState::ensure_net_id(const ObjectID &p_id) {
- TrackedNode *tnode = tracked_nodes.getptr(p_id);
- ERR_FAIL_COND_V(!tnode, 0);
- if (tnode->net_id == 0) {
- tnode->net_id = ++last_net_id;
- }
- return tnode->net_id;
-}
-
-void SceneReplicationState::on_peer_change(int p_peer, bool p_connected) {
- if (p_connected) {
- peers_info[p_peer] = PeerInfo();
- known_peers.insert(p_peer);
- } else {
- peers_info.erase(p_peer);
- known_peers.erase(p_peer);
- }
-}
-
-void SceneReplicationState::reset() {
- peers_info.clear();
- known_peers.clear();
- // Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned.
- for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) {
- TrackedNode &tobj = E.value;
- tobj.net_id = 0;
- tobj.remote_peer = 0;
- tobj.last_sync = 0;
- }
-}
-
-Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner) {
- const ObjectID oid = p_node->get_instance_id();
- TrackedNode &tobj = _track(oid);
- ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
- tobj.spawner = p_spawner->get_instance_id();
- spawned_nodes.insert(oid);
- return OK;
-}
-
-Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner) {
- const ObjectID oid = p_node->get_instance_id();
- ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER);
- TrackedNode &tobj = _track(oid);
- ERR_FAIL_COND_V(tobj.spawner != p_spawner->get_instance_id(), ERR_INVALID_PARAMETER);
- tobj.spawner = ObjectID();
- spawned_nodes.erase(oid);
- for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.spawn_nodes.erase(oid);
- }
- return OK;
-}
-
-Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync) {
- const ObjectID oid = p_node->get_instance_id();
- TrackedNode &tobj = _track(oid);
- ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE);
- tobj.synchronizer = p_sync->get_instance_id();
- synced_nodes.insert(oid);
- return OK;
-}
-
-Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync) {
- const ObjectID oid = p_node->get_instance_id();
- ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER);
- TrackedNode &tobj = _track(oid);
- ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER);
- tobj.synchronizer = ObjectID();
- synced_nodes.erase(oid);
- for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.sync_nodes.erase(oid);
- }
- return OK;
-}
-
-Error SceneReplicationState::peer_add_sync(int p_peer, const ObjectID &p_id) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
- peers_info[p_peer].sync_nodes.insert(p_id);
- return OK;
-}
-
-Error SceneReplicationState::peer_del_sync(int p_peer, const ObjectID &p_id) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
- peers_info[p_peer].sync_nodes.erase(p_id);
- return OK;
-}
-
-const HashSet<ObjectID> SceneReplicationState::get_peer_sync_nodes(int p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
- return peers_info[p_peer].sync_nodes;
-}
-
-bool SceneReplicationState::is_peer_sync(int p_peer, const ObjectID &p_id) const {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
- return peers_info[p_peer].sync_nodes.has(p_id);
-}
-
-Error SceneReplicationState::peer_add_spawn(int p_peer, const ObjectID &p_id) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
- peers_info[p_peer].spawn_nodes.insert(p_id);
- return OK;
-}
-
-Error SceneReplicationState::peer_del_spawn(int p_peer, const ObjectID &p_id) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
- peers_info[p_peer].spawn_nodes.erase(p_id);
- return OK;
-}
-
-const HashSet<ObjectID> SceneReplicationState::get_peer_spawn_nodes(int p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
- return peers_info[p_peer].spawn_nodes;
-}
-
-bool SceneReplicationState::is_peer_spawn(int p_peer, const ObjectID &p_id) const {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
- return peers_info[p_peer].spawn_nodes.has(p_id);
-}
-
-Node *SceneReplicationState::peer_get_remote(int p_peer, uint32_t p_net_id) {
- PeerInfo *info = peers_info.getptr(p_peer);
- return info && info->recv_nodes.has(p_net_id) ? Object::cast_to<Node>(ObjectDB::get_instance(info->recv_nodes[p_net_id])) : nullptr;
-}
-
-Error SceneReplicationState::peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner) {
- ERR_FAIL_COND_V(!p_node || !p_spawner, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAVAILABLE);
- PeerInfo &pinfo = peers_info[p_peer];
- ObjectID oid = p_node->get_instance_id();
- TrackedNode &tobj = _track(oid);
- tobj.spawner = p_spawner->get_instance_id();
- tobj.net_id = p_net_id;
- tobj.remote_peer = p_peer;
- tobj.last_sync = pinfo.last_recv_sync;
- // Also track as a remote.
- ERR_FAIL_COND_V(pinfo.recv_nodes.has(p_net_id), ERR_ALREADY_IN_USE);
- pinfo.recv_nodes[p_net_id] = oid;
- return OK;
-}
-
-Error SceneReplicationState::peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAUTHORIZED);
- PeerInfo &info = peers_info[p_peer];
- ERR_FAIL_COND_V(!info.recv_nodes.has(p_net_id), ERR_UNAUTHORIZED);
- *r_node = Object::cast_to<Node>(ObjectDB::get_instance(info.recv_nodes[p_net_id]));
- info.recv_nodes.erase(p_net_id);
- return OK;
-}
-
-uint16_t SceneReplicationState::peer_sync_next(int p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), 0);
- PeerInfo &info = peers_info[p_peer];
- return ++info.last_sent_sync;
-}
-
-void SceneReplicationState::peer_sync_recv(int p_peer, uint16_t p_time) {
- ERR_FAIL_COND(!peers_info.has(p_peer));
- peers_info[p_peer].last_recv_sync = p_time;
-}
diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h
deleted file mode 100644
index 7973b5c904..0000000000
--- a/scene/multiplayer/scene_replication_state.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*************************************************************************/
-/* scene_replication_state.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef SCENE_REPLICATON_STATE_H
-#define SCENE_REPLICATON_STATE_H
-
-#include "core/object/ref_counted.h"
-
-class MultiplayerSpawner;
-class MultiplayerSynchronizer;
-class Node;
-
-class SceneReplicationState : public RefCounted {
-private:
- struct TrackedNode {
- ObjectID id;
- uint32_t net_id = 0;
- uint32_t remote_peer = 0;
- ObjectID spawner;
- ObjectID synchronizer;
- uint16_t last_sync = 0;
- uint64_t last_sync_msec = 0;
-
- bool operator==(const ObjectID &p_other) { return id == p_other; }
-
- Node *get_node() const { return id.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(id)) : nullptr; }
- MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; }
- MultiplayerSynchronizer *get_synchronizer() const { return synchronizer.is_valid() ? Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(synchronizer)) : nullptr; }
- TrackedNode() {}
- TrackedNode(const ObjectID &p_id) { id = p_id; }
- TrackedNode(const ObjectID &p_id, uint32_t p_net_id) {
- id = p_id;
- net_id = p_net_id;
- }
- };
-
- struct PeerInfo {
- HashSet<ObjectID> sync_nodes;
- HashSet<ObjectID> spawn_nodes;
- HashMap<uint32_t, ObjectID> recv_nodes;
- uint16_t last_sent_sync = 0;
- uint16_t last_recv_sync = 0;
- };
-
- HashSet<int> known_peers;
- uint32_t last_net_id = 0;
- HashMap<ObjectID, TrackedNode> tracked_nodes;
- HashMap<int, PeerInfo> peers_info;
- HashSet<ObjectID> spawned_nodes;
- HashSet<ObjectID> synced_nodes;
-
- TrackedNode &_track(const ObjectID &p_id);
- void _untrack(const ObjectID &p_id);
- bool is_tracked(const ObjectID &p_id) const { return tracked_nodes.has(p_id); }
-
-public:
- const HashSet<int> get_peers() const { return known_peers; }
- const HashSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; }
- bool is_spawned_node(const ObjectID &p_id) const { return spawned_nodes.has(p_id); }
- const HashSet<ObjectID> &get_synced_nodes() const { return synced_nodes; }
- bool is_synced_node(const ObjectID &p_id) const { return synced_nodes.has(p_id); }
-
- MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; }
- MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; }
- Node *get_node(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_node() : nullptr; }
- bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time);
- bool update_sync_time(const ObjectID &p_id, uint64_t p_msec);
-
- uint32_t get_net_id(const ObjectID &p_id) const;
- void set_net_id(const ObjectID &p_id, uint32_t p_net_id);
- uint32_t ensure_net_id(const ObjectID &p_id);
-
- void reset();
- void on_peer_change(int p_peer, bool p_connected);
-
- Error config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner);
- Error config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner);
-
- Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
- Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
-
- Error peer_add_sync(int p_peer, const ObjectID &p_id);
- Error peer_del_sync(int p_peer, const ObjectID &p_id);
-
- const HashSet<ObjectID> get_peer_sync_nodes(int p_peer);
- bool is_peer_sync(int p_peer, const ObjectID &p_id) const;
-
- Error peer_add_spawn(int p_peer, const ObjectID &p_id);
- Error peer_del_spawn(int p_peer, const ObjectID &p_id);
-
- const HashSet<ObjectID> get_peer_spawn_nodes(int p_peer);
- bool is_peer_spawn(int p_peer, const ObjectID &p_id) const;
-
- const HashMap<uint32_t, ObjectID> peer_get_remotes(int p_peer) const;
- Node *peer_get_remote(int p_peer, uint32_t p_net_id);
- Error peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner);
- Error peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node);
-
- uint16_t peer_sync_next(int p_peer);
- void peer_sync_recv(int p_peer, uint16_t p_time);
-
- SceneReplicationState() {}
-};
-
-#endif // SCENE_REPLICATON_STATE_H
diff --git a/scene/multiplayer/scene_rpc_interface.cpp b/scene/multiplayer/scene_rpc_interface.cpp
deleted file mode 100644
index 144a10c665..0000000000
--- a/scene/multiplayer/scene_rpc_interface.cpp
+++ /dev/null
@@ -1,509 +0,0 @@
-/*************************************************************************/
-/* scene_rpc_interface.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "scene/multiplayer/scene_rpc_interface.h"
-
-#include "core/debugger/engine_debugger.h"
-#include "core/io/marshalls.h"
-#include "core/multiplayer/multiplayer_api.h"
-#include "scene/main/node.h"
-#include "scene/main/window.h"
-
-MultiplayerRPCInterface *SceneRPCInterface::_create(MultiplayerAPI *p_multiplayer) {
- return memnew(SceneRPCInterface(p_multiplayer));
-}
-
-void SceneRPCInterface::make_default() {
- MultiplayerAPI::create_default_rpc_interface = _create;
-}
-
-#ifdef DEBUG_ENABLED
-_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {
- if (EngineDebugger::is_profiling("rpc")) {
- Array values;
- values.push_back(p_id);
- values.push_back(p_what);
- EngineDebugger::profiler_add_frame_data("rpc", values);
- }
-}
-#else
-_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {}
-#endif
-
-// Returns the packet size stripping the node path added when the node is not yet cached.
-int get_packet_len(uint32_t p_node_target, int p_packet_len) {
- if (p_node_target & 0x80000000) {
- int ofs = p_node_target & 0x7FFFFFFF;
- return p_packet_len - (p_packet_len - ofs);
- } else {
- return p_packet_len;
- }
-}
-
-const Multiplayer::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) {
- const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods();
- for (int i = 0; i < node_config.size(); i++) {
- if (node_config[i].name == p_method) {
- r_id = ((uint16_t)i) | (1 << 15);
- return node_config[i];
- }
- }
- if (p_node->get_script_instance()) {
- const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
- for (int i = 0; i < script_config.size(); i++) {
- if (script_config[i].name == p_method) {
- r_id = (uint16_t)i;
- return script_config[i];
- }
- }
- }
- return Multiplayer::RPCConfig();
-}
-
-const Multiplayer::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) {
- Vector<Multiplayer::RPCConfig> config;
- uint16_t id = p_id;
- if (id & (1 << 15)) {
- id = id & ~(1 << 15);
- config = p_node->get_node_rpc_methods();
- } else if (p_node->get_script_instance()) {
- config = p_node->get_script_instance()->get_rpc_methods();
- }
- if (id < config.size()) {
- return config[id];
- }
- return Multiplayer::RPCConfig();
-}
-
-_FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int p_remote_id) {
- switch (mode) {
- case Multiplayer::RPC_MODE_DISABLED: {
- return false;
- } break;
- case Multiplayer::RPC_MODE_ANY_PEER: {
- return true;
- } break;
- case Multiplayer::RPC_MODE_AUTHORITY: {
- return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority();
- } break;
- }
-
- return false;
-}
-
-String SceneRPCInterface::get_rpc_md5(const Object *p_obj) const {
- const Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node, "");
- String rpc_list;
- const Vector<Multiplayer::RPCConfig> node_config = node->get_node_rpc_methods();
- for (int i = 0; i < node_config.size(); i++) {
- rpc_list += String(node_config[i].name);
- }
- if (node->get_script_instance()) {
- const Vector<Multiplayer::RPCConfig> script_config = node->get_script_instance()->get_rpc_methods();
- for (int i = 0; i < script_config.size(); i++) {
- rpc_list += String(script_config[i].name);
- }
- }
- return rpc_list.md5_text();
-}
-
-Node *SceneRPCInterface::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
- Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
- ERR_FAIL_COND_V(!root_node, nullptr);
- Node *node = nullptr;
-
- if (p_node_target & 0x80000000) {
- // Use full path (not cached yet).
- int ofs = p_node_target & 0x7FFFFFFF;
-
- ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
-
- NodePath np = paths;
-
- node = root_node->get_node(np);
-
- if (!node) {
- ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
- }
- return node;
- } else {
- // Use cached path.
- return Object::cast_to<Node>(multiplayer->get_cached_object(p_from, p_node_target));
- }
-}
-
-void SceneRPCInterface::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) {
- // Extract packet meta
- int packet_min_size = 1;
- int name_id_offset = 1;
- ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
- // Compute the meta size, which depends on the compression level.
- int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT;
- int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT;
-
- switch (node_id_compression) {
- case NETWORK_NODE_ID_COMPRESSION_8:
- packet_min_size += 1;
- name_id_offset += 1;
- break;
- case NETWORK_NODE_ID_COMPRESSION_16:
- packet_min_size += 2;
- name_id_offset += 2;
- break;
- case NETWORK_NODE_ID_COMPRESSION_32:
- packet_min_size += 4;
- name_id_offset += 4;
- break;
- default:
- ERR_FAIL_MSG("Was not possible to extract the node id compression mode.");
- }
- switch (name_id_compression) {
- case NETWORK_NAME_ID_COMPRESSION_8:
- packet_min_size += 1;
- break;
- case NETWORK_NAME_ID_COMPRESSION_16:
- packet_min_size += 2;
- break;
- default:
- ERR_FAIL_MSG("Was not possible to extract the name id compression mode.");
- }
- ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
-
- uint32_t node_target = 0;
- switch (node_id_compression) {
- case NETWORK_NODE_ID_COMPRESSION_8:
- node_target = p_packet[1];
- break;
- case NETWORK_NODE_ID_COMPRESSION_16:
- node_target = decode_uint16(p_packet + 1);
- break;
- case NETWORK_NODE_ID_COMPRESSION_32:
- node_target = decode_uint32(p_packet + 1);
- break;
- default:
- // Unreachable, checked before.
- CRASH_NOW();
- }
-
- Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
- ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found.");
-
- uint16_t name_id = 0;
- switch (name_id_compression) {
- case NETWORK_NAME_ID_COMPRESSION_8:
- name_id = p_packet[name_id_offset];
- break;
- case NETWORK_NAME_ID_COMPRESSION_16:
- name_id = decode_uint16(p_packet + name_id_offset);
- break;
- default:
- // Unreachable, checked before.
- CRASH_NOW();
- }
-
- const int packet_len = get_packet_len(node_target, p_packet_len);
- _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
-}
-
-void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
- ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small.");
-
- // Check that remote can call the RPC on this node.
- const Multiplayer::RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id);
- ERR_FAIL_COND(config.name == StringName());
-
- bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from);
- ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + ".");
-
- int argc = 0;
-
- const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG;
- if (byte_only_or_no_args) {
- if (p_offset < p_packet_len) {
- // This packet contains only bytes.
- argc = 1;
- }
- } else {
- // Normal variant, takes the argument count from the packet.
- ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
- argc = p_packet[p_offset];
- p_offset += 1;
- }
-
- Vector<Variant> args;
- Vector<const Variant *> argp;
- args.resize(argc);
- argp.resize(argc);
-
-#ifdef DEBUG_ENABLED
- _profile_node_data("rpc_in", p_node->get_instance_id());
-#endif
-
- int out;
- MultiplayerAPI::decode_and_decompress_variants(args, &p_packet[p_offset], p_packet_len - p_offset, out, byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
- for (int i = 0; i < argc; i++) {
- argp.write[i] = &args[i];
- }
-
- Callable::CallError ce;
-
- p_node->callp(config.name, (const Variant **)argp.ptr(), argc, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce);
- error = "RPC - " + error;
- ERR_PRINT(error);
- }
-}
-
-void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer.");
-
- ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet.");
-
- ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected.");
-
- ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255).");
-
- if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) {
- ERR_FAIL_COND_MSG(p_to == peer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(peer->get_unique_id()) + ".");
-
- ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + ".");
- }
-
- // See if all peers have cached path (if so, call can be fast).
- int psc_id;
- const bool has_all_peers = multiplayer->send_object_cache(p_from, p_to, psc_id);
-
- // Create base packet, lots of hardcode because it must be tight.
-
- int ofs = 0;
-
-#define MAKE_ROOM(m_amount) \
- if (packet_cache.size() < m_amount) \
- packet_cache.resize(m_amount);
-
- // Encode meta.
- uint8_t command_type = MultiplayerAPI::NETWORK_COMMAND_REMOTE_CALL;
- uint8_t node_id_compression = UINT8_MAX;
- uint8_t name_id_compression = UINT8_MAX;
- bool byte_only_or_no_args = false;
-
- MAKE_ROOM(1);
- // The meta is composed along the way, so just set 0 for now.
- packet_cache.write[0] = 0;
- ofs += 1;
-
- // Encode Node ID.
- if (has_all_peers) {
- // Compress the node ID only if all the target peers already know it.
- if (psc_id >= 0 && psc_id <= 255) {
- // We can encode the id in 1 byte
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = static_cast<uint8_t>(psc_id);
- ofs += 1;
- } else if (psc_id >= 0 && psc_id <= 65535) {
- // We can encode the id in 2 bytes
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
- MAKE_ROOM(ofs + 2);
- encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs]));
- ofs += 2;
- } else {
- // Too big, let's use 4 bytes.
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
- MAKE_ROOM(ofs + 4);
- encode_uint32(psc_id, &(packet_cache.write[ofs]));
- ofs += 4;
- }
- } else {
- // The targets don't know the node yet, so we need to use 32 bits int.
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
- MAKE_ROOM(ofs + 4);
- encode_uint32(psc_id, &(packet_cache.write[ofs]));
- ofs += 4;
- }
-
- // Encode method ID
- if (p_rpc_id <= UINT8_MAX) {
- // The ID fits in 1 byte
- name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id);
- ofs += 1;
- } else {
- // The ID is larger, let's use 2 bytes
- name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
- MAKE_ROOM(ofs + 2);
- encode_uint16(p_rpc_id, &(packet_cache.write[ofs]));
- ofs += 2;
- }
-
- int len;
- Error err = MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, nullptr, len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
- ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC arguments. THIS IS LIKELY A BUG IN THE ENGINE!");
- if (byte_only_or_no_args) {
- MAKE_ROOM(ofs + len);
- } else {
- MAKE_ROOM(ofs + 1 + len);
- packet_cache.write[ofs] = p_argcount;
- ofs += 1;
- }
- if (len) {
- MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, &packet_cache.write[ofs], len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
- ofs += len;
- }
-
- ERR_FAIL_COND(command_type > 7);
- ERR_FAIL_COND(node_id_compression > 3);
- ERR_FAIL_COND(name_id_compression > 1);
-
- // We can now set the meta
- packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0);
-
-#ifdef DEBUG_ENABLED
- multiplayer->profile_bandwidth("out", ofs);
-#endif
-
- // Take chance and set transfer mode, since all send methods will use it.
- peer->set_transfer_channel(p_config.channel);
- peer->set_transfer_mode(p_config.transfer_mode);
-
- if (has_all_peers) {
- // They all have verified paths, so send fast.
- peer->set_target_peer(p_to); // To all of you.
- peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
- } else {
- // Unreachable because the node ID is never compressed if the peers doesn't know it.
- CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32);
-
- // Not all verified path, so send one by one.
-
- // Append path at the end, since we will need it for some packets.
- NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
- CharString pname = String(from_path).utf8();
- int path_len = encode_cstring(pname.get_data(), nullptr);
- MAKE_ROOM(ofs + path_len);
- encode_cstring(pname.get_data(), &(packet_cache.write[ofs]));
-
- for (const int &P : multiplayer->get_connected_peers()) {
- if (p_to < 0 && P == -p_to) {
- continue; // Continue, excluded.
- }
-
- if (p_to > 0 && P != p_to) {
- continue; // Continue, not for this peer.
- }
-
- bool confirmed = multiplayer->is_cache_confirmed(from_path, P);
-
- peer->set_target_peer(P); // To this one specifically.
-
- if (confirmed) {
- // This one confirmed path, so use id.
- encode_uint32(psc_id, &(packet_cache.write[1]));
- peer->put_packet(packet_cache.ptr(), ofs);
- } else {
- // This one did not confirm path yet, so use entire path (sorry!).
- encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag.
- peer->put_packet(packet_cache.ptr(), ofs + path_len);
- }
- }
- }
-}
-
-void SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- ERR_FAIL_COND_MSG(!peer.is_valid(), "Trying to call an RPC while no multiplayer peer is active.");
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND(!node);
- ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
- ERR_FAIL_COND_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a multiplayer peer which is not connected.");
-
- int node_id = peer->get_unique_id();
- bool call_local_native = false;
- bool call_local_script = false;
- uint16_t rpc_id = UINT16_MAX;
- const Multiplayer::RPCConfig config = _get_rpc_config(node, p_method, rpc_id);
- ERR_FAIL_COND_MSG(config.name == StringName(),
- vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is missing or not marked for RPCs in the local script.", p_method, node->get_path()));
- if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
- if (rpc_id & (1 << 15)) {
- call_local_native = config.call_local;
- } else {
- call_local_script = config.call_local;
- }
- }
-
- if (p_peer_id != node_id) {
-#ifdef DEBUG_ENABLED
- _profile_node_data("rpc_out", node->get_instance_id());
-#endif
-
- _send_rpc(node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
- }
-
- if (call_local_native) {
- Callable::CallError ce;
-
- multiplayer->set_remote_sender_override(peer->get_unique_id());
- node->callp(p_method, p_arg, p_argcount, ce);
- multiplayer->set_remote_sender_override(0);
-
- if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce);
- error = "rpc() aborted in local call: - " + error + ".";
- ERR_PRINT(error);
- return;
- }
- }
-
- if (call_local_script) {
- Callable::CallError ce;
- ce.error = Callable::CallError::CALL_OK;
-
- multiplayer->set_remote_sender_override(peer->get_unique_id());
- node->get_script_instance()->callp(p_method, p_arg, p_argcount, ce);
- multiplayer->set_remote_sender_override(0);
-
- if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce);
- error = "rpc() aborted in script local call: - " + error + ".";
- ERR_PRINT(error);
- return;
- }
- }
-
- ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.call_local, "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
-}
diff --git a/scene/multiplayer/scene_rpc_interface.h b/scene/multiplayer/scene_rpc_interface.h
deleted file mode 100644
index 86e1d0d280..0000000000
--- a/scene/multiplayer/scene_rpc_interface.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************/
-/* scene_rpc_interface.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef SCENE_RPC_INTERFACE_H
-#define SCENE_RPC_INTERFACE_H
-
-#include "core/multiplayer/multiplayer.h"
-#include "core/multiplayer/multiplayer_api.h"
-
-class SceneRPCInterface : public MultiplayerRPCInterface {
- GDCLASS(SceneRPCInterface, MultiplayerRPCInterface);
-
-private:
- enum NetworkNodeIdCompression {
- NETWORK_NODE_ID_COMPRESSION_8 = 0,
- NETWORK_NODE_ID_COMPRESSION_16,
- NETWORK_NODE_ID_COMPRESSION_32,
- };
-
- enum NetworkNameIdCompression {
- NETWORK_NAME_ID_COMPRESSION_8 = 0,
- NETWORK_NAME_ID_COMPRESSION_16,
- };
-
- // The RPC meta is composed by a single byte that contains (starting from the least significant bit):
- // - `NetworkCommands` in the first four bits.
- // - `NetworkNodeIdCompression` in the next 2 bits.
- // - `NetworkNameIdCompression` in the next 1 bit.
- // - `byte_only_or_no_args` in the next 1 bit.
- enum {
- NODE_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, // 2 bits for this.
- NAME_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_2_SHIFT,
- BYTE_ONLY_OR_NO_ARGS_SHIFT = MultiplayerAPI::CMD_FLAG_3_SHIFT,
- };
-
- enum {
- NODE_ID_COMPRESSION_FLAG = (1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1)), // 2 bits for this.
- NAME_ID_COMPRESSION_FLAG = (1 << NAME_ID_COMPRESSION_SHIFT),
- BYTE_ONLY_OR_NO_ARGS_FLAG = (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT),
- };
-
- MultiplayerAPI *multiplayer = nullptr;
- Vector<uint8_t> packet_cache;
-
-protected:
- static MultiplayerRPCInterface *_create(MultiplayerAPI *p_multiplayer);
-
- _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id);
- void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
-
- void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
- Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
-
-public:
- static void make_default();
-
- virtual void rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override;
- virtual void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) override;
- virtual String get_rpc_md5(const Object *p_obj) const override;
-
- SceneRPCInterface(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; }
-};
-
-#endif // SCENE_RPC_INTERFACE_H
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 09a283ea53..0878a9f78f 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -130,18 +130,14 @@
#include "scene/main/http_request.h"
#include "scene/main/instance_placeholder.h"
#include "scene/main/missing_node.h"
+#include "scene/main/multiplayer_api.h"
#include "scene/main/resource_preloader.h"
#include "scene/main/scene_tree.h"
#include "scene/main/timer.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
-#include "scene/multiplayer/multiplayer_spawner.h"
-#include "scene/multiplayer/multiplayer_synchronizer.h"
-#include "scene/multiplayer/scene_cache_interface.h"
-#include "scene/multiplayer/scene_replication_interface.h"
-#include "scene/multiplayer/scene_rpc_interface.h"
#include "scene/resources/animation_library.h"
-#include "scene/resources/audio_stream_sample.h"
+#include "scene/resources/audio_stream_wav.h"
#include "scene/resources/bit_map.h"
#include "scene/resources/bone_map.h"
#include "scene/resources/box_shape_3d.h"
@@ -248,6 +244,7 @@
#include "scene/3d/ray_cast_3d.h"
#include "scene/3d/reflection_probe.h"
#include "scene/3d/remote_transform_3d.h"
+#include "scene/3d/shape_cast_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/3d/skeleton_ik_3d.h"
#include "scene/3d/soft_dynamic_body_3d.h"
@@ -322,9 +319,13 @@ void register_scene_types() {
GDREGISTER_ABSTRACT_CLASS(Viewport);
GDREGISTER_CLASS(SubViewport);
GDREGISTER_CLASS(ViewportTexture);
+
+ GDREGISTER_ABSTRACT_CLASS(MultiplayerPeer);
+ GDREGISTER_CLASS(MultiplayerPeerExtension);
+ GDREGISTER_ABSTRACT_CLASS(MultiplayerAPI);
+ GDREGISTER_CLASS(MultiplayerAPIExtension);
+
GDREGISTER_CLASS(HTTPRequest);
- GDREGISTER_CLASS(MultiplayerSpawner);
- GDREGISTER_CLASS(MultiplayerSynchronizer);
GDREGISTER_CLASS(Timer);
GDREGISTER_CLASS(CanvasLayer);
GDREGISTER_CLASS(CanvasModulate);
@@ -549,6 +550,7 @@ void register_scene_types() {
GDREGISTER_CLASS(CollisionShape3D);
GDREGISTER_CLASS(CollisionPolygon3D);
GDREGISTER_CLASS(RayCast3D);
+ GDREGISTER_CLASS(ShapeCast3D);
GDREGISTER_CLASS(MultiMeshInstance3D);
GDREGISTER_CLASS(Curve3D);
@@ -870,13 +872,12 @@ void register_scene_types() {
GDREGISTER_ABSTRACT_CLASS(Font);
GDREGISTER_CLASS(FontFile);
GDREGISTER_CLASS(FontVariation);
+ GDREGISTER_CLASS(SystemFont);
GDREGISTER_CLASS(Curve);
GDREGISTER_CLASS(LabelSettings);
- GDREGISTER_CLASS(SceneReplicationConfig);
-
GDREGISTER_CLASS(TextLine);
GDREGISTER_CLASS(TextParagraph);
@@ -903,7 +904,7 @@ void register_scene_types() {
GDREGISTER_CLASS(AudioStreamPlayer3D);
#endif
GDREGISTER_ABSTRACT_CLASS(VideoStream);
- GDREGISTER_CLASS(AudioStreamSample);
+ GDREGISTER_CLASS(AudioStreamWAV);
OS::get_singleton()->yield(); // may take time to init
@@ -1090,6 +1091,9 @@ void register_scene_types() {
ClassDB::add_compatibility_class("World", "World3D");
// Renamed during 4.0 alpha, added to ease transition between alphas.
+ ClassDB::add_compatibility_class("AudioStreamOGGVorbis", "AudioStreamOggVorbis");
+ ClassDB::add_compatibility_class("AudioStreamSample", "AudioStreamWAV");
+ ClassDB::add_compatibility_class("OGGPacketSequence", "OggPacketSequence");
ClassDB::add_compatibility_class("StreamCubemap", "CompressedCubemap");
ClassDB::add_compatibility_class("StreamCubemapArray", "CompressedCubemapArray");
ClassDB::add_compatibility_class("StreamTexture2D", "CompressedTexture2D");
@@ -1117,9 +1121,6 @@ void register_scene_types() {
}
SceneDebugger::initialize();
- SceneReplicationInterface::make_default();
- SceneRPCInterface::make_default();
- SceneCacheInterface::make_default();
}
void initialize_theme() {
diff --git a/scene/register_scene_types.h b/scene/register_scene_types.h
index f0a14387c1..dce8713976 100644
--- a/scene/register_scene_types.h
+++ b/scene/register_scene_types.h
@@ -36,4 +36,4 @@ void unregister_scene_types();
void initialize_theme();
-#endif
+#endif // REGISTER_SCENE_TYPES_H
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 7183accc66..19545167c8 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -2302,7 +2302,7 @@ Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a
}
Quaternion Animation::_cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const {
- return p_a.cubic_slerp(p_b, p_pre_a, p_post_b, p_c);
+ return p_a.spherical_cubic_interpolate(p_b, p_pre_a, p_post_b, p_c);
}
Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const {
@@ -2363,7 +2363,7 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a
Quaternion pa = p_pre_a;
Quaternion pb = p_post_b;
- return a.cubic_slerp(b, pa, pb, p_c);
+ return a.spherical_cubic_interpolate(b, pa, pb, p_c);
}
case Variant::AABB: {
AABB a = p_a;
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index b4528ccd3a..abbcaa92bc 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -488,4 +488,4 @@ VARIANT_ENUM_CAST(Animation::UpdateMode);
VARIANT_ENUM_CAST(Animation::HandleMode);
VARIANT_ENUM_CAST(Animation::LoopMode);
-#endif
+#endif // ANIMATION_H
diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h
index 7a69cd140a..d63807b6d7 100644
--- a/scene/resources/animation_library.h
+++ b/scene/resources/animation_library.h
@@ -63,4 +63,4 @@ public:
AnimationLibrary();
};
-#endif // ANIMATIONLIBRARY_H
+#endif // ANIMATION_LIBRARY_H
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_wav.cpp
index dcd36284d4..a87c8272ea 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_wav.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* audio_stream_sample.cpp */
+/* audio_stream_wav.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "audio_stream_sample.h"
+#include "audio_stream_wav.h"
#include "core/io/file_access.h"
#include "core/io/marshalls.h"
-void AudioStreamPlaybackSample::start(float p_from_pos) {
- if (base->format == AudioStreamSample::FORMAT_IMA_ADPCM) {
+void AudioStreamPlaybackWAV::start(float p_from_pos) {
+ if (base->format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
//no seeking in IMA_ADPCM
for (int i = 0; i < 2; i++) {
ima_adpcm[i].step_index = 0;
@@ -55,24 +55,24 @@ void AudioStreamPlaybackSample::start(float p_from_pos) {
active = true;
}
-void AudioStreamPlaybackSample::stop() {
+void AudioStreamPlaybackWAV::stop() {
active = false;
}
-bool AudioStreamPlaybackSample::is_playing() const {
+bool AudioStreamPlaybackWAV::is_playing() const {
return active;
}
-int AudioStreamPlaybackSample::get_loop_count() const {
+int AudioStreamPlaybackWAV::get_loop_count() const {
return 0;
}
-float AudioStreamPlaybackSample::get_playback_position() const {
+float AudioStreamPlaybackWAV::get_playback_position() const {
return float(offset >> MIX_FRAC_BITS) / base->mix_rate;
}
-void AudioStreamPlaybackSample::seek(float p_time) {
- if (base->format == AudioStreamSample::FORMAT_IMA_ADPCM) {
+void AudioStreamPlaybackWAV::seek(float p_time) {
+ if (base->format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
return; //no seeking in ima-adpcm
}
@@ -87,7 +87,7 @@ void AudioStreamPlaybackSample::seek(float p_time) {
}
template <class Depth, bool is_stereo, bool is_ima_adpcm>
-void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &offset, int32_t &increment, uint32_t amount, IMA_ADPCM_State *ima_adpcm) {
+void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &offset, int32_t &increment, uint32_t amount, IMA_ADPCM_State *ima_adpcm) {
// this function will be compiled branchless by any decent compiler
int32_t final, final_r, next, next_r;
@@ -124,7 +124,7 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds
ima_adpcm[i].last_nibble++;
const uint8_t *src_ptr = (const uint8_t *)base->data;
- src_ptr += AudioStreamSample::DATA_PAD;
+ src_ptr += AudioStreamWAV::DATA_PAD;
uint8_t nbb = src_ptr[(ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i];
nibble = (ima_adpcm[i].last_nibble & 1) ? (nbb >> 4) : (nbb & 0xF);
@@ -221,7 +221,7 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds
}
}
-int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
if (!base->data || !active) {
for (int i = 0; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
@@ -231,13 +231,13 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
int len = base->data_bytes;
switch (base->format) {
- case AudioStreamSample::FORMAT_8_BITS:
+ case AudioStreamWAV::FORMAT_8_BITS:
len /= 1;
break;
- case AudioStreamSample::FORMAT_16_BITS:
+ case AudioStreamWAV::FORMAT_16_BITS:
len /= 2;
break;
- case AudioStreamSample::FORMAT_IMA_ADPCM:
+ case AudioStreamWAV::FORMAT_IMA_ADPCM:
len *= 2;
break;
}
@@ -251,13 +251,13 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
int64_t loop_begin_fp = ((int64_t)base->loop_begin << MIX_FRAC_BITS);
int64_t loop_end_fp = ((int64_t)base->loop_end << MIX_FRAC_BITS);
int64_t length_fp = ((int64_t)len << MIX_FRAC_BITS);
- int64_t begin_limit = (base->loop_mode != AudioStreamSample::LOOP_DISABLED) ? loop_begin_fp : 0;
- int64_t end_limit = (base->loop_mode != AudioStreamSample::LOOP_DISABLED) ? loop_end_fp : length_fp;
+ int64_t begin_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_begin_fp : 0;
+ int64_t end_limit = (base->loop_mode != AudioStreamWAV::LOOP_DISABLED) ? loop_end_fp : length_fp;
bool is_stereo = base->stereo;
int32_t todo = p_frames;
- if (base->loop_mode == AudioStreamSample::LOOP_BACKWARD) {
+ if (base->loop_mode == AudioStreamWAV::LOOP_BACKWARD) {
sign = -1;
}
@@ -271,20 +271,20 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
//looping
- AudioStreamSample::LoopMode loop_format = base->loop_mode;
- AudioStreamSample::Format format = base->format;
+ AudioStreamWAV::LoopMode loop_format = base->loop_mode;
+ AudioStreamWAV::Format format = base->format;
/* audio data */
uint8_t *dataptr = (uint8_t *)base->data;
- const void *data = dataptr + AudioStreamSample::DATA_PAD;
+ const void *data = dataptr + AudioStreamWAV::DATA_PAD;
AudioFrame *dst_buff = p_buffer;
- if (format == AudioStreamSample::FORMAT_IMA_ADPCM) {
- if (loop_format != AudioStreamSample::LOOP_DISABLED) {
+ if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
+ if (loop_format != AudioStreamWAV::LOOP_DISABLED) {
ima_adpcm[0].loop_pos = loop_begin_fp >> MIX_FRAC_BITS;
ima_adpcm[1].loop_pos = loop_begin_fp >> MIX_FRAC_BITS;
- loop_format = AudioStreamSample::LOOP_FORWARD;
+ loop_format = AudioStreamWAV::LOOP_FORWARD;
}
}
@@ -297,9 +297,9 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
if (increment < 0) {
/* going backwards */
- if (loop_format != AudioStreamSample::LOOP_DISABLED && offset < loop_begin_fp) {
+ if (loop_format != AudioStreamWAV::LOOP_DISABLED && offset < loop_begin_fp) {
/* loopstart reached */
- if (loop_format == AudioStreamSample::LOOP_PINGPONG) {
+ if (loop_format == AudioStreamWAV::LOOP_PINGPONG) {
/* bounce ping pong */
offset = loop_begin_fp + (loop_begin_fp - offset);
increment = -increment;
@@ -317,10 +317,10 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
}
} else {
/* going forward */
- if (loop_format != AudioStreamSample::LOOP_DISABLED && offset >= loop_end_fp) {
+ if (loop_format != AudioStreamWAV::LOOP_DISABLED && offset >= loop_end_fp) {
/* loopend reached */
- if (loop_format == AudioStreamSample::LOOP_PINGPONG) {
+ if (loop_format == AudioStreamWAV::LOOP_PINGPONG) {
/* bounce ping pong */
offset = loop_end_fp - (offset - loop_end_fp);
increment = -increment;
@@ -328,7 +328,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
} else {
/* go to loop-begin */
- if (format == AudioStreamSample::FORMAT_IMA_ADPCM) {
+ if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
for (int i = 0; i < 2; i++) {
ima_adpcm[i].step_index = ima_adpcm[i].loop_step_index;
ima_adpcm[i].predictor = ima_adpcm[i].loop_predictor;
@@ -366,14 +366,14 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
todo -= target;
switch (base->format) {
- case AudioStreamSample::FORMAT_8_BITS: {
+ case AudioStreamWAV::FORMAT_8_BITS: {
if (is_stereo) {
do_resample<int8_t, true, false>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm);
} else {
do_resample<int8_t, false, false>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm);
}
} break;
- case AudioStreamSample::FORMAT_16_BITS: {
+ case AudioStreamWAV::FORMAT_16_BITS: {
if (is_stereo) {
do_resample<int16_t, true, false>((int16_t *)data, dst_buff, offset, increment, target, ima_adpcm);
} else {
@@ -381,7 +381,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
}
} break;
- case AudioStreamSample::FORMAT_IMA_ADPCM: {
+ case AudioStreamWAV::FORMAT_IMA_ADPCM: {
if (is_stereo) {
do_resample<int8_t, true, true>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm);
} else {
@@ -406,73 +406,73 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
return p_frames;
}
-void AudioStreamPlaybackSample::tag_used_streams() {
+void AudioStreamPlaybackWAV::tag_used_streams() {
base->tag_used(get_playback_position());
}
-AudioStreamPlaybackSample::AudioStreamPlaybackSample() {}
+AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {}
/////////////////////
-void AudioStreamSample::set_format(Format p_format) {
+void AudioStreamWAV::set_format(Format p_format) {
format = p_format;
}
-AudioStreamSample::Format AudioStreamSample::get_format() const {
+AudioStreamWAV::Format AudioStreamWAV::get_format() const {
return format;
}
-void AudioStreamSample::set_loop_mode(LoopMode p_loop_mode) {
+void AudioStreamWAV::set_loop_mode(LoopMode p_loop_mode) {
loop_mode = p_loop_mode;
}
-AudioStreamSample::LoopMode AudioStreamSample::get_loop_mode() const {
+AudioStreamWAV::LoopMode AudioStreamWAV::get_loop_mode() const {
return loop_mode;
}
-void AudioStreamSample::set_loop_begin(int p_frame) {
+void AudioStreamWAV::set_loop_begin(int p_frame) {
loop_begin = p_frame;
}
-int AudioStreamSample::get_loop_begin() const {
+int AudioStreamWAV::get_loop_begin() const {
return loop_begin;
}
-void AudioStreamSample::set_loop_end(int p_frame) {
+void AudioStreamWAV::set_loop_end(int p_frame) {
loop_end = p_frame;
}
-int AudioStreamSample::get_loop_end() const {
+int AudioStreamWAV::get_loop_end() const {
return loop_end;
}
-void AudioStreamSample::set_mix_rate(int p_hz) {
+void AudioStreamWAV::set_mix_rate(int p_hz) {
ERR_FAIL_COND(p_hz == 0);
mix_rate = p_hz;
}
-int AudioStreamSample::get_mix_rate() const {
+int AudioStreamWAV::get_mix_rate() const {
return mix_rate;
}
-void AudioStreamSample::set_stereo(bool p_enable) {
+void AudioStreamWAV::set_stereo(bool p_enable) {
stereo = p_enable;
}
-bool AudioStreamSample::is_stereo() const {
+bool AudioStreamWAV::is_stereo() const {
return stereo;
}
-float AudioStreamSample::get_length() const {
+float AudioStreamWAV::get_length() const {
int len = data_bytes;
switch (format) {
- case AudioStreamSample::FORMAT_8_BITS:
+ case AudioStreamWAV::FORMAT_8_BITS:
len /= 1;
break;
- case AudioStreamSample::FORMAT_16_BITS:
+ case AudioStreamWAV::FORMAT_16_BITS:
len /= 2;
break;
- case AudioStreamSample::FORMAT_IMA_ADPCM:
+ case AudioStreamWAV::FORMAT_IMA_ADPCM:
len *= 2;
break;
}
@@ -484,11 +484,11 @@ float AudioStreamSample::get_length() const {
return float(len) / mix_rate;
}
-bool AudioStreamSample::is_monophonic() const {
+bool AudioStreamWAV::is_monophonic() const {
return false;
}
-void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) {
+void AudioStreamWAV::set_data(const Vector<uint8_t> &p_data) {
AudioServer::get_singleton()->lock();
if (data) {
memfree(data);
@@ -510,7 +510,7 @@ void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) {
AudioServer::get_singleton()->unlock();
}
-Vector<uint8_t> AudioStreamSample::get_data() const {
+Vector<uint8_t> AudioStreamWAV::get_data() const {
Vector<uint8_t> pv;
if (data) {
@@ -525,8 +525,8 @@ Vector<uint8_t> AudioStreamSample::get_data() const {
return pv;
}
-Error AudioStreamSample::save_to_wav(const String &p_path) {
- if (format == AudioStreamSample::FORMAT_IMA_ADPCM) {
+Error AudioStreamWAV::save_to_wav(const String &p_path) {
+ if (format == AudioStreamWAV::FORMAT_IMA_ADPCM) {
WARN_PRINT("Saving IMA_ADPC samples are not supported yet");
return ERR_UNAVAILABLE;
}
@@ -544,13 +544,13 @@ Error AudioStreamSample::save_to_wav(const String &p_path) {
int byte_pr_sample = 0;
switch (format) {
- case AudioStreamSample::FORMAT_8_BITS:
+ case AudioStreamWAV::FORMAT_8_BITS:
byte_pr_sample = 1;
break;
- case AudioStreamSample::FORMAT_16_BITS:
+ case AudioStreamWAV::FORMAT_16_BITS:
byte_pr_sample = 2;
break;
- case AudioStreamSample::FORMAT_IMA_ADPCM:
+ case AudioStreamWAV::FORMAT_IMA_ADPCM:
byte_pr_sample = 4;
break;
}
@@ -583,19 +583,19 @@ Error AudioStreamSample::save_to_wav(const String &p_path) {
Vector<uint8_t> data = get_data();
const uint8_t *read_data = data.ptr();
switch (format) {
- case AudioStreamSample::FORMAT_8_BITS:
+ case AudioStreamWAV::FORMAT_8_BITS:
for (unsigned int i = 0; i < data_bytes; i++) {
uint8_t data_point = (read_data[i] + 128);
file->store_8(data_point);
}
break;
- case AudioStreamSample::FORMAT_16_BITS:
+ case AudioStreamWAV::FORMAT_16_BITS:
for (unsigned int i = 0; i < data_bytes / 2; i++) {
uint16_t data_point = decode_uint16(&read_data[i * 2]);
file->store_16(data_point);
}
break;
- case AudioStreamSample::FORMAT_IMA_ADPCM:
+ case AudioStreamWAV::FORMAT_IMA_ADPCM:
//Unimplemented
break;
}
@@ -603,40 +603,40 @@ Error AudioStreamSample::save_to_wav(const String &p_path) {
return OK;
}
-Ref<AudioStreamPlayback> AudioStreamSample::instantiate_playback() {
- Ref<AudioStreamPlaybackSample> sample;
+Ref<AudioStreamPlayback> AudioStreamWAV::instantiate_playback() {
+ Ref<AudioStreamPlaybackWAV> sample;
sample.instantiate();
- sample->base = Ref<AudioStreamSample>(this);
+ sample->base = Ref<AudioStreamWAV>(this);
return sample;
}
-String AudioStreamSample::get_stream_name() const {
+String AudioStreamWAV::get_stream_name() const {
return "";
}
-void AudioStreamSample::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamSample::set_data);
- ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamSample::get_data);
+void AudioStreamWAV::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamWAV::set_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamWAV::get_data);
- ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioStreamSample::set_format);
- ClassDB::bind_method(D_METHOD("get_format"), &AudioStreamSample::get_format);
+ ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioStreamWAV::set_format);
+ ClassDB::bind_method(D_METHOD("get_format"), &AudioStreamWAV::get_format);
- ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AudioStreamSample::set_loop_mode);
- ClassDB::bind_method(D_METHOD("get_loop_mode"), &AudioStreamSample::get_loop_mode);
+ ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AudioStreamWAV::set_loop_mode);
+ ClassDB::bind_method(D_METHOD("get_loop_mode"), &AudioStreamWAV::get_loop_mode);
- ClassDB::bind_method(D_METHOD("set_loop_begin", "loop_begin"), &AudioStreamSample::set_loop_begin);
- ClassDB::bind_method(D_METHOD("get_loop_begin"), &AudioStreamSample::get_loop_begin);
+ ClassDB::bind_method(D_METHOD("set_loop_begin", "loop_begin"), &AudioStreamWAV::set_loop_begin);
+ ClassDB::bind_method(D_METHOD("get_loop_begin"), &AudioStreamWAV::get_loop_begin);
- ClassDB::bind_method(D_METHOD("set_loop_end", "loop_end"), &AudioStreamSample::set_loop_end);
- ClassDB::bind_method(D_METHOD("get_loop_end"), &AudioStreamSample::get_loop_end);
+ ClassDB::bind_method(D_METHOD("set_loop_end", "loop_end"), &AudioStreamWAV::set_loop_end);
+ ClassDB::bind_method(D_METHOD("get_loop_end"), &AudioStreamWAV::get_loop_end);
- ClassDB::bind_method(D_METHOD("set_mix_rate", "mix_rate"), &AudioStreamSample::set_mix_rate);
- ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamSample::get_mix_rate);
+ ClassDB::bind_method(D_METHOD("set_mix_rate", "mix_rate"), &AudioStreamWAV::set_mix_rate);
+ ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamWAV::get_mix_rate);
- ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamSample::set_stereo);
- ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamSample::is_stereo);
+ ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamWAV::set_stereo);
+ ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamWAV::is_stereo);
- ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamSample::save_to_wav);
+ ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamWAV::save_to_wav);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
@@ -656,9 +656,9 @@ void AudioStreamSample::_bind_methods() {
BIND_ENUM_CONSTANT(LOOP_BACKWARD);
}
-AudioStreamSample::AudioStreamSample() {}
+AudioStreamWAV::AudioStreamWAV() {}
-AudioStreamSample::~AudioStreamSample() {
+AudioStreamWAV::~AudioStreamWAV() {
if (data) {
memfree(data);
data = nullptr;
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_wav.h
index 2e694cffe2..d800388d96 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_wav.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* audio_stream_sample.h */
+/* audio_stream_wav.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef AUDIO_STREAM_SAMPLE_H
-#define AUDIO_STREAM_SAMPLE_H
+#ifndef AUDIO_STREAM_WAV_H
+#define AUDIO_STREAM_WAV_H
#include "servers/audio/audio_stream.h"
-class AudioStreamSample;
+class AudioStreamWAV;
-class AudioStreamPlaybackSample : public AudioStreamPlayback {
- GDCLASS(AudioStreamPlaybackSample, AudioStreamPlayback);
+class AudioStreamPlaybackWAV : public AudioStreamPlayback {
+ GDCLASS(AudioStreamPlaybackWAV, AudioStreamPlayback);
enum {
MIX_FRAC_BITS = 13,
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
@@ -57,8 +57,8 @@ class AudioStreamPlaybackSample : public AudioStreamPlayback {
int64_t offset = 0;
int sign = 1;
bool active = false;
- friend class AudioStreamSample;
- Ref<AudioStreamSample> base;
+ friend class AudioStreamWAV;
+ Ref<AudioStreamWAV> base;
template <class Depth, bool is_stereo, bool is_ima_adpcm>
void do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &offset, int32_t &increment, uint32_t amount, IMA_ADPCM_State *ima_adpcm);
@@ -77,11 +77,11 @@ public:
virtual void tag_used_streams() override;
- AudioStreamPlaybackSample();
+ AudioStreamPlaybackWAV();
};
-class AudioStreamSample : public AudioStream {
- GDCLASS(AudioStreamSample, AudioStream);
+class AudioStreamWAV : public AudioStream {
+ GDCLASS(AudioStreamWAV, AudioStream);
RES_BASE_EXTENSION("sample")
public:
@@ -100,7 +100,7 @@ public:
};
private:
- friend class AudioStreamPlaybackSample;
+ friend class AudioStreamPlaybackWAV;
enum {
DATA_PAD = 16 //padding for interpolation
@@ -149,11 +149,11 @@ public:
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
- AudioStreamSample();
- ~AudioStreamSample();
+ AudioStreamWAV();
+ ~AudioStreamWAV();
};
-VARIANT_ENUM_CAST(AudioStreamSample::Format)
-VARIANT_ENUM_CAST(AudioStreamSample::LoopMode)
+VARIANT_ENUM_CAST(AudioStreamWAV::Format)
+VARIANT_ENUM_CAST(AudioStreamWAV::LoopMode)
-#endif // AUDIO_STREAM_SAMPLE_H
+#endif // AUDIO_STREAM_WAV_H
diff --git a/scene/resources/box_shape_3d.h b/scene/resources/box_shape_3d.h
index 4e6db893af..3dd0bb6cb7 100644
--- a/scene/resources/box_shape_3d.h
+++ b/scene/resources/box_shape_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef BOX_SHAPE_H
-#define BOX_SHAPE_H
+#ifndef BOX_SHAPE_3D_H
+#define BOX_SHAPE_3D_H
#include "scene/resources/shape_3d.h"
@@ -56,4 +56,4 @@ public:
BoxShape3D();
};
-#endif // BOX_SHAPE_H
+#endif // BOX_SHAPE_3D_H
diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h
index 4c039ab326..ad7f2e5b74 100644
--- a/scene/resources/capsule_shape_3d.h
+++ b/scene/resources/capsule_shape_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CAPSULE_SHAPE_H
-#define CAPSULE_SHAPE_H
+#ifndef CAPSULE_SHAPE_3D_H
+#define CAPSULE_SHAPE_3D_H
#include "scene/resources/shape_3d.h"
@@ -55,4 +55,4 @@ public:
CapsuleShape3D();
};
-#endif // CAPSULE_SHAPE_H
+#endif // CAPSULE_SHAPE_3D_H
diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h
index 0f49a0d80f..d350d753b0 100644
--- a/scene/resources/concave_polygon_shape_2d.h
+++ b/scene/resources/concave_polygon_shape_2d.h
@@ -52,4 +52,4 @@ public:
ConcavePolygonShape2D();
};
-#endif
+#endif // CONCAVE_POLYGON_SHAPE_2D_H
diff --git a/scene/resources/concave_polygon_shape_3d.h b/scene/resources/concave_polygon_shape_3d.h
index a265590edd..68feacfa25 100644
--- a/scene/resources/concave_polygon_shape_3d.h
+++ b/scene/resources/concave_polygon_shape_3d.h
@@ -77,4 +77,4 @@ public:
ConcavePolygonShape3D();
};
-#endif // CONCAVE_POLYGON_SHAPE_H
+#endif // CONCAVE_POLYGON_SHAPE_3D_H
diff --git a/scene/resources/convex_polygon_shape_3d.h b/scene/resources/convex_polygon_shape_3d.h
index 930edb015d..5ca4d2a713 100644
--- a/scene/resources/convex_polygon_shape_3d.h
+++ b/scene/resources/convex_polygon_shape_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CONVEX_POLYGON_SHAPE_H
-#define CONVEX_POLYGON_SHAPE_H
+#ifndef CONVEX_POLYGON_SHAPE_3D_H
+#define CONVEX_POLYGON_SHAPE_3D_H
#include "scene/resources/shape_3d.h"
@@ -52,4 +52,4 @@ public:
ConvexPolygonShape3D();
};
-#endif // CONVEX_POLYGON_SHAPE_H
+#endif // CONVEX_POLYGON_SHAPE_3D_H
diff --git a/scene/resources/cylinder_shape_3d.h b/scene/resources/cylinder_shape_3d.h
index 65427423c8..f554358044 100644
--- a/scene/resources/cylinder_shape_3d.h
+++ b/scene/resources/cylinder_shape_3d.h
@@ -54,4 +54,4 @@ public:
CylinderShape3D();
};
-#endif // CYLINDER_SHAPE_H
+#endif // CYLINDER_SHAPE_3D_H
diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h
index f777330a07..9b070a90cc 100644
--- a/scene/resources/default_theme/default_theme.h
+++ b/scene/resources/default_theme/default_theme.h
@@ -39,4 +39,4 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel = TextServer::SUBPIXEL_POSITIONING_AUTO, TextServer::Hinting p_font_hinting = TextServer::HINTING_LIGHT, bool p_font_antialiased = true, bool p_font_msdf = false, bool p_font_generate_mipmaps = false);
void clear_default_theme();
-#endif
+#endif // DEFAULT_THEME_H
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 854bd34d6a..a8d4903dad 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -1423,7 +1423,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_fog_light_energy", "get_fog_light_energy");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_scatter", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_fog_sun_scatter", "get_fog_sun_scatter");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_fog_density", "get_fog_density");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater,suffix:m"), "set_fog_height", "get_fog_height");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_lesser,or_greater"), "set_fog_height_density", "get_fog_height_density");
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index f61ac7fcaa..619036d296 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -79,9 +79,9 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL));
// Drawing char.
- ClassDB::bind_method(D_METHOD("get_char_size", "char"), &Font::get_char_size);
- ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0)));
- ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)));
+ ClassDB::bind_method(D_METHOD("get_char_size", "char", "font_size"), &Font::get_char_size);
+ ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "font_size", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0)));
+ ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "font_size", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)));
// Helper functions.
ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
@@ -157,7 +157,7 @@ void Font::set_fallbacks(const TypedArray<Font> &p_fallbacks) {
for (int i = 0; i < fallbacks.size(); i++) {
Ref<Font> f = fallbacks[i];
if (f.is_valid()) {
- f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
}
_invalidate_rids();
@@ -2534,7 +2534,7 @@ void FontVariation::set_base_font(const Ref<Font> &p_font) {
}
base_font = p_font;
if (base_font.is_valid()) {
- base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
_invalidate_rids();
notify_property_list_changed();
@@ -2565,7 +2565,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@@ -2582,7 +2582,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@@ -2592,7 +2592,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@@ -2693,3 +2693,374 @@ FontVariation::FontVariation() {
FontVariation::~FontVariation() {
reset_state();
}
+
+/*************************************************************************/
+/* SystemFont */
+/*************************************************************************/
+
+void SystemFont::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &SystemFont::set_antialiased);
+ ClassDB::bind_method(D_METHOD("is_antialiased"), &SystemFont::is_antialiased);
+
+ ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &SystemFont::set_generate_mipmaps);
+ ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &SystemFont::get_generate_mipmaps);
+
+ ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &SystemFont::set_force_autohinter);
+ ClassDB::bind_method(D_METHOD("is_force_autohinter"), &SystemFont::is_force_autohinter);
+
+ ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &SystemFont::set_hinting);
+ ClassDB::bind_method(D_METHOD("get_hinting"), &SystemFont::get_hinting);
+
+ ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &SystemFont::set_subpixel_positioning);
+ ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &SystemFont::get_subpixel_positioning);
+
+ ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &SystemFont::set_oversampling);
+ ClassDB::bind_method(D_METHOD("get_oversampling"), &SystemFont::get_oversampling);
+
+ ClassDB::bind_method(D_METHOD("get_font_names"), &SystemFont::get_font_names);
+ ClassDB::bind_method(D_METHOD("set_font_names", "names"), &SystemFont::set_font_names);
+
+ ClassDB::bind_method(D_METHOD("set_font_style", "style"), &SystemFont::set_font_style);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "font_names"), "set_font_names", "get_font_names");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic"), "set_font_style", "get_font_style");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "get_generate_mipmaps");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), "set_subpixel_positioning", "get_subpixel_positioning");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks");
+}
+
+void SystemFont::_update_rids() const {
+ Ref<Font> f = _get_base_font_or_default();
+
+ rids.clear();
+ if (fallbacks.is_empty() && f.is_valid()) {
+ RID rid = _get_rid();
+ if (rid.is_valid()) {
+ rids.push_back(rid);
+ }
+
+ const TypedArray<Font> &base_fallbacks = f->get_fallbacks();
+ for (int i = 0; i < base_fallbacks.size(); i++) {
+ _update_rids_fb(base_fallbacks[i], 0);
+ }
+ } else {
+ _update_rids_fb(const_cast<SystemFont *>(this), 0);
+ }
+ dirty_rids = false;
+}
+
+void SystemFont::_update_base_font() {
+ if (base_font.is_valid()) {
+ base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ base_font.unref();
+ }
+
+ face_indeces.clear();
+ ftr_weight = 0;
+ ftr_italic = 0;
+ for (const String &E : names) {
+ if (E.is_empty()) {
+ continue;
+ }
+
+ String path = OS::get_singleton()->get_system_font_path(E, style & TextServer::FONT_BOLD, style & TextServer::FONT_ITALIC);
+ if (path.is_empty()) {
+ continue;
+ }
+ Ref<FontFile> file;
+ file.instantiate();
+ Error err = file->load_dynamic_font(path);
+ if (err != OK) {
+ continue;
+ }
+
+ // If it's a font collection check all faces to match requested style.
+ for (int i = 0; i < file->get_face_count(); i++) {
+ file->set_face_index(0, i);
+ if (((file->get_font_style() & TextServer::FONT_BOLD) == (style & TextServer::FONT_BOLD)) && ((file->get_font_style() & TextServer::FONT_ITALIC) == (style & TextServer::FONT_ITALIC))) {
+ face_indeces.push_back(i);
+ }
+ }
+ if (face_indeces.is_empty()) {
+ face_indeces.push_back(0);
+ }
+ file->set_face_index(0, face_indeces[0]);
+
+ // If it's a variable font, apply weight and italic coordinates to match requested style.
+ Dictionary ftr = file->get_supported_variation_list();
+ if ((style & TextServer::FONT_BOLD) && ftr.has(TS->name_to_tag("weight"))) {
+ ftr_weight = 700;
+ }
+ if ((style & TextServer::FONT_ITALIC) && ftr.has(TS->name_to_tag("italic"))) {
+ ftr_italic = 1;
+ }
+
+ // Apply font rendering settings.
+ file->set_antialiased(antialiased);
+ file->set_generate_mipmaps(mipmaps);
+ file->set_force_autohinter(force_autohinter);
+ file->set_hinting(hinting);
+ file->set_subpixel_positioning(subpixel_positioning);
+ file->set_oversampling(oversampling);
+
+ base_font = file;
+ }
+
+ if (base_font.is_valid()) {
+ base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ }
+
+ _invalidate_rids();
+ notify_property_list_changed();
+}
+
+void SystemFont::reset_state() {
+ if (base_font.is_valid()) {
+ base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ base_font.unref();
+ }
+
+ if (theme_font.is_valid()) {
+ theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ theme_font.unref();
+ }
+
+ names.clear();
+ face_indeces.clear();
+ ftr_weight = 0;
+ ftr_italic = 0;
+ style = 0;
+ antialiased = true;
+ mipmaps = false;
+ force_autohinter = false;
+ hinting = TextServer::HINTING_LIGHT;
+ subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
+ oversampling = 0.f;
+
+ Font::reset_state();
+}
+
+Ref<Font> SystemFont::_get_base_font_or_default() const {
+ if (theme_font.is_valid()) {
+ theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids));
+ theme_font.unref();
+ }
+
+ if (base_font.is_valid()) {
+ return base_font;
+ }
+
+ // Check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ List<StringName> theme_types;
+ Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
+
+ for (const StringName &E : theme_types) {
+ if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) {
+ Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+ if (f.is_valid()) {
+ theme_font = f;
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ }
+ return f;
+ }
+ }
+ }
+
+ // Lastly, fall back on the items defined in the default Theme, if they exist.
+ if (Theme::get_default().is_valid()) {
+ List<StringName> theme_types;
+ Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
+
+ for (const StringName &E : theme_types) {
+ if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) {
+ Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+ if (f.is_valid()) {
+ theme_font = f;
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ }
+ return f;
+ }
+ }
+
+ // If they don't exist, use any type to return the default/empty value.
+ Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
+ if (f.is_valid()) {
+ theme_font = f;
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ }
+ return f;
+ }
+
+ return Ref<Font>();
+}
+
+void SystemFont::set_antialiased(bool p_antialiased) {
+ if (antialiased != p_antialiased) {
+ antialiased = p_antialiased;
+ if (base_font.is_valid()) {
+ base_font->set_antialiased(antialiased);
+ }
+ emit_changed();
+ }
+}
+
+bool SystemFont::is_antialiased() const {
+ return antialiased;
+}
+
+void SystemFont::set_generate_mipmaps(bool p_generate_mipmaps) {
+ if (mipmaps != p_generate_mipmaps) {
+ mipmaps = p_generate_mipmaps;
+ if (base_font.is_valid()) {
+ base_font->set_generate_mipmaps(mipmaps);
+ }
+ emit_changed();
+ }
+}
+
+bool SystemFont::get_generate_mipmaps() const {
+ return mipmaps;
+}
+
+void SystemFont::set_force_autohinter(bool p_force_autohinter) {
+ if (force_autohinter != p_force_autohinter) {
+ force_autohinter = p_force_autohinter;
+ if (base_font.is_valid()) {
+ base_font->set_force_autohinter(force_autohinter);
+ }
+ emit_changed();
+ }
+}
+
+bool SystemFont::is_force_autohinter() const {
+ return force_autohinter;
+}
+
+void SystemFont::set_hinting(TextServer::Hinting p_hinting) {
+ if (hinting != p_hinting) {
+ hinting = p_hinting;
+ if (base_font.is_valid()) {
+ base_font->set_hinting(hinting);
+ }
+ emit_changed();
+ }
+}
+
+TextServer::Hinting SystemFont::get_hinting() const {
+ return hinting;
+}
+
+void SystemFont::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) {
+ if (subpixel_positioning != p_subpixel) {
+ subpixel_positioning = p_subpixel;
+ if (base_font.is_valid()) {
+ base_font->set_subpixel_positioning(subpixel_positioning);
+ }
+ emit_changed();
+ }
+}
+
+TextServer::SubpixelPositioning SystemFont::get_subpixel_positioning() const {
+ return subpixel_positioning;
+}
+
+void SystemFont::set_oversampling(real_t p_oversampling) {
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ if (base_font.is_valid()) {
+ base_font->set_oversampling(oversampling);
+ }
+ emit_changed();
+ }
+}
+
+real_t SystemFont::get_oversampling() const {
+ return oversampling;
+}
+
+void SystemFont::set_font_names(const PackedStringArray &p_names) {
+ if (names != p_names) {
+ names = p_names;
+ _update_base_font();
+ }
+}
+
+PackedStringArray SystemFont::get_font_names() const {
+ return names;
+}
+
+void SystemFont::set_font_style(BitField<TextServer::FontStyle> p_style) {
+ if (style != p_style) {
+ style = p_style;
+ _update_base_font();
+ }
+}
+
+BitField<TextServer::FontStyle> SystemFont::get_font_style() const {
+ return style;
+}
+
+int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const {
+ if (base_font.is_valid()) {
+ return base_font->get_spacing(p_spacing);
+ } else {
+ return 0;
+ }
+}
+
+RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
+ Ref<Font> f = _get_base_font_or_default();
+ if (f.is_valid()) {
+ Dictionary var = p_variation_coordinates;
+ if (ftr_weight > 0 && !var.has(TS->name_to_tag("weight"))) {
+ var[TS->name_to_tag("weight")] = ftr_weight;
+ }
+ if (ftr_italic > 0 && !var.has(TS->name_to_tag("italic"))) {
+ var[TS->name_to_tag("italic")] = ftr_italic;
+ }
+
+ if (!face_indeces.is_empty()) {
+ int face_index = CLAMP(p_face_index, 0, face_indeces.size() - 1);
+ return f->find_variation(var, face_indeces[face_index], p_strength, p_transform);
+ } else {
+ return f->find_variation(var, 0, p_strength, p_transform);
+ }
+ }
+ return RID();
+}
+
+RID SystemFont::_get_rid() const {
+ Ref<Font> f = _get_base_font_or_default();
+ if (f.is_valid()) {
+ if (!face_indeces.is_empty()) {
+ Dictionary var;
+ if (ftr_weight > 0) {
+ var[TS->name_to_tag("weight")] = ftr_weight;
+ }
+ if (ftr_italic > 0) {
+ var[TS->name_to_tag("italic")] = ftr_italic;
+ }
+ return f->find_variation(var, face_indeces[0]);
+ } else {
+ return f->_get_rid();
+ }
+ }
+ return RID();
+}
+
+int64_t SystemFont::get_face_count() const {
+ return face_indeces.size();
+}
+
+SystemFont::SystemFont() {
+ /* NOP */
+}
+
+SystemFont::~SystemFont() {
+ reset_state();
+}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 7a42a4dfea..260b4e521f 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -41,7 +41,7 @@ class TextLine;
class TextParagraph;
/*************************************************************************/
-/* Font */
+/* Font */
/*************************************************************************/
class Font : public Resource {
@@ -381,4 +381,74 @@ public:
~FontVariation();
};
-#endif /* FONT_H */
+/*************************************************************************/
+/* SystemFont */
+/*************************************************************************/
+
+class SystemFont : public Font {
+ GDCLASS(SystemFont, Font);
+
+ PackedStringArray names;
+ BitField<TextServer::FontStyle> style = 0;
+
+ mutable Ref<Font> theme_font;
+
+ Ref<FontFile> base_font;
+ Vector<int> face_indeces;
+ int ftr_weight = 0;
+ int ftr_italic = 0;
+
+ bool antialiased = true;
+ bool mipmaps = false;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ real_t oversampling = 0.f;
+
+protected:
+ static void _bind_methods();
+
+ virtual void _update_base_font();
+ virtual void _update_rids() const override;
+
+ virtual void reset_state() override;
+
+public:
+ virtual Ref<Font> _get_base_font_or_default() const;
+
+ virtual void set_antialiased(bool p_antialiased);
+ virtual bool is_antialiased() const;
+
+ virtual void set_generate_mipmaps(bool p_generate_mipmaps);
+ virtual bool get_generate_mipmaps() const;
+
+ virtual void set_force_autohinter(bool p_force_autohinter);
+ virtual bool is_force_autohinter() const;
+
+ virtual void set_hinting(TextServer::Hinting p_hinting);
+ virtual TextServer::Hinting get_hinting() const;
+
+ virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel);
+ virtual TextServer::SubpixelPositioning get_subpixel_positioning() const;
+
+ virtual void set_oversampling(real_t p_oversampling);
+ virtual real_t get_oversampling() const;
+
+ virtual void set_font_names(const PackedStringArray &p_names);
+ virtual PackedStringArray get_font_names() const;
+
+ virtual void set_font_style(BitField<TextServer::FontStyle> p_style);
+ virtual BitField<TextServer::FontStyle> get_font_style() const override;
+
+ virtual int get_spacing(TextServer::SpacingType p_spacing) const override;
+
+ virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override;
+ virtual RID _get_rid() const override;
+
+ int64_t get_face_count() const override;
+
+ SystemFont();
+ ~SystemFont();
+};
+
+#endif // FONT_H
diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h
index 79d1b15674..633238030b 100644
--- a/scene/resources/height_map_shape_3d.h
+++ b/scene/resources/height_map_shape_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef HEIGHT_MAP_SHAPE_H
-#define HEIGHT_MAP_SHAPE_H
+#ifndef HEIGHT_MAP_SHAPE_3D_H
+#define HEIGHT_MAP_SHAPE_3D_H
#include "scene/resources/shape_3d.h"
@@ -60,4 +60,4 @@ public:
HeightMapShape3D();
};
-#endif /* !HEIGHT_MAP_SHAPE_H */
+#endif // HEIGHT_MAP_SHAPE_3D_H
diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h
index e5f627ae8e..de10fdbfbe 100644
--- a/scene/resources/immediate_mesh.h
+++ b/scene/resources/immediate_mesh.h
@@ -115,4 +115,4 @@ public:
~ImmediateMesh();
};
-#endif // IMMEDIATEMESH_H
+#endif // IMMEDIATE_MESH_H
diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h
index 8f77597a58..bf1d0301d1 100644
--- a/scene/resources/importer_mesh.h
+++ b/scene/resources/importer_mesh.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCENE_IMPORTER_MESH_H
-#define SCENE_IMPORTER_MESH_H
+#ifndef IMPORTER_MESH_H
+#define IMPORTER_MESH_H
#include "core/io/resource.h"
#include "core/templates/local_vector.h"
@@ -130,4 +130,5 @@ public:
Ref<ArrayMesh> get_mesh(const Ref<ArrayMesh> &p_base = Ref<ArrayMesh>());
void clear();
};
-#endif // SCENE_IMPORTER_MESH_H
+
+#endif // IMPORTER_MESH_H
diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp
index e8b986b431..ef380a68f9 100644
--- a/scene/resources/label_settings.cpp
+++ b/scene/resources/label_settings.cpp
@@ -99,7 +99,7 @@ void LabelSettings::set_font(const Ref<Font> &p_font) {
}
font = p_font;
if (font.is_valid()) {
- font->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ font->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed), CONNECT_REFERENCE_COUNTED);
}
emit_changed();
}
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index b7a3b677f5..f07232a3ad 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -202,7 +202,98 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const {
void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
if (!shader.is_null()) {
- shader->get_param_list(p_list);
+ List<PropertyInfo> list;
+ shader->get_param_list(&list, true);
+
+ HashMap<String, HashMap<String, List<PropertyInfo>>> groups;
+ {
+ HashMap<String, List<PropertyInfo>> none_subgroup;
+ none_subgroup.insert("<None>", List<PropertyInfo>());
+ groups.insert("<None>", none_subgroup);
+ }
+
+ String last_group = "<None>";
+ String last_subgroup = "<None>";
+
+ bool is_none_group_undefined = true;
+ bool is_none_group = true;
+
+ for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) {
+ if (E->get().usage == PROPERTY_USAGE_GROUP) {
+ if (!E->get().name.is_empty()) {
+ Vector<String> vgroup = E->get().name.split("::");
+ last_group = vgroup[0];
+ if (vgroup.size() > 1) {
+ last_subgroup = vgroup[1];
+ } else {
+ last_subgroup = "<None>";
+ }
+ is_none_group = false;
+
+ if (!groups.has(last_group)) {
+ PropertyInfo info;
+ info.usage = PROPERTY_USAGE_GROUP;
+ info.name = last_group;
+
+ List<PropertyInfo> none_subgroup;
+ none_subgroup.push_back(info);
+
+ HashMap<String, List<PropertyInfo>> subgroup_map;
+ subgroup_map.insert("<None>", none_subgroup);
+
+ groups.insert(last_group, subgroup_map);
+ }
+
+ if (!groups[last_group].has(last_subgroup)) {
+ PropertyInfo info;
+ info.usage = PROPERTY_USAGE_SUBGROUP;
+ info.name = last_subgroup;
+
+ List<PropertyInfo> subgroup;
+ subgroup.push_back(info);
+
+ groups[last_group].insert(last_subgroup, subgroup);
+ }
+ } else {
+ last_group = "<None>";
+ last_subgroup = "<None>";
+ is_none_group = true;
+ }
+ continue; // Pass group.
+ }
+
+ if (is_none_group_undefined && is_none_group) {
+ is_none_group_undefined = false;
+
+ PropertyInfo info;
+ info.usage = PROPERTY_USAGE_GROUP;
+ info.name = "Shader Param";
+ groups["<None>"]["<None>"].push_back(info);
+ }
+
+ PropertyInfo info = E->get();
+ info.name = info.name;
+ groups[last_group][last_subgroup].push_back(info);
+ }
+
+ // Sort groups alphabetically.
+ List<UniformProp> props;
+ for (HashMap<String, HashMap<String, List<PropertyInfo>>>::Iterator group = groups.begin(); group; ++group) {
+ for (HashMap<String, List<PropertyInfo>>::Iterator subgroup = group->value.begin(); subgroup; ++subgroup) {
+ for (List<PropertyInfo>::Element *item = subgroup->value.front(); item; item = item->next()) {
+ if (subgroup->key == "<None>") {
+ props.push_back({ group->key, item->get() });
+ } else {
+ props.push_back({ group->key + "::" + subgroup->key, item->get() });
+ }
+ }
+ }
+ }
+ props.sort_custom<UniformPropComparator>();
+
+ for (List<UniformProp>::Element *E = props.front(); E; E = E->next()) {
+ p_list->push_back(E->get().info);
+ }
}
}
diff --git a/scene/resources/material.h b/scene/resources/material.h
index b845fd68c8..8c04817c6b 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -84,6 +84,17 @@ class ShaderMaterial : public Material {
HashMap<StringName, Variant> param_cache;
+ struct UniformProp {
+ String str;
+ PropertyInfo info;
+ };
+
+ struct UniformPropComparator {
+ bool operator()(const UniformProp &p_a, const UniformProp &p_b) const {
+ return p_a.str.naturalnocasecmp_to(p_b.str) < 0;
+ }
+ };
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -809,4 +820,4 @@ public:
//////////////////////
-#endif
+#endif // MATERIAL_H
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 3e7b0a2808..ec9db89794 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -260,6 +260,64 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
return triangle_mesh;
}
+Ref<TriangleMesh> Mesh::generate_surface_triangle_mesh(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, get_surface_count(), Ref<TriangleMesh>());
+
+ if (surface_triangle_meshes.size() != get_surface_count()) {
+ surface_triangle_meshes.resize(get_surface_count());
+ }
+
+ if (surface_triangle_meshes[p_surface].is_valid()) {
+ return surface_triangle_meshes[p_surface];
+ }
+
+ int facecount = 0;
+
+ if (surface_get_primitive_type(p_surface) != PRIMITIVE_TRIANGLES) {
+ return Ref<TriangleMesh>();
+ }
+
+ if (surface_get_format(p_surface) & ARRAY_FORMAT_INDEX) {
+ facecount += surface_get_array_index_len(p_surface);
+ } else {
+ facecount += surface_get_array_len(p_surface);
+ }
+
+ Vector<Vector3> faces;
+ faces.resize(facecount);
+ Vector3 *facesw = faces.ptrw();
+
+ Array a = surface_get_arrays(p_surface);
+ ERR_FAIL_COND_V(a.is_empty(), Ref<TriangleMesh>());
+
+ int vc = surface_get_array_len(p_surface);
+ Vector<Vector3> vertices = a[ARRAY_VERTEX];
+ const Vector3 *vr = vertices.ptr();
+ int widx = 0;
+
+ if (surface_get_format(p_surface) & ARRAY_FORMAT_INDEX) {
+ int ic = surface_get_array_index_len(p_surface);
+ Vector<int> indices = a[ARRAY_INDEX];
+ const int *ir = indices.ptr();
+
+ for (int j = 0; j < ic; j++) {
+ int index = ir[j];
+ facesw[widx++] = vr[index];
+ }
+
+ } else {
+ for (int j = 0; j < vc; j++) {
+ facesw[widx++] = vr[j];
+ }
+ }
+
+ Ref<TriangleMesh> triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
+ triangle_mesh->create(faces);
+ surface_triangle_meshes.set(p_surface, triangle_mesh);
+
+ return triangle_mesh;
+}
+
void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) {
if (debug_lines.size() > 0) {
r_lines = debug_lines;
@@ -320,6 +378,14 @@ Vector<Face3> Mesh::get_faces() const {
return Vector<Face3>();
}
+Vector<Face3> Mesh::get_surface_faces(int p_surface) const {
+ Ref<TriangleMesh> tm = generate_surface_triangle_mesh(p_surface);
+ if (tm.is_valid()) {
+ return tm->get_faces();
+ }
+ return Vector<Face3>();
+}
+
Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const {
if (p_simplify) {
ConvexDecompositionSettings settings;
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index b166d12ead..142373ce7f 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -42,6 +42,7 @@ class Mesh : public Resource {
GDCLASS(Mesh, Resource);
mutable Ref<TriangleMesh> triangle_mesh; //cached
+ mutable Vector<Ref<TriangleMesh>> surface_triangle_meshes; //cached
mutable Vector<Vector3> debug_lines;
Size2i lightmap_size_hint;
@@ -161,7 +162,9 @@ public:
virtual AABB get_aabb() const;
Vector<Face3> get_faces() const;
+ Vector<Face3> get_surface_faces(int p_surface) const;
Ref<TriangleMesh> generate_triangle_mesh() const;
+ Ref<TriangleMesh> generate_surface_triangle_mesh(int p_surface) const;
void generate_debug_mesh_lines(Vector<Vector3> &r_lines);
void generate_debug_mesh_indices(Vector<Vector3> &r_points);
@@ -362,4 +365,4 @@ public:
~PlaceholderMesh();
};
-#endif
+#endif // MESH_H
diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h
index 30ada5365f..0f8cc76173 100644
--- a/scene/resources/multimesh.h
+++ b/scene/resources/multimesh.h
@@ -113,4 +113,4 @@ public:
VARIANT_ENUM_CAST(MultiMesh::TransformFormat);
-#endif // MULTI_MESH_H
+#endif // MULTIMESH_H
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index a808ead66b..ac5493efdc 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -30,6 +30,10 @@
#include "navigation_mesh.h"
+#ifdef DEBUG_ENABLED
+#include "servers/navigation_server_3d.h"
+#endif
+
void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
ERR_FAIL_COND(p_mesh.is_null());
@@ -337,6 +341,7 @@ void NavigationMesh::clear_polygons() {
polygons.clear();
}
+#ifndef DISABLE_DEPRECATED
Ref<Mesh> NavigationMesh::get_debug_mesh() {
if (debug_mesh.is_valid()) {
return debug_mesh;
@@ -420,6 +425,102 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
return debug_mesh;
}
+#endif // DISABLE_DEPRECATED
+
+#ifdef DEBUG_ENABLED
+Ref<ArrayMesh> NavigationMesh::_get_debug_mesh() {
+ if (debug_mesh.is_valid()) {
+ // Blocks further updates for now, code below is intended for dynamic updates e.g. when settings change.
+ return debug_mesh;
+ }
+
+ if (!debug_mesh.is_valid()) {
+ debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ } else {
+ debug_mesh->clear_surfaces();
+ }
+
+ if (vertices.size() == 0) {
+ return debug_mesh;
+ }
+
+ int polygon_count = get_polygon_count();
+
+ if (polygon_count < 1) {
+ // no face, no play
+ return debug_mesh;
+ }
+
+ // build geometry face surface
+ Vector<Vector3> face_vertex_array;
+ face_vertex_array.resize(polygon_count * 3);
+
+ for (int i = 0; i < polygon_count; i++) {
+ Vector<int> polygon = get_polygon(i);
+
+ face_vertex_array.push_back(vertices[polygon[0]]);
+ face_vertex_array.push_back(vertices[polygon[1]]);
+ face_vertex_array.push_back(vertices[polygon[2]]);
+ }
+
+ Array face_mesh_array;
+ face_mesh_array.resize(Mesh::ARRAY_MAX);
+ face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
+
+ // if enabled add vertex colors to colorize each face individually
+ bool enabled_geometry_face_random_color = NavigationServer3D::get_singleton()->get_debug_navigation_enable_geometry_face_random_color();
+ if (enabled_geometry_face_random_color) {
+ Color debug_navigation_geometry_face_color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color();
+ Color polygon_color = debug_navigation_geometry_face_color;
+
+ Vector<Color> face_color_array;
+ face_color_array.resize(polygon_count * 3);
+
+ for (int i = 0; i < polygon_count; i++) {
+ polygon_color = debug_navigation_geometry_face_color * (Color(Math::randf(), Math::randf(), Math::randf()));
+
+ Vector<int> polygon = get_polygon(i);
+
+ face_color_array.push_back(polygon_color);
+ face_color_array.push_back(polygon_color);
+ face_color_array.push_back(polygon_color);
+ }
+ face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array;
+ }
+
+ debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
+ Ref<StandardMaterial3D> debug_geometry_face_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_material();
+ debug_mesh->surface_set_material(debug_mesh->get_surface_count(), debug_geometry_face_material);
+
+ // if enabled build geometry edge line surface
+ bool enabled_edge_lines = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_lines();
+
+ if (enabled_edge_lines) {
+ Vector<Vector3> line_vertex_array;
+ line_vertex_array.resize(polygon_count * 6);
+
+ for (int i = 0; i < polygon_count; i++) {
+ Vector<int> polygon = get_polygon(i);
+
+ line_vertex_array.push_back(vertices[polygon[0]]);
+ line_vertex_array.push_back(vertices[polygon[1]]);
+ line_vertex_array.push_back(vertices[polygon[1]]);
+ line_vertex_array.push_back(vertices[polygon[2]]);
+ line_vertex_array.push_back(vertices[polygon[2]]);
+ line_vertex_array.push_back(vertices[polygon[0]]);
+ }
+
+ Array line_mesh_array;
+ line_mesh_array.resize(Mesh::ARRAY_MAX);
+ line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
+ debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array);
+ Ref<StandardMaterial3D> debug_geometry_edge_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_material();
+ debug_mesh->surface_set_material(debug_mesh->get_surface_count(), debug_geometry_edge_material);
+ }
+
+ return debug_mesh;
+}
+#endif
void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationMesh::set_sample_partition_type);
diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h
index 40b275c792..79d8962d24 100644
--- a/scene/resources/navigation_mesh.h
+++ b/scene/resources/navigation_mesh.h
@@ -204,7 +204,11 @@ public:
Vector<int> get_polygon(int p_idx);
void clear_polygons();
+#ifndef DISABLE_DEPRECATED
Ref<Mesh> get_debug_mesh();
+#endif // DISABLE_DEPRECATED
+
+ Ref<ArrayMesh> _get_debug_mesh();
NavigationMesh();
};
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 2c58aa83a9..ac67e6e5e9 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -434,10 +434,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
for (int j = 0; j < binds.size(); j++) {
argptrs[j] = &binds[j];
}
- callable = callable.bind(argptrs, binds.size());
+ callable = callable.bindp(argptrs, binds.size());
}
- cfrom->connect(snames[c.signal], callable, varray(), CONNECT_PERSIST | c.flags);
+ cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags);
}
//Node *s = ret_nodes[0];
@@ -892,9 +892,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, HashMap<String
cd.signal = _nm_get_string(c.signal.get_name(), name_map);
cd.flags = c.flags;
cd.unbinds = unbinds;
- for (int i = 0; i < c.binds.size(); i++) { // TODO: This could be removed now.
- cd.binds.push_back(_vm_get_variant(c.binds[i], variant_map));
- }
+
for (int i = 0; i < binds.size(); i++) {
cd.binds.push_back(_vm_get_variant(binds[i], variant_map));
}
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index 5f8001c871..8e1a1d29b6 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -249,4 +249,4 @@ public:
VARIANT_ENUM_CAST(PackedScene::GenEditState)
-#endif // SCENE_PRELOADER_H
+#endif // PACKED_SCENE_H
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index cb93211756..ec124e379f 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -595,4 +595,5 @@ public:
};
VARIANT_ENUM_CAST(RibbonTrailMesh::Shape)
-#endif
+
+#endif // PRIMITIVE_MESHES_H
diff --git a/scene/resources/scene_replication_config.cpp b/scene/resources/scene_replication_config.cpp
deleted file mode 100644
index 6789f9f7d5..0000000000
--- a/scene/resources/scene_replication_config.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/*************************************************************************/
-/* scene_replication_config.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "scene_replication_config.h"
-
-#include "core/multiplayer/multiplayer_api.h"
-#include "scene/main/node.h"
-
-bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_value) {
- String name = p_name;
-
- if (name.begins_with("properties/")) {
- int idx = name.get_slicec('/', 1).to_int();
- String what = name.get_slicec('/', 2);
-
- if (properties.size() == idx && what == "path") {
- ERR_FAIL_COND_V(p_value.get_type() != Variant::NODE_PATH, false);
- NodePath path = p_value;
- ERR_FAIL_COND_V(path.is_empty() || path.get_subname_count() == 0, false);
- add_property(path);
- return true;
- }
- ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
- ERR_FAIL_INDEX_V(idx, properties.size(), false);
- ReplicationProperty &prop = properties[idx];
- if (what == "sync") {
- prop.sync = p_value;
- if (prop.sync) {
- sync_props.push_back(prop.name);
- } else {
- sync_props.erase(prop.name);
- }
- return true;
- } else if (what == "spawn") {
- prop.spawn = p_value;
- if (prop.spawn) {
- spawn_props.push_back(prop.name);
- } else {
- spawn_props.erase(prop.name);
- }
- return true;
- }
- }
- return false;
-}
-
-bool SceneReplicationConfig::_get(const StringName &p_name, Variant &r_ret) const {
- String name = p_name;
-
- if (name.begins_with("properties/")) {
- int idx = name.get_slicec('/', 1).to_int();
- String what = name.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(idx, properties.size(), false);
- const ReplicationProperty &prop = properties[idx];
- if (what == "path") {
- r_ret = prop.name;
- return true;
- } else if (what == "sync") {
- r_ret = prop.sync;
- return true;
- } else if (what == "spawn") {
- r_ret = prop.spawn;
- return true;
- }
- }
- return false;
-}
-
-void SceneReplicationConfig::_get_property_list(List<PropertyInfo> *p_list) const {
- for (int i = 0; i < properties.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/spawn", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
- }
-}
-
-TypedArray<NodePath> SceneReplicationConfig::get_properties() const {
- TypedArray<NodePath> paths;
- for (const ReplicationProperty &prop : properties) {
- paths.push_back(prop.name);
- }
- return paths;
-}
-
-void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) {
- ERR_FAIL_COND(properties.find(p_path));
-
- if (p_index < 0 || p_index == properties.size()) {
- properties.push_back(ReplicationProperty(p_path));
- return;
- }
-
- ERR_FAIL_INDEX(p_index, properties.size());
-
- List<ReplicationProperty>::Element *I = properties.front();
- int c = 0;
- while (c < p_index) {
- I = I->next();
- c++;
- }
- properties.insert_before(I, ReplicationProperty(p_path));
-}
-
-void SceneReplicationConfig::remove_property(const NodePath &p_path) {
- properties.erase(p_path);
-}
-
-bool SceneReplicationConfig::has_property(const NodePath &p_path) const {
- for (int i = 0; i < properties.size(); i++) {
- if (properties[i].name == p_path) {
- return true;
- }
- }
- return false;
-}
-
-int SceneReplicationConfig::property_get_index(const NodePath &p_path) const {
- for (int i = 0; i < properties.size(); i++) {
- if (properties[i].name == p_path) {
- return i;
- }
- }
- ERR_FAIL_V(-1);
-}
-
-bool SceneReplicationConfig::property_get_spawn(const NodePath &p_path) {
- List<ReplicationProperty>::Element *E = properties.find(p_path);
- ERR_FAIL_COND_V(!E, false);
- return E->get().spawn;
-}
-
-void SceneReplicationConfig::property_set_spawn(const NodePath &p_path, bool p_enabled) {
- List<ReplicationProperty>::Element *E = properties.find(p_path);
- ERR_FAIL_COND(!E);
- if (E->get().spawn == p_enabled) {
- return;
- }
- E->get().spawn = p_enabled;
- spawn_props.clear();
- for (const ReplicationProperty &prop : properties) {
- if (prop.spawn) {
- spawn_props.push_back(p_path);
- }
- }
-}
-
-bool SceneReplicationConfig::property_get_sync(const NodePath &p_path) {
- List<ReplicationProperty>::Element *E = properties.find(p_path);
- ERR_FAIL_COND_V(!E, false);
- return E->get().sync;
-}
-
-void SceneReplicationConfig::property_set_sync(const NodePath &p_path, bool p_enabled) {
- List<ReplicationProperty>::Element *E = properties.find(p_path);
- ERR_FAIL_COND(!E);
- if (E->get().sync == p_enabled) {
- return;
- }
- E->get().sync = p_enabled;
- sync_props.clear();
- for (const ReplicationProperty &prop : properties) {
- if (prop.sync) {
- sync_props.push_back(p_path);
- }
- }
-}
-
-void SceneReplicationConfig::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_properties"), &SceneReplicationConfig::get_properties);
- ClassDB::bind_method(D_METHOD("add_property", "path", "index"), &SceneReplicationConfig::add_property, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("has_property", "path"), &SceneReplicationConfig::has_property);
- ClassDB::bind_method(D_METHOD("remove_property", "path"), &SceneReplicationConfig::remove_property);
- ClassDB::bind_method(D_METHOD("property_get_index", "path"), &SceneReplicationConfig::property_get_index);
- ClassDB::bind_method(D_METHOD("property_get_spawn", "path"), &SceneReplicationConfig::property_get_spawn);
- ClassDB::bind_method(D_METHOD("property_set_spawn", "path", "enabled"), &SceneReplicationConfig::property_set_spawn);
- ClassDB::bind_method(D_METHOD("property_get_sync", "path"), &SceneReplicationConfig::property_get_sync);
- ClassDB::bind_method(D_METHOD("property_set_sync", "path", "enabled"), &SceneReplicationConfig::property_set_sync);
-}
diff --git a/scene/resources/scene_replication_config.h b/scene/resources/scene_replication_config.h
deleted file mode 100644
index ab3658d2a7..0000000000
--- a/scene/resources/scene_replication_config.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************/
-/* scene_replication_config.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef SCENE_REPLICATION_CONFIG_H
-#define SCENE_REPLICATION_CONFIG_H
-
-#include "core/io/resource.h"
-
-#include "core/variant/typed_array.h"
-
-class SceneReplicationConfig : public Resource {
- GDCLASS(SceneReplicationConfig, Resource);
- OBJ_SAVE_TYPE(SceneReplicationConfig);
- RES_BASE_EXTENSION("repl");
-
-private:
- struct ReplicationProperty {
- NodePath name;
- bool spawn = true;
- bool sync = true;
-
- bool operator==(const ReplicationProperty &p_to) {
- return name == p_to.name;
- }
-
- ReplicationProperty() {}
-
- ReplicationProperty(const NodePath &p_name) {
- name = p_name;
- }
- };
-
- List<ReplicationProperty> properties;
- List<NodePath> spawn_props;
- List<NodePath> sync_props;
-
-protected:
- static void _bind_methods();
-
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
-public:
- TypedArray<NodePath> get_properties() const;
-
- void add_property(const NodePath &p_path, int p_index = -1);
- void remove_property(const NodePath &p_path);
- bool has_property(const NodePath &p_path) const;
-
- int property_get_index(const NodePath &p_path) const;
- bool property_get_spawn(const NodePath &p_path);
- void property_set_spawn(const NodePath &p_path, bool p_enabled);
-
- bool property_get_sync(const NodePath &p_path);
- void property_set_sync(const NodePath &p_path, bool p_enabled);
-
- const List<NodePath> &get_spawn_properties() { return spawn_props; }
- const List<NodePath> &get_sync_properties() { return sync_props; }
-
- SceneReplicationConfig() {}
-};
-
-#endif // SCENE_REPLICATION_CONFIG_H
diff --git a/scene/resources/separation_ray_shape_2d.cpp b/scene/resources/separation_ray_shape_2d.cpp
index 0d6aee47d8..fea41061fd 100644
--- a/scene/resources/separation_ray_shape_2d.cpp
+++ b/scene/resources/separation_ray_shape_2d.cpp
@@ -57,7 +57,7 @@ void SeparationRayShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Transform2D xf;
xf.rotate(target_position.angle());
- xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
+ xf.translate_local(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
Vector<Vector2> pts = {
xf.xform(Vector2(arrow_size, 0)),
diff --git a/scene/resources/separation_ray_shape_3d.h b/scene/resources/separation_ray_shape_3d.h
index 0e750a48e6..ae08ff7887 100644
--- a/scene/resources/separation_ray_shape_3d.h
+++ b/scene/resources/separation_ray_shape_3d.h
@@ -28,8 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SEPARATION_RAY_SHAPE_H
-#define SEPARATION_RAY_SHAPE_H
+#ifndef SEPARATION_RAY_SHAPE_3D_H
+#define SEPARATION_RAY_SHAPE_3D_H
+
#include "scene/resources/shape_3d.h"
class SeparationRayShape3D : public Shape3D {
@@ -53,4 +54,5 @@ public:
SeparationRayShape3D();
};
-#endif // SEPARATION_RAY_SHAPE_H
+
+#endif // SEPARATION_RAY_SHAPE_3D_H
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 16117986fe..74031e02d7 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -103,7 +103,7 @@ String Shader::get_code() const {
return code;
}
-void Shader::get_param_list(List<PropertyInfo> *p_params) const {
+void Shader::get_param_list(List<PropertyInfo> *p_params, bool p_get_groups) const {
_update_shader();
List<PropertyInfo> local;
@@ -112,12 +112,16 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
params_cache_dirty = false;
for (PropertyInfo &pi : local) {
- if (default_textures.has(pi.name)) { //do not show default textures
+ bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP;
+ if (!p_get_groups && is_group) {
continue;
}
- String original_name = pi.name;
- pi.name = "shader_param/" + pi.name;
- params_cache[pi.name] = original_name;
+ if (!is_group) {
+ if (default_textures.has(pi.name)) { //do not show default textures
+ continue;
+ }
+ params_cache[pi.name] = pi.name;
+ }
if (p_params) {
//small little hack
if (pi.type == Variant::RID) {
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 5de8ad5518..7aa14651a5 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -78,7 +78,7 @@ public:
void set_code(const String &p_code);
String get_code() const;
- void get_param_list(List<PropertyInfo> *p_params) const;
+ void get_param_list(List<PropertyInfo> *p_params, bool p_get_groups = false) const;
bool has_param(const StringName &p_param) const;
void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index = 0);
diff --git a/scene/resources/shape_3d.h b/scene/resources/shape_3d.h
index 77e79a269d..58fe723d89 100644
--- a/scene/resources/shape_3d.h
+++ b/scene/resources/shape_3d.h
@@ -73,4 +73,4 @@ public:
~Shape3D();
};
-#endif // SHAPE_H
+#endif // SHAPE_3D_H
diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h
index 3b9235ffd8..4c49119df0 100644
--- a/scene/resources/skeleton_modification_2d.h
+++ b/scene/resources/skeleton_modification_2d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION2D_H
-#define SKELETONMODIFICATION2D_H
+#ifndef SKELETON_MODIFICATION_2D_H
+#define SKELETON_MODIFICATION_2D_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_stack_2d.h"
@@ -86,4 +86,4 @@ public:
SkeletonModification2D();
};
-#endif // SKELETONMODIFICATION2D_H
+#endif // SKELETON_MODIFICATION_2D_H
diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/resources/skeleton_modification_2d_ccdik.h
index 31485fa31f..e49dfca5b5 100644
--- a/scene/resources/skeleton_modification_2d_ccdik.h
+++ b/scene/resources/skeleton_modification_2d_ccdik.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION2DCCDIK_H
-#define SKELETONMODIFICATION2DCCDIK_H
+#ifndef SKELETON_MODIFICATION_2D_CCDIK_H
+#define SKELETON_MODIFICATION_2D_CCDIK_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_2d.h"
@@ -113,4 +113,4 @@ public:
~SkeletonModification2DCCDIK();
};
-#endif // SKELETONMODIFICATION2DCCDIK_H
+#endif // SKELETON_MODIFICATION_2D_CCDIK_H
diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h
index d5077084ef..4a875d039f 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.h
+++ b/scene/resources/skeleton_modification_2d_fabrik.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION2DFABRIK_H
-#define SKELETONMODIFICATION2DFABRIK_H
+#ifndef SKELETON_MODIFICATION_2D_FABRIK_H
+#define SKELETON_MODIFICATION_2D_FABRIK_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_2d.h"
@@ -105,4 +105,4 @@ public:
~SkeletonModification2DFABRIK();
};
-#endif // SKELETONMODIFICATION2DFABRIK_H
+#endif // SKELETON_MODIFICATION_2D_FABRIK_H
diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h
index a1abca6564..b9080eafa9 100644
--- a/scene/resources/skeleton_modification_2d_jiggle.h
+++ b/scene/resources/skeleton_modification_2d_jiggle.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION2DJIGGLE_H
-#define SKELETONMODIFICATION2DJIGGLE_H
+#ifndef SKELETON_MODIFICATION_2D_JIGGLE_H
+#define SKELETON_MODIFICATION_2D_JIGGLE_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_2d.h"
@@ -136,4 +136,4 @@ public:
~SkeletonModification2DJiggle();
};
-#endif // SKELETONMODIFICATION2DJIGGLE_H
+#endif // SKELETON_MODIFICATION_2D_JIGGLE_H
diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h
index ff91b92e7d..e4d7afa605 100644
--- a/scene/resources/skeleton_modification_2d_lookat.h
+++ b/scene/resources/skeleton_modification_2d_lookat.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION2DLOOKAT_H
-#define SKELETONMODIFICATION2DLOOKAT_H
+#ifndef SKELETON_MODIFICATION_2D_LOOKAT_H
+#define SKELETON_MODIFICATION_2D_LOOKAT_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_2d.h"
@@ -97,4 +97,4 @@ public:
~SkeletonModification2DLookAt();
};
-#endif // SKELETONMODIFICATION2DLOOKAT_H
+#endif // SKELETON_MODIFICATION_2D_LOOKAT_H
diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h
index 373ff666ee..55482b2cca 100644
--- a/scene/resources/skeleton_modification_2d_physicalbones.h
+++ b/scene/resources/skeleton_modification_2d_physicalbones.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION2DPHYSICALBONES_H
-#define SKELETONMODIFICATION2DPHYSICALBONES_H
+#ifndef SKELETON_MODIFICATION_2D_PHYSICALBONES_H
+#define SKELETON_MODIFICATION_2D_PHYSICALBONES_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_2d.h"
@@ -79,4 +79,4 @@ public:
~SkeletonModification2DPhysicalBones();
};
-#endif // SKELETONMODIFICATION2DPHYSICALBONES_H
+#endif // SKELETON_MODIFICATION_2D_PHYSICALBONES_H
diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h
index 99d9f6f381..4da9fcc1f6 100644
--- a/scene/resources/skeleton_modification_2d_stackholder.h
+++ b/scene/resources/skeleton_modification_2d_stackholder.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION2DSTACKHOLDER_H
-#define SKELETONMODIFICATION2DSTACKHOLDER_H
+#ifndef SKELETON_MODIFICATION_2D_STACKHOLDER_H
+#define SKELETON_MODIFICATION_2D_STACKHOLDER_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_2d.h"
@@ -61,4 +61,4 @@ public:
~SkeletonModification2DStackHolder();
};
-#endif // SKELETONMODIFICATION2DSTACKHOLDER_H
+#endif // SKELETON_MODIFICATION_2D_STACKHOLDER_H
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h
index fc14d35f70..e1dbb6cfda 100644
--- a/scene/resources/skeleton_modification_2d_twoboneik.h
+++ b/scene/resources/skeleton_modification_2d_twoboneik.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION2DTWOBONEIK_H
-#define SKELETONMODIFICATION2DTWOBONEIK_H
+#ifndef SKELETON_MODIFICATION_2D_TWOBONEIK_H
+#define SKELETON_MODIFICATION_2D_TWOBONEIK_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_2d.h"
@@ -104,4 +104,4 @@ public:
~SkeletonModification2DTwoBoneIK();
};
-#endif // SKELETONMODIFICATION2DTWOBONEIK_H
+#endif // SKELETON_MODIFICATION_2D_TWOBONEIK_H
diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h
index ab736fcbd2..6daf5efcd9 100644
--- a/scene/resources/skeleton_modification_3d.h
+++ b/scene/resources/skeleton_modification_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATION3D_H
-#define SKELETONMODIFICATION3D_H
+#ifndef SKELETON_MODIFICATION_3D_H
+#define SKELETON_MODIFICATION_3D_H
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/skeleton_modification_stack_3d.h"
@@ -76,4 +76,4 @@ public:
SkeletonModification3D();
};
-#endif // SKELETONMODIFICATION3D_H
+#endif // SKELETON_MODIFICATION_3D_H
diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/resources/skeleton_modification_3d_ccdik.h
index 873ab8aa5a..7098794038 100644
--- a/scene/resources/skeleton_modification_3d_ccdik.h
+++ b/scene/resources/skeleton_modification_3d_ccdik.h
@@ -32,8 +32,8 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
-#ifndef SKELETONMODIFICATION3DCCDIK_H
-#define SKELETONMODIFICATION3DCCDIK_H
+#ifndef SKELETON_MODIFICATION_3D_CCDIK_H
+#define SKELETON_MODIFICATION_3D_CCDIK_H
class SkeletonModification3DCCDIK : public SkeletonModification3D {
GDCLASS(SkeletonModification3DCCDIK, SkeletonModification3D);
@@ -111,4 +111,4 @@ public:
~SkeletonModification3DCCDIK();
};
-#endif //SKELETONMODIFICATION3DCCDIK_H
+#endif // SKELETON_MODIFICATION_3D_CCDIK_H
diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h
index cc4d3a5e20..3d66bb6d99 100644
--- a/scene/resources/skeleton_modification_3d_fabrik.h
+++ b/scene/resources/skeleton_modification_3d_fabrik.h
@@ -32,8 +32,8 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
-#ifndef SKELETONMODIFICATION3DFABRIK_H
-#define SKELETONMODIFICATION3DFABRIK_H
+#ifndef SKELETON_MODIFICATION_3D_FABRIK_H
+#define SKELETON_MODIFICATION_3D_FABRIK_H
class SkeletonModification3DFABRIK : public SkeletonModification3D {
GDCLASS(SkeletonModification3DFABRIK, SkeletonModification3D);
@@ -121,4 +121,4 @@ public:
~SkeletonModification3DFABRIK();
};
-#endif //SKELETONMODIFICATION3DFABRIK_H
+#endif // SKELETON_MODIFICATION_3D_FABRIK_H
diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/resources/skeleton_modification_3d_jiggle.h
index 7ec5ed4f11..f41ffcd58d 100644
--- a/scene/resources/skeleton_modification_3d_jiggle.h
+++ b/scene/resources/skeleton_modification_3d_jiggle.h
@@ -32,8 +32,8 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
-#ifndef SKELETONMODIFICATION3DJIGGLE_H
-#define SKELETONMODIFICATION3DJIGGLE_H
+#ifndef SKELETON_MODIFICATION_3D_JIGGLE_H
+#define SKELETON_MODIFICATION_3D_JIGGLE_H
class SkeletonModification3DJiggle : public SkeletonModification3D {
GDCLASS(SkeletonModification3DJiggle, SkeletonModification3D);
@@ -135,4 +135,4 @@ public:
~SkeletonModification3DJiggle();
};
-#endif //SKELETONMODIFICATION3DJIGGLE_H
+#endif // SKELETON_MODIFICATION_3D_JIGGLE_H
diff --git a/scene/resources/skeleton_modification_3d_lookat.h b/scene/resources/skeleton_modification_3d_lookat.h
index 9f5120a0fd..4e5714b5dc 100644
--- a/scene/resources/skeleton_modification_3d_lookat.h
+++ b/scene/resources/skeleton_modification_3d_lookat.h
@@ -31,8 +31,8 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
-#ifndef SKELETONMODIFICATION3DLOOKAT_H
-#define SKELETONMODIFICATION3DLOOKAT_H
+#ifndef SKELETON_MODIFICATION_3D_LOOKAT_H
+#define SKELETON_MODIFICATION_3D_LOOKAT_H
class SkeletonModification3DLookAt : public SkeletonModification3D {
GDCLASS(SkeletonModification3DLookAt, SkeletonModification3D);
@@ -86,4 +86,4 @@ public:
~SkeletonModification3DLookAt();
};
-#endif //SKELETONMODIFICATION3DLOOKAT_H
+#endif // SKELETON_MODIFICATION_3D_LOOKAT_H
diff --git a/scene/resources/skeleton_modification_3d_stackholder.h b/scene/resources/skeleton_modification_3d_stackholder.h
index 5780d7d43f..ae22099158 100644
--- a/scene/resources/skeleton_modification_3d_stackholder.h
+++ b/scene/resources/skeleton_modification_3d_stackholder.h
@@ -31,8 +31,8 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
-#ifndef SKELETONMODIFICATION3DSTACKHOLDER_H
-#define SKELETONMODIFICATION3DSTACKHOLDER_H
+#ifndef SKELETON_MODIFICATION_3D_STACKHOLDER_H
+#define SKELETON_MODIFICATION_3D_STACKHOLDER_H
class SkeletonModification3DStackHolder : public SkeletonModification3D {
GDCLASS(SkeletonModification3DStackHolder, SkeletonModification3D);
@@ -56,4 +56,4 @@ public:
~SkeletonModification3DStackHolder();
};
-#endif //SKELETONMODIFICATION3DSTACKHOLDER_H
+#endif // SKELETON_MODIFICATION_3D_STACKHOLDER_H
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/resources/skeleton_modification_3d_twoboneik.h
index a4ddc6cee7..57e8237511 100644
--- a/scene/resources/skeleton_modification_3d_twoboneik.h
+++ b/scene/resources/skeleton_modification_3d_twoboneik.h
@@ -31,8 +31,8 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
-#ifndef SKELETONMODIFICATION3DTWOBONEIK_H
-#define SKELETONMODIFICATION3DTWOBONEIK_H
+#ifndef SKELETON_MODIFICATION_3D_TWOBONEIK_H
+#define SKELETON_MODIFICATION_3D_TWOBONEIK_H
class SkeletonModification3DTwoBoneIK : public SkeletonModification3D {
GDCLASS(SkeletonModification3DTwoBoneIK, SkeletonModification3D);
@@ -115,4 +115,4 @@ public:
~SkeletonModification3DTwoBoneIK();
};
-#endif //SKELETONMODIFICATION3DTWOBONEIK_H
+#endif // SKELETON_MODIFICATION_3D_TWOBONEIK_H
diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h
index 7b5f8e0322..ceffd71368 100644
--- a/scene/resources/skeleton_modification_stack_2d.h
+++ b/scene/resources/skeleton_modification_stack_2d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATIONSTACK2D_H
-#define SKELETONMODIFICATIONSTACK2D_H
+#ifndef SKELETON_MODIFICATION_STACK_2D_H
+#define SKELETON_MODIFICATION_STACK_2D_H
#include "scene/2d/skeleton_2d.h"
#include "scene/resources/skeleton_modification_2d.h"
@@ -96,4 +96,4 @@ public:
SkeletonModificationStack2D();
};
-#endif // SKELETONMODIFICATION2D_H
+#endif // SKELETON_MODIFICATION_STACK_2D_H
diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h
index 2c17fba2c5..4ff1bd1e33 100644
--- a/scene/resources/skeleton_modification_stack_3d.h
+++ b/scene/resources/skeleton_modification_stack_3d.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SKELETONMODIFICATIONSTACK3D_H
-#define SKELETONMODIFICATIONSTACK3D_H
+#ifndef SKELETON_MODIFICATION_STACK_3D_H
+#define SKELETON_MODIFICATION_STACK_3D_H
#include "core/templates/local_vector.h"
#include "scene/3d/skeleton_3d.h"
@@ -88,4 +88,4 @@ public:
SkeletonModificationStack3D();
};
-#endif // SKELETONMODIFICATIONSTACK3D_H
+#endif // SKELETON_MODIFICATION_STACK_3D_H
diff --git a/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp
index 0714de470c..bfb4bb6e2b 100644
--- a/scene/resources/skeleton_profile.cpp
+++ b/scene/resources/skeleton_profile.cpp
@@ -123,12 +123,20 @@ bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const {
void SkeletonProfile::_validate_property(PropertyInfo &property) const {
if (is_read_only) {
- if (property.name == ("group_size") || property.name == ("bone_size")) {
+ if (property.name == ("group_size") || property.name == ("bone_size") || property.name == ("root_bone") || property.name == ("scale_base_bone")) {
property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
}
+ if (property.name == ("root_bone") || property.name == ("scale_base_bone")) {
+ String hint = "";
+ for (int i = 0; i < bones.size(); i++) {
+ hint += i == 0 ? String(bones[i].bone_name) : "," + String(bones[i].bone_name);
+ }
+ property.hint_string = hint;
+ }
+
PackedStringArray split = property.name.split("/");
if (split.size() == 3 && split[0] == "bones") {
if (split[2] == "bone_tail" && get_tail_direction(split[1].to_int()) != TAIL_DIRECTION_SPECIFIC_CHILD) {
@@ -168,6 +176,28 @@ void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
+StringName SkeletonProfile::get_root_bone() {
+ return root_bone;
+}
+
+void SkeletonProfile::set_root_bone(StringName p_bone_name) {
+ if (is_read_only) {
+ return;
+ }
+ root_bone = p_bone_name;
+}
+
+StringName SkeletonProfile::get_scale_base_bone() {
+ return scale_base_bone;
+}
+
+void SkeletonProfile::set_scale_base_bone(StringName p_bone_name) {
+ if (is_read_only) {
+ return;
+ }
+ scale_base_bone = p_bone_name;
+}
+
int SkeletonProfile::get_group_size() {
return groups.size();
}
@@ -361,6 +391,12 @@ bool SkeletonProfile::has_bone(StringName p_bone_name) {
}
void SkeletonProfile::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_root_bone", "bone_name"), &SkeletonProfile::set_root_bone);
+ ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonProfile::get_root_bone);
+
+ ClassDB::bind_method(D_METHOD("set_scale_base_bone", "bone_name"), &SkeletonProfile::set_scale_base_bone);
+ ClassDB::bind_method(D_METHOD("get_scale_base_bone"), &SkeletonProfile::get_scale_base_bone);
+
ClassDB::bind_method(D_METHOD("set_group_size", "size"), &SkeletonProfile::set_group_size);
ClassDB::bind_method(D_METHOD("get_group_size"), &SkeletonProfile::get_group_size);
@@ -396,6 +432,9 @@ void SkeletonProfile::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_group", "bone_idx"), &SkeletonProfile::get_group);
ClassDB::bind_method(D_METHOD("set_group", "bone_idx", "group"), &SkeletonProfile::set_group);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_root_bone", "get_root_bone");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "scale_base_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_scale_base_bone", "get_scale_base_bone");
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "group_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Groups,groups/"), "set_group_size", "get_group_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Bones,bones/"), "set_bone_size", "get_bone_size");
@@ -415,6 +454,9 @@ SkeletonProfile::~SkeletonProfile() {
SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
is_read_only = true;
+ root_bone = "Root";
+ scale_base_bone = "Hips";
+
groups.resize(4);
groups.write[0].group_name = "Body";
diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h
index d305311538..84dfca458e 100644
--- a/scene/resources/skeleton_profile.h
+++ b/scene/resources/skeleton_profile.h
@@ -64,6 +64,9 @@ protected:
bool require = false;
};
+ StringName root_bone;
+ StringName scale_base_bone;
+
Vector<SkeletonProfileGroup> groups;
Vector<SkeletonProfileBone> bones;
@@ -74,6 +77,12 @@ protected:
static void _bind_methods();
public:
+ StringName get_root_bone();
+ void set_root_bone(StringName p_bone_name);
+
+ StringName get_scale_base_bone();
+ void set_scale_base_bone(StringName p_bone_name);
+
int get_group_size();
void set_group_size(int p_size);
diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h
index 5be8922ba4..61999af3c4 100644
--- a/scene/resources/sky_material.h
+++ b/scene/resources/sky_material.h
@@ -219,4 +219,4 @@ public:
~PhysicalSkyMaterial();
};
-#endif /* !SKY_MATERIAL_H */
+#endif // SKY_MATERIAL_H
diff --git a/scene/resources/sphere_shape_3d.h b/scene/resources/sphere_shape_3d.h
index 8f77378ef4..91d72e01ec 100644
--- a/scene/resources/sphere_shape_3d.h
+++ b/scene/resources/sphere_shape_3d.h
@@ -52,4 +52,4 @@ public:
SphereShape3D();
};
-#endif // SPHERE_SHAPE_H
+#endif // SPHERE_SHAPE_3D_H
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index 3b3654775f..9f4f69d3ba 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -264,4 +264,4 @@ public:
~StyleBoxLine();
};
-#endif
+#endif // STYLE_BOX_H
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 2d399ca3bf..6735d6623f 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -207,4 +207,4 @@ public:
VARIANT_ENUM_CAST(SurfaceTool::CustomFormat)
VARIANT_ENUM_CAST(SurfaceTool::SkinWeightCount)
-#endif
+#endif // SURFACE_TOOL_H
diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h
index 1243a9dbf7..69131ffbee 100644
--- a/scene/resources/syntax_highlighter.h
+++ b/scene/resources/syntax_highlighter.h
@@ -142,4 +142,4 @@ public:
Color get_member_variable_color() const;
};
-#endif
+#endif // SYNTAX_HIGHLIGHTER_H
diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h
index 0c8cf855f0..168e4f5879 100644
--- a/scene/resources/text_file.h
+++ b/scene/resources/text_file.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TEXTFILE_H
-#define TEXTFILE_H
+#ifndef TEXT_FILE_H
+#define TEXT_FILE_H
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
@@ -51,4 +51,4 @@ public:
Error load_text(const String &p_path);
};
-#endif // TEXTFILE_H
+#endif // TEXT_FILE_H
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 0aefe34f7d..05ed9238b8 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -2009,7 +2009,7 @@ void CurveXYZTexture::set_curve_x(Ref<Curve> p_curve) {
}
_curve_x = p_curve;
if (_curve_x.is_valid()) {
- _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED);
+ _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
}
_update();
}
@@ -2022,7 +2022,7 @@ void CurveXYZTexture::set_curve_y(Ref<Curve> p_curve) {
}
_curve_y = p_curve;
if (_curve_y.is_valid()) {
- _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED);
+ _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
}
_update();
}
@@ -2035,7 +2035,7 @@ void CurveXYZTexture::set_curve_z(Ref<Curve> p_curve) {
}
_curve_z = p_curve;
if (_curve_z.is_valid()) {
- _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED);
+ _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
}
_update();
}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index b107a2a70d..36b193c5d4 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -1106,4 +1106,4 @@ public:
PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
};
-#endif
+#endif // TEXTURE_H
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 39b77568cf..3f6eec8497 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -295,7 +295,7 @@ void Theme::set_default_font(const Ref<Font> &p_default_font) {
default_font = p_default_font;
if (default_font.is_valid()) {
- default_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
+ default_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed();
@@ -341,7 +341,7 @@ void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, c
icon_map[p_theme_type][p_name] = p_icon;
if (p_icon.is_valid()) {
- icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
+ icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed(!existing);
@@ -451,7 +451,7 @@ void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_typ
style_map[p_theme_type][p_name] = p_style;
if (p_style.is_valid()) {
- style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
+ style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed(!existing);
@@ -561,7 +561,7 @@ void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, c
font_map[p_theme_type][p_name] = p_font;
if (p_font.is_valid()) {
- font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
+ font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed(!existing);
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 87d7d2fdea..a2aca5e61f 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -256,4 +256,4 @@ public:
VARIANT_ENUM_CAST(Theme::DataType);
-#endif
+#endif // THEME_H
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 5b250f48cc..b0b9f1228f 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -2467,9 +2467,42 @@ Vector<Point2> TileSet::_get_half_offset_side_terrain_peering_bit_polygon(Vector
}
void TileSet::reset_state() {
+ // Rendering
occlusion_layers.clear();
+ tile_lines_mesh.instantiate();
+ tile_filled_mesh.instantiate();
+ tile_meshes_dirty = true;
+
+ // Physics
physics_layers.clear();
+
+ // Terrains
+ terrain_sets.clear();
+ terrain_meshes.clear();
+ terrain_peering_bits_meshes.clear();
+ per_terrain_pattern_tiles.clear();
+ terrains_cache_dirty = true;
+
+ // Navigation
+ navigation_layers.clear();
+
custom_data_layers.clear();
+ custom_data_layers_by_name.clear();
+
+ // Proxies
+ source_level_proxies.clear();
+ coords_level_proxies.clear();
+ alternative_level_proxies.clear();
+
+#ifndef DISABLE_DEPRECATED
+ for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
+ memdelete(E.value);
+ }
+ compatibility_data.clear();
+#endif // DISABLE_DEPRECATED
+ while (!source_ids.is_empty()) {
+ remove_source(source_ids[0]);
+ }
}
const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1);
@@ -3462,6 +3495,10 @@ void TileSetSource::set_tile_set(const TileSet *p_tile_set) {
tile_set = p_tile_set;
}
+void TileSetSource::reset_state() {
+ tile_set = nullptr;
+};
+
void TileSetSource::_bind_methods() {
// Base tiles
ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetSource::get_tiles_count);
@@ -3645,12 +3682,17 @@ void TileSetAtlasSource::remove_custom_data_layer(int p_index) {
}
void TileSetAtlasSource::reset_state() {
- // Reset all TileData.
+ tile_set = nullptr;
+
for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
- for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
- E_alternative.value->reset_state();
+ for (const KeyValue<int, TileData *> &E_tile_data : E_tile.value.alternatives) {
+ memdelete(E_tile_data.value);
}
}
+ _coords_mapping_cache.clear();
+ tiles.clear();
+ tiles_ids.clear();
+ _queue_update_padded_texture();
}
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
@@ -4980,13 +5022,6 @@ void TileData::remove_custom_data_layer(int p_index) {
custom_data.remove_at(p_index);
}
-void TileData::reset_state() {
- occluders.clear();
- physics.clear();
- navigation.clear();
- custom_data.clear();
-}
-
void TileData::set_allow_transform(bool p_allow_transform) {
allow_transform = p_allow_transform;
}
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index a97ae94ccc..6ea3889fce 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -569,7 +569,7 @@ public:
virtual void add_custom_data_layer(int p_index){};
virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
virtual void remove_custom_data_layer(int p_index){};
- virtual void reset_state() override{};
+ virtual void reset_state() override;
// Tiles.
virtual int get_tiles_count() const = 0;
@@ -847,7 +847,6 @@ public:
void add_custom_data_layer(int p_index);
void move_custom_data_layer(int p_from_index, int p_to_pos);
void remove_custom_data_layer(int p_index);
- void reset_state();
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;
diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h
index e368ffc030..35686b293c 100644
--- a/scene/resources/video_stream.h
+++ b/scene/resources/video_stream.h
@@ -75,4 +75,4 @@ public:
virtual Ref<VideoStreamPlayback> instantiate_playback() = 0;
};
-#endif
+#endif // VIDEO_STREAM_H
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index b68cce9dda..5e0627a7d9 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -967,6 +967,12 @@ void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from
ERR_FAIL_COND(!g->nodes.has(p_to_node));
ERR_FAIL_INDEX(p_to_port, g->nodes[p_to_node].node->get_input_port_count());
+ for (const Connection &E : g->connections) {
+ if (E.from_node == p_from_node && E.from_port == p_from_port && E.to_node == p_to_node && E.to_port == p_to_port) {
+ return;
+ }
+ }
+
Connection c;
c.from_node = p_from_node;
c.from_port = p_from_port;
@@ -3745,7 +3751,7 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
}
return vformat(RTR("This uniform type does not support the '%s' qualifier."), qualifier_str);
} else if (qualifier == Qualifier::QUAL_GLOBAL) {
- RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(uniform_name);
+ RS::GlobalShaderUniformType gvt = RS::get_singleton()->global_shader_uniform_get_type(uniform_name);
if (gvt == RS::GLOBAL_VAR_TYPE_MAX) {
return vformat(RTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name);
}
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index 05a059373b..64acb6d18f 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -352,4 +352,4 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeParticleEmit::EmitFlags)
-#endif
+#endif // VISUAL_SHADER_PARTICLE_NODES_H
diff --git a/scene/resources/world_boundary_shape_3d.h b/scene/resources/world_boundary_shape_3d.h
index 5378bc52c7..5c34767106 100644
--- a/scene/resources/world_boundary_shape_3d.h
+++ b/scene/resources/world_boundary_shape_3d.h
@@ -53,4 +53,5 @@ public:
WorldBoundaryShape3D();
};
-#endif // WORLD_BOUNDARY_SHAPE_H
+
+#endif // WORLD_BOUNDARY_SHAPE_3D_H