summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite_2d.cpp54
-rw-r--r--scene/2d/animated_sprite_2d.h10
-rw-r--r--scene/2d/area_2d.cpp108
-rw-r--r--scene/2d/area_2d.h10
-rw-r--r--scene/2d/audio_listener_2d.cpp5
-rw-r--r--scene/2d/audio_listener_2d.h4
-rw-r--r--scene/2d/audio_stream_player_2d.cpp119
-rw-r--r--scene/2d/audio_stream_player_2d.h4
-rw-r--r--scene/2d/back_buffer_copy.cpp4
-rw-r--r--scene/2d/back_buffer_copy.h4
-rw-r--r--scene/2d/camera_2d.cpp52
-rw-r--r--scene/2d/camera_2d.h5
-rw-r--r--scene/2d/canvas_group.cpp4
-rw-r--r--scene/2d/canvas_group.h4
-rw-r--r--scene/2d/canvas_modulate.cpp49
-rw-r--r--scene/2d/canvas_modulate.h4
-rw-r--r--scene/2d/collision_object_2d.cpp15
-rw-r--r--scene/2d/collision_object_2d.h8
-rw-r--r--scene/2d/collision_polygon_2d.cpp45
-rw-r--r--scene/2d/collision_polygon_2d.h4
-rw-r--r--scene/2d/collision_shape_2d.cpp45
-rw-r--r--scene/2d/collision_shape_2d.h4
-rw-r--r--scene/2d/cpu_particles_2d.cpp123
-rw-r--r--scene/2d/cpu_particles_2d.h12
-rw-r--r--scene/2d/gpu_particles_2d.cpp340
-rw-r--r--scene/2d/gpu_particles_2d.h53
-rw-r--r--scene/2d/joint_2d.cpp32
-rw-r--r--scene/2d/joint_2d.h4
-rw-r--r--scene/2d/light_2d.cpp39
-rw-r--r--scene/2d/light_2d.h4
-rw-r--r--scene/2d/light_occluder_2d.cpp77
-rw-r--r--scene/2d/light_occluder_2d.h9
-rw-r--r--scene/2d/line_2d.cpp25
-rw-r--r--scene/2d/line_2d.h4
-rw-r--r--scene/2d/line_builder.cpp4
-rw-r--r--scene/2d/line_builder.h4
-rw-r--r--scene/2d/mesh_instance_2d.cpp15
-rw-r--r--scene/2d/mesh_instance_2d.h4
-rw-r--r--scene/2d/multimesh_instance_2d.cpp15
-rw-r--r--scene/2d/multimesh_instance_2d.h4
-rw-r--r--scene/2d/navigation_agent_2d.cpp30
-rw-r--r--scene/2d/navigation_agent_2d.h19
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp35
-rw-r--r--scene/2d/navigation_obstacle_2d.h4
-rw-r--r--scene/2d/navigation_region_2d.cpp42
-rw-r--r--scene/2d/navigation_region_2d.h10
-rw-r--r--scene/2d/node_2d.cpp95
-rw-r--r--scene/2d/node_2d.h12
-rw-r--r--scene/2d/parallax_background.cpp15
-rw-r--r--scene/2d/parallax_background.h4
-rw-r--r--scene/2d/parallax_layer.cpp7
-rw-r--r--scene/2d/parallax_layer.h4
-rw-r--r--scene/2d/path_2d.cpp53
-rw-r--r--scene/2d/path_2d.h5
-rw-r--r--scene/2d/physical_bone_2d.cpp10
-rw-r--r--scene/2d/physical_bone_2d.h6
-rw-r--r--scene/2d/physics_body_2d.cpp281
-rw-r--r--scene/2d/physics_body_2d.h48
-rw-r--r--scene/2d/polygon_2d.cpp21
-rw-r--r--scene/2d/polygon_2d.h4
-rw-r--r--scene/2d/position_2d.cpp73
-rw-r--r--scene/2d/position_2d.h6
-rw-r--r--scene/2d/ray_cast_2d.cpp51
-rw-r--r--scene/2d/ray_cast_2d.h12
-rw-r--r--scene/2d/remote_transform_2d.cpp9
-rw-r--r--scene/2d/remote_transform_2d.h4
-rw-r--r--scene/2d/shape_cast_2d.cpp46
-rw-r--r--scene/2d/shape_cast_2d.h13
-rw-r--r--scene/2d/skeleton_2d.cpp382
-rw-r--r--scene/2d/skeleton_2d.h4
-rw-r--r--scene/2d/sprite_2d.cpp9
-rw-r--r--scene/2d/sprite_2d.h4
-rw-r--r--scene/2d/tile_map.cpp268
-rw-r--r--scene/2d/tile_map.h45
-rw-r--r--scene/2d/touch_screen_button.cpp62
-rw-r--r--scene/2d/touch_screen_button.h10
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.cpp37
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.h6
-rw-r--r--scene/3d/area_3d.cpp122
-rw-r--r--scene/3d/area_3d.h12
-rw-r--r--scene/3d/audio_listener_3d.cpp7
-rw-r--r--scene/3d/audio_listener_3d.h4
-rw-r--r--scene/3d/audio_stream_player_3d.cpp146
-rw-r--r--scene/3d/audio_stream_player_3d.h4
-rw-r--r--scene/3d/bone_attachment_3d.cpp15
-rw-r--r--scene/3d/bone_attachment_3d.h4
-rw-r--r--scene/3d/camera_3d.cpp44
-rw-r--r--scene/3d/camera_3d.h6
-rw-r--r--scene/3d/collision_object_3d.cpp16
-rw-r--r--scene/3d/collision_object_3d.h10
-rw-r--r--scene/3d/collision_polygon_3d.cpp13
-rw-r--r--scene/3d/collision_polygon_3d.h4
-rw-r--r--scene/3d/collision_shape_3d.cpp15
-rw-r--r--scene/3d/collision_shape_3d.h7
-rw-r--r--scene/3d/cpu_particles_3d.cpp233
-rw-r--r--scene/3d/cpu_particles_3d.h17
-rw-r--r--scene/3d/decal.cpp14
-rw-r--r--scene/3d/decal.h5
-rw-r--r--scene/3d/fog_volume.cpp4
-rw-r--r--scene/3d/fog_volume.h5
-rw-r--r--scene/3d/gpu_particles_3d.cpp85
-rw-r--r--scene/3d/gpu_particles_3d.h23
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp283
-rw-r--r--scene/3d/gpu_particles_collision_3d.h78
-rw-r--r--scene/3d/importer_mesh_instance_3d.cpp4
-rw-r--r--scene/3d/importer_mesh_instance_3d.h4
-rw-r--r--scene/3d/joint_3d.cpp32
-rw-r--r--scene/3d/joint_3d.h4
-rw-r--r--scene/3d/label_3d.cpp1010
-rw-r--r--scene/3d/label_3d.h240
-rw-r--r--scene/3d/light_3d.cpp130
-rw-r--r--scene/3d/light_3d.h41
-rw-r--r--scene/3d/lightmap_gi.cpp73
-rw-r--r--scene/3d/lightmap_gi.h5
-rw-r--r--scene/3d/lightmap_probe.cpp4
-rw-r--r--scene/3d/lightmap_probe.h4
-rw-r--r--scene/3d/lightmapper.cpp4
-rw-r--r--scene/3d/lightmapper.h10
-rw-r--r--scene/3d/mesh_instance_3d.cpp30
-rw-r--r--scene/3d/mesh_instance_3d.h7
-rw-r--r--scene/3d/multimesh_instance_3d.cpp8
-rw-r--r--scene/3d/multimesh_instance_3d.h6
-rw-r--r--scene/3d/navigation_agent_3d.cpp38
-rw-r--r--scene/3d/navigation_agent_3d.h26
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp33
-rw-r--r--scene/3d/navigation_obstacle_3d.h4
-rw-r--r--scene/3d/navigation_region_3d.cpp31
-rw-r--r--scene/3d/navigation_region_3d.h10
-rw-r--r--scene/3d/node_3d.cpp157
-rw-r--r--scene/3d/node_3d.h27
-rw-r--r--scene/3d/occluder_instance_3d.cpp564
-rw-r--r--scene/3d/occluder_instance_3d.h133
-rw-r--r--scene/3d/path_3d.cpp17
-rw-r--r--scene/3d/path_3d.h5
-rw-r--r--scene/3d/physics_body_3d.cpp315
-rw-r--r--scene/3d/physics_body_3d.h60
-rw-r--r--scene/3d/position_3d.cpp4
-rw-r--r--scene/3d/position_3d.h4
-rw-r--r--scene/3d/proximity_group_3d.cpp182
-rw-r--r--scene/3d/ray_cast_3d.cpp40
-rw-r--r--scene/3d/ray_cast_3d.h16
-rw-r--r--scene/3d/reflection_probe.cpp24
-rw-r--r--scene/3d/reflection_probe.h11
-rw-r--r--scene/3d/remote_transform_3d.cpp9
-rw-r--r--scene/3d/remote_transform_3d.h4
-rw-r--r--scene/3d/skeleton_3d.cpp42
-rw-r--r--scene/3d/skeleton_3d.h15
-rw-r--r--scene/3d/skeleton_ik_3d.cpp14
-rw-r--r--scene/3d/skeleton_ik_3d.h6
-rw-r--r--scene/3d/soft_dynamic_body_3d.cpp29
-rw-r--r--scene/3d/soft_dynamic_body_3d.h10
-rw-r--r--scene/3d/spring_arm_3d.cpp27
-rw-r--r--scene/3d/spring_arm_3d.h6
-rw-r--r--scene/3d/sprite_3d.cpp136
-rw-r--r--scene/3d/sprite_3d.h31
-rw-r--r--scene/3d/vehicle_body_3d.cpp32
-rw-r--r--scene/3d/vehicle_body_3d.h8
-rw-r--r--scene/3d/velocity_tracker_3d.cpp4
-rw-r--r--scene/3d/velocity_tracker_3d.h4
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp43
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.h6
-rw-r--r--scene/3d/visual_instance_3d.cpp62
-rw-r--r--scene/3d/visual_instance_3d.h16
-rw-r--r--scene/3d/voxel_gi.cpp16
-rw-r--r--scene/3d/voxel_gi.h7
-rw-r--r--scene/3d/voxelizer.cpp13
-rw-r--r--scene/3d/voxelizer.h6
-rw-r--r--scene/3d/world_environment.cpp61
-rw-r--r--scene/3d/world_environment.h4
-rw-r--r--scene/3d/xr_nodes.cpp71
-rw-r--r--scene/3d/xr_nodes.h15
-rw-r--r--scene/SCsub1
-rw-r--r--scene/animation/animation_blend_space_1d.cpp8
-rw-r--r--scene/animation/animation_blend_space_1d.h4
-rw-r--r--scene/animation/animation_blend_space_2d.cpp14
-rw-r--r--scene/animation/animation_blend_space_2d.h4
-rw-r--r--scene/animation/animation_blend_tree.cpp14
-rw-r--r--scene/animation/animation_blend_tree.h6
-rw-r--r--scene/animation/animation_node_state_machine.cpp590
-rw-r--r--scene/animation/animation_node_state_machine.h44
-rw-r--r--scene/animation/animation_player.cpp496
-rw-r--r--scene/animation/animation_player.h81
-rw-r--r--scene/animation/animation_tree.cpp378
-rw-r--r--scene/animation/animation_tree.h39
-rw-r--r--scene/animation/easing_equations.h4
-rw-r--r--scene/animation/root_motion_view.cpp134
-rw-r--r--scene/animation/root_motion_view.h5
-rw-r--r--scene/animation/tween.cpp181
-rw-r--r--scene/animation/tween.h23
-rw-r--r--scene/audio/audio_stream_player.cpp86
-rw-r--r--scene/audio/audio_stream_player.h4
-rw-r--r--scene/debugger/scene_debugger.cpp252
-rw-r--r--scene/debugger/scene_debugger.h45
-rw-r--r--scene/gui/aspect_ratio_container.cpp46
-rw-r--r--scene/gui/aspect_ratio_container.h29
-rw-r--r--scene/gui/base_button.cpp107
-rw-r--r--scene/gui/base_button.h11
-rw-r--r--scene/gui/box_container.cpp69
-rw-r--r--scene/gui/box_container.h23
-rw-r--r--scene/gui/button.cpp151
-rw-r--r--scene/gui/button.h25
-rw-r--r--scene/gui/center_container.cpp44
-rw-r--r--scene/gui/center_container.h7
-rw-r--r--scene/gui/check_box.cpp70
-rw-r--r--scene/gui/check_box.h10
-rw-r--r--scene/gui/check_button.cpp101
-rw-r--r--scene/gui/check_button.h12
-rw-r--r--scene/gui/code_edit.cpp310
-rw-r--r--scene/gui/code_edit.h20
-rw-r--r--scene/gui/color_picker.cpp29
-rw-r--r--scene/gui/color_picker.h6
-rw-r--r--scene/gui/color_rect.cpp10
-rw-r--r--scene/gui/color_rect.h4
-rw-r--r--scene/gui/container.cpp52
-rw-r--r--scene/gui/container.h10
-rw-r--r--scene/gui/control.cpp735
-rw-r--r--scene/gui/control.h55
-rw-r--r--scene/gui/dialogs.cpp37
-rw-r--r--scene/gui/dialogs.h18
-rw-r--r--scene/gui/file_dialog.cpp131
-rw-r--r--scene/gui/file_dialog.h61
-rw-r--r--scene/gui/flow_container.cpp281
-rw-r--r--scene/gui/flow_container.h (renamed from scene/3d/proximity_group_3d.h)70
-rw-r--r--scene/gui/gradient_edit.cpp141
-rw-r--r--scene/gui/gradient_edit.h10
-rw-r--r--scene/gui/graph_edit.cpp409
-rw-r--r--scene/gui/graph_edit.h93
-rw-r--r--scene/gui/graph_node.cpp122
-rw-r--r--scene/gui/graph_node.h19
-rw-r--r--scene/gui/grid_container.cpp93
-rw-r--r--scene/gui/grid_container.h4
-rw-r--r--scene/gui/item_list.cpp877
-rw-r--r--scene/gui/item_list.h12
-rw-r--r--scene/gui/label.cpp570
-rw-r--r--scene/gui/label.h52
-rw-r--r--scene/gui/line_edit.cpp306
-rw-r--r--scene/gui/line_edit.h39
-rw-r--r--scene/gui/link_button.cpp34
-rw-r--r--scene/gui/link_button.h13
-rw-r--r--scene/gui/margin_container.cpp25
-rw-r--r--scene/gui/margin_container.h7
-rw-r--r--scene/gui/menu_button.cpp32
-rw-r--r--scene/gui/menu_button.h10
-rw-r--r--scene/gui/nine_patch_rect.cpp32
-rw-r--r--scene/gui/nine_patch_rect.h4
-rw-r--r--scene/gui/option_button.cpp253
-rw-r--r--scene/gui/option_button.h24
-rw-r--r--scene/gui/panel.cpp35
-rw-r--r--scene/gui/panel.h19
-rw-r--r--scene/gui/panel_container.cpp90
-rw-r--r--scene/gui/panel_container.h7
-rw-r--r--scene/gui/popup.cpp92
-rw-r--r--scene/gui/popup.h14
-rw-r--r--scene/gui/popup_menu.cpp460
-rw-r--r--scene/gui/popup_menu.h21
-rw-r--r--scene/gui/progress_bar.cpp110
-rw-r--r--scene/gui/progress_bar.h20
-rw-r--r--scene/gui/range.cpp15
-rw-r--r--scene/gui/range.h12
-rw-r--r--scene/gui/reference_rect.cpp20
-rw-r--r--scene/gui/reference_rect.h4
-rw-r--r--scene/gui/rich_text_effect.cpp4
-rw-r--r--scene/gui/rich_text_effect.h4
-rw-r--r--scene/gui/rich_text_label.cpp1186
-rw-r--r--scene/gui/rich_text_label.h120
-rw-r--r--scene/gui/scroll_bar.cpp306
-rw-r--r--scene/gui/scroll_bar.h4
-rw-r--r--scene/gui/scroll_container.cpp290
-rw-r--r--scene/gui/scroll_container.h47
-rw-r--r--scene/gui/separator.cpp5
-rw-r--r--scene/gui/separator.h4
-rw-r--r--scene/gui/slider.cpp22
-rw-r--r--scene/gui/slider.h4
-rw-r--r--scene/gui/spin_box.cpp93
-rw-r--r--scene/gui/spin_box.h14
-rw-r--r--scene/gui/split_container.cpp34
-rw-r--r--scene/gui/split_container.h7
-rw-r--r--scene/gui/subviewport_container.cpp127
-rw-r--r--scene/gui/subviewport_container.h10
-rw-r--r--scene/gui/tab_bar.cpp1169
-rw-r--r--scene/gui/tab_bar.h62
-rw-r--r--scene/gui/tab_container.cpp1196
-rw-r--r--scene/gui/tab_container.h71
-rw-r--r--scene/gui/text_edit.cpp1016
-rw-r--r--scene/gui/text_edit.h96
-rw-r--r--scene/gui/texture_button.cpp127
-rw-r--r--scene/gui/texture_button.h12
-rw-r--r--scene/gui/texture_progress_bar.cpp50
-rw-r--r--scene/gui/texture_progress_bar.h4
-rw-r--r--scene/gui/texture_rect.cpp183
-rw-r--r--scene/gui/texture_rect.h13
-rw-r--r--scene/gui/tree.cpp714
-rw-r--r--scene/gui/tree.h84
-rw-r--r--scene/gui/video_stream_player.cpp (renamed from scene/gui/video_player.cpp)187
-rw-r--r--scene/gui/video_stream_player.h (renamed from scene/gui/video_player.h)20
-rw-r--r--scene/gui/view_panner.cpp184
-rw-r--r--scene/gui/view_panner.h83
-rw-r--r--scene/main/canvas_item.cpp209
-rw-r--r--scene/main/canvas_item.h22
-rw-r--r--scene/main/canvas_layer.cpp54
-rw-r--r--scene/main/canvas_layer.h10
-rw-r--r--scene/main/http_request.cpp141
-rw-r--r--scene/main/http_request.h18
-rw-r--r--scene/main/instance_placeholder.cpp4
-rw-r--r--scene/main/instance_placeholder.h4
-rw-r--r--scene/main/missing_node.cpp98
-rw-r--r--scene/main/missing_node.h63
-rw-r--r--scene/main/node.cpp527
-rw-r--r--scene/main/node.h95
-rw-r--r--scene/main/resource_preloader.cpp27
-rw-r--r--scene/main/resource_preloader.h10
-rw-r--r--scene/main/scene_tree.cpp274
-rw-r--r--scene/main/scene_tree.h62
-rw-r--r--scene/main/shader_globals_override.cpp56
-rw-r--r--scene/main/shader_globals_override.h4
-rw-r--r--scene/main/timer.cpp10
-rw-r--r--scene/main/timer.h4
-rw-r--r--scene/main/viewport.cpp544
-rw-r--r--scene/main/viewport.h76
-rw-r--r--scene/main/window.cpp168
-rw-r--r--scene/main/window.h15
-rw-r--r--scene/multiplayer/SCsub5
-rw-r--r--scene/multiplayer/multiplayer_spawner.cpp231
-rw-r--r--scene/multiplayer/multiplayer_spawner.h101
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.cpp163
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.h72
-rw-r--r--scene/multiplayer/scene_cache_interface.cpp263
-rw-r--r--scene/multiplayer/scene_cache_interface.h82
-rw-r--r--scene/multiplayer/scene_replication_interface.cpp419
-rw-r--r--scene/multiplayer/scene_replication_interface.h84
-rw-r--r--scene/multiplayer/scene_replication_state.cpp254
-rw-r--r--scene/multiplayer/scene_replication_state.h121
-rw-r--r--scene/multiplayer/scene_rpc_interface.cpp511
-rw-r--r--scene/multiplayer/scene_rpc_interface.h91
-rw-r--r--scene/property_utils.cpp50
-rw-r--r--scene/property_utils.h6
-rw-r--r--scene/register_scene_types.cpp259
-rw-r--r--scene/register_scene_types.h6
-rw-r--r--scene/resources/animation.cpp149
-rw-r--r--scene/resources/animation.h12
-rw-r--r--scene/resources/animation_library.cpp151
-rw-r--r--scene/resources/animation_library.h66
-rw-r--r--scene/resources/audio_stream_sample.cpp10
-rw-r--r--scene/resources/audio_stream_sample.h5
-rw-r--r--scene/resources/bit_map.cpp10
-rw-r--r--scene/resources/bit_map.h4
-rw-r--r--scene/resources/box_shape_3d.cpp7
-rw-r--r--scene/resources/box_shape_3d.h4
-rw-r--r--scene/resources/camera_effects.cpp4
-rw-r--r--scene/resources/camera_effects.h4
-rw-r--r--scene/resources/canvas_item_material.cpp6
-rw-r--r--scene/resources/canvas_item_material.h21
-rw-r--r--scene/resources/capsule_shape_2d.cpp16
-rw-r--r--scene/resources/capsule_shape_2d.h4
-rw-r--r--scene/resources/capsule_shape_3d.cpp11
-rw-r--r--scene/resources/capsule_shape_3d.h8
-rw-r--r--scene/resources/circle_shape_2d.cpp18
-rw-r--r--scene/resources/circle_shape_2d.h4
-rw-r--r--scene/resources/concave_polygon_shape_2d.cpp4
-rw-r--r--scene/resources/concave_polygon_shape_2d.h4
-rw-r--r--scene/resources/concave_polygon_shape_3d.cpp8
-rw-r--r--scene/resources/concave_polygon_shape_3d.h4
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp6
-rw-r--r--scene/resources/convex_polygon_shape_2d.h4
-rw-r--r--scene/resources/convex_polygon_shape_3d.cpp4
-rw-r--r--scene/resources/convex_polygon_shape_3d.h4
-rw-r--r--scene/resources/curve.cpp647
-rw-r--r--scene/resources/curve.h124
-rw-r--r--scene/resources/cylinder_shape_3d.cpp11
-rw-r--r--scene/resources/cylinder_shape_3d.h6
-rw-r--r--scene/resources/default_theme/SCsub21
-rw-r--r--scene/resources/default_theme/add.svg1
-rw-r--r--scene/resources/default_theme/arrow_down.pngbin109 -> 0 bytes
-rw-r--r--scene/resources/default_theme/arrow_down.svg1
-rw-r--r--scene/resources/default_theme/arrow_left.pngbin159 -> 0 bytes
-rw-r--r--scene/resources/default_theme/arrow_left.svg1
-rw-r--r--scene/resources/default_theme/arrow_right.pngbin103 -> 0 bytes
-rw-r--r--scene/resources/default_theme/arrow_right.svg1
-rw-r--r--scene/resources/default_theme/bar_arrow.pngbin208 -> 0 bytes
-rw-r--r--scene/resources/default_theme/bookmark.pngbin160 -> 0 bytes
-rw-r--r--scene/resources/default_theme/bookmark.svg1
-rw-r--r--scene/resources/default_theme/breakpoint.svg1
-rw-r--r--scene/resources/default_theme/button_disabled.pngbin256 -> 0 bytes
-rw-r--r--scene/resources/default_theme/button_focus.pngbin203 -> 0 bytes
-rw-r--r--scene/resources/default_theme/button_hover.pngbin342 -> 0 bytes
-rw-r--r--scene/resources/default_theme/button_normal.pngbin336 -> 0 bytes
-rw-r--r--scene/resources/default_theme/button_pressed.pngbin499 -> 0 bytes
-rw-r--r--scene/resources/default_theme/checked.pngbin363 -> 0 bytes
-rw-r--r--scene/resources/default_theme/checked.svg1
-rw-r--r--scene/resources/default_theme/checked_disabled.pngbin421 -> 0 bytes
-rw-r--r--scene/resources/default_theme/checked_disabled.svg1
-rw-r--r--scene/resources/default_theme/checker_bg.pngbin77 -> 0 bytes
-rw-r--r--scene/resources/default_theme/close.pngbin155 -> 0 bytes
-rw-r--r--scene/resources/default_theme/close.svg1
-rw-r--r--scene/resources/default_theme/close_hl.pngbin155 -> 0 bytes
-rw-r--r--scene/resources/default_theme/close_hl.svg1
-rw-r--r--scene/resources/default_theme/color_picker_bar_arrow.svg1
-rw-r--r--scene/resources/default_theme/color_picker_cursor.svg1
-rw-r--r--scene/resources/default_theme/color_picker_hue.pngbin141 -> 0 bytes
-rw-r--r--scene/resources/default_theme/color_picker_hue.svg1
-rw-r--r--scene/resources/default_theme/color_picker_overbright.svg1
-rw-r--r--scene/resources/default_theme/color_picker_pipette.svg1
-rw-r--r--scene/resources/default_theme/color_picker_sample.pngbin117 -> 0 bytes
-rw-r--r--scene/resources/default_theme/default_theme.cpp887
-rw-r--r--scene/resources/default_theme/default_theme.h8
-rw-r--r--scene/resources/default_theme/default_theme_icons_builders.py78
-rw-r--r--scene/resources/default_theme/dialog_bg.pngbin1314 -> 0 bytes
-rw-r--r--scene/resources/default_theme/dropdown.pngbin133 -> 0 bytes
-rw-r--r--scene/resources/default_theme/ellipsis.pngbin193 -> 0 bytes
-rw-r--r--scene/resources/default_theme/error_icon.pngbin106 -> 0 bytes
-rw-r--r--scene/resources/default_theme/error_icon.svg1
-rw-r--r--scene/resources/default_theme/error_icon.xpm38
-rw-r--r--scene/resources/default_theme/file.svg1
-rw-r--r--scene/resources/default_theme/focus.pngbin200 -> 0 bytes
-rw-r--r--scene/resources/default_theme/folder.svg1
-rw-r--r--scene/resources/default_theme/folder_up.svg1
-rw-r--r--scene/resources/default_theme/graph_node.pngbin762 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_breakpoint.pngbin140 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_close.pngbin152 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_comment.pngbin382 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_comment_focus.pngbin373 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_default.pngbin205 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_default_focus.pngbin195 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_position.pngbin140 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_selected.pngbin827 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_port.pngbin167 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_port.svg1
-rw-r--r--scene/resources/default_theme/grid_layout.svg1
-rw-r--r--scene/resources/default_theme/grid_minimap.svg1
-rw-r--r--scene/resources/default_theme/grid_snap.svg1
-rw-r--r--scene/resources/default_theme/hseparator.pngbin112 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_bg.pngbin263 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber.pngbin300 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber_disabled.pngbin288 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber_hl.pngbin450 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_tick.pngbin149 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_tick.svg1
-rw-r--r--scene/resources/default_theme/hsplit_bg.pngbin85 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hsplitter.pngbin97 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hsplitter.svg1
-rw-r--r--scene/resources/default_theme/icon_add.pngbin86 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_close.pngbin155 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_color_pick.pngbin227 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_file.pngbin183 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_folder.pngbin103 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_grid_layout.pngbin2170 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_grid_minimap.pngbin640 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_parent_folder.pngbin161 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_reload.pngbin234 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_snap_grid.pngbin226 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_visibility.pngbin448 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_less.pngbin76 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_more.pngbin85 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_reset.pngbin108 -> 0 bytes
-rw-r--r--scene/resources/default_theme/indeterminate.pngbin242 -> 0 bytes
-rw-r--r--scene/resources/default_theme/indeterminate.svg1
-rw-r--r--scene/resources/default_theme/line_edit.pngbin176 -> 0 bytes
-rw-r--r--scene/resources/default_theme/line_edit_clear.pngbin158 -> 0 bytes
-rw-r--r--scene/resources/default_theme/line_edit_clear.svg1
-rw-r--r--scene/resources/default_theme/line_edit_disabled.pngbin135 -> 0 bytes
-rwxr-xr-xscene/resources/default_theme/make_header.py73
-rw-r--r--scene/resources/default_theme/mini_checkerboard.pngbin80 -> 0 bytes
-rw-r--r--scene/resources/default_theme/mini_checkerboard.svg1
-rw-r--r--scene/resources/default_theme/option_arrow.pngbin119 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_arrow.svg1
-rw-r--r--scene/resources/default_theme/option_button_disabled.pngbin656 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_disabled_mirrored.pngbin704 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_hover.pngbin667 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_hover_mirrored.pngbin728 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_normal.pngbin668 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_normal_mirrored.pngbin726 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_pressed.pngbin677 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_pressed_mirrored.pngbin736 -> 0 bytes
-rw-r--r--scene/resources/default_theme/overbright_indicator.pngbin210 -> 0 bytes
-rw-r--r--scene/resources/default_theme/panel_bg.pngbin85 -> 0 bytes
-rw-r--r--scene/resources/default_theme/picker_cursor.pngbin300 -> 0 bytes
-rw-r--r--scene/resources/default_theme/popup_bg.pngbin410 -> 0 bytes
-rw-r--r--scene/resources/default_theme/popup_bg_disabled.pngbin362 -> 0 bytes
-rw-r--r--scene/resources/default_theme/popup_menu_arrow_left.svg1
-rw-r--r--scene/resources/default_theme/popup_menu_arrow_right.svg1
-rw-r--r--scene/resources/default_theme/popup_window.pngbin921 -> 0 bytes
-rw-r--r--scene/resources/default_theme/progress_bar.pngbin194 -> 0 bytes
-rw-r--r--scene/resources/default_theme/progress_fill.pngbin112 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_checked.pngbin257 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_checked.svg1
-rw-r--r--scene/resources/default_theme/radio_checked_disabled.pngbin235 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_checked_disabled.svg1
-rw-r--r--scene/resources/default_theme/radio_unchecked.pngbin207 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_unchecked.svg1
-rw-r--r--scene/resources/default_theme/radio_unchecked_disabled.pngbin183 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_unchecked_disabled.svg1
-rw-r--r--scene/resources/default_theme/reload.svg1
-rw-r--r--scene/resources/default_theme/resizer_nw.svg1
-rw-r--r--scene/resources/default_theme/resizer_se.svg1
-rw-r--r--scene/resources/default_theme/scroll_bg.pngbin252 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left.pngbin196 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left.svg1
-rw-r--r--scene/resources/default_theme/scroll_button_left_hl.pngbin200 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left_hl.svg1
-rw-r--r--scene/resources/default_theme/scroll_button_right.pngbin198 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_right.svg1
-rw-r--r--scene/resources/default_theme/scroll_button_right_hl.pngbin210 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_right_hl.svg1
-rw-r--r--scene/resources/default_theme/scroll_grabber.pngbin264 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber_hl.pngbin275 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber_pressed.pngbin104 -> 0 bytes
-rw-r--r--scene/resources/default_theme/selection.pngbin195 -> 0 bytes
-rw-r--r--scene/resources/default_theme/selection_oof.pngbin196 -> 0 bytes
-rw-r--r--scene/resources/default_theme/slider_grabber.svg1
-rw-r--r--scene/resources/default_theme/slider_grabber_disabled.svg1
-rw-r--r--scene/resources/default_theme/slider_grabber_hl.svg1
-rw-r--r--scene/resources/default_theme/space.pngbin99 -> 0 bytes
-rw-r--r--scene/resources/default_theme/spinbox_updown.pngbin146 -> 0 bytes
-rw-r--r--scene/resources/default_theme/submenu.pngbin103 -> 0 bytes
-rw-r--r--scene/resources/default_theme/submenu_mirrored.pngbin157 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab.pngbin82 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_behind.pngbin282 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_close.pngbin158 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_container_bg.pngbin336 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_current.pngbin346 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_disabled.pngbin258 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_menu.pngbin111 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_menu_hl.pngbin111 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tabs_drop_mark.svg1
-rw-r--r--scene/resources/default_theme/tabs_menu.svg1
-rw-r--r--scene/resources/default_theme/tabs_menu_hl.svg1
-rw-r--r--scene/resources/default_theme/text_edit_ellipsis.svg1
-rw-r--r--scene/resources/default_theme/text_edit_space.svg1
-rw-r--r--scene/resources/default_theme/text_edit_tab.svg1
-rw-r--r--scene/resources/default_theme/theme_data.h517
-rw-r--r--scene/resources/default_theme/toggle_off.pngbin1077 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_off.svg1
-rw-r--r--scene/resources/default_theme/toggle_off_disabled.pngbin932 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_off_disabled.svg1
-rw-r--r--scene/resources/default_theme/toggle_off_disabled_mirrored.pngbin535 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_off_disabled_mirrored.svg1
-rw-r--r--scene/resources/default_theme/toggle_off_mirrored.pngbin1169 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_off_mirrored.svg1
-rw-r--r--scene/resources/default_theme/toggle_on.pngbin1034 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_on.svg1
-rw-r--r--scene/resources/default_theme/toggle_on_disabled.pngbin1039 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_disabled.svg1
-rw-r--r--scene/resources/default_theme/toggle_on_disabled_mirrored.pngbin496 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_disabled_mirrored.svg1
-rw-r--r--scene/resources/default_theme/toggle_on_mirrored.pngbin1133 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_mirrored.svg1
-rw-r--r--scene/resources/default_theme/tooltip_bg.pngbin198 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tree_bg.pngbin176 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tree_bg_disabled.pngbin135 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tree_title.pngbin86 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tree_title_pressed.pngbin86 -> 0 bytes
-rw-r--r--scene/resources/default_theme/unchecked.pngbin222 -> 0 bytes
-rw-r--r--scene/resources/default_theme/unchecked.svg1
-rw-r--r--scene/resources/default_theme/unchecked_disabled.pngbin246 -> 0 bytes
-rw-r--r--scene/resources/default_theme/unchecked_disabled.svg1
-rw-r--r--scene/resources/default_theme/updown.pngbin144 -> 0 bytes
-rw-r--r--scene/resources/default_theme/updown.svg1
-rw-r--r--scene/resources/default_theme/visibility_visible.svg1
-rw-r--r--scene/resources/default_theme/vseparator.pngbin108 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_bg.pngbin276 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber.pngbin245 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber_disabled.pngbin237 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber_hl.pngbin261 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_tick.pngbin145 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_tick.svg1
-rw-r--r--scene/resources/default_theme/vsplit_bg.pngbin85 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vsplitter.pngbin95 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vsplitter.svg1
-rw-r--r--scene/resources/default_theme/window_resizer.pngbin87 -> 0 bytes
-rw-r--r--scene/resources/default_theme/window_resizer_mirrored.pngbin109 -> 0 bytes
-rw-r--r--scene/resources/default_theme/zoom_less.svg1
-rw-r--r--scene/resources/default_theme/zoom_more.svg1
-rw-r--r--scene/resources/default_theme/zoom_reset.svg1
-rw-r--r--scene/resources/environment.cpp175
-rw-r--r--scene/resources/environment.h55
-rw-r--r--scene/resources/fog_material.cpp4
-rw-r--r--scene/resources/fog_material.h4
-rw-r--r--scene/resources/font.cpp1018
-rw-r--r--scene/resources/font.h44
-rw-r--r--scene/resources/gradient.cpp10
-rw-r--r--scene/resources/gradient.h6
-rw-r--r--scene/resources/height_map_shape_3d.cpp9
-rw-r--r--scene/resources/height_map_shape_3d.h4
-rw-r--r--scene/resources/immediate_mesh.cpp11
-rw-r--r--scene/resources/immediate_mesh.h6
-rw-r--r--scene/resources/importer_mesh.cpp48
-rw-r--r--scene/resources/importer_mesh.h4
-rw-r--r--scene/resources/material.cpp255
-rw-r--r--scene/resources/material.h122
-rw-r--r--scene/resources/mesh.cpp362
-rw-r--r--scene/resources/mesh.h101
-rw-r--r--scene/resources/mesh_data_tool.cpp6
-rw-r--r--scene/resources/mesh_data_tool.h4
-rw-r--r--scene/resources/mesh_library.cpp37
-rw-r--r--scene/resources/mesh_library.h8
-rw-r--r--scene/resources/multimesh.cpp16
-rw-r--r--scene/resources/multimesh.h4
-rw-r--r--scene/resources/navigation_mesh.cpp18
-rw-r--r--scene/resources/navigation_mesh.h12
-rw-r--r--scene/resources/packed_scene.cpp244
-rw-r--r--scene/resources/packed_scene.h20
-rw-r--r--scene/resources/particles_material.cpp83
-rw-r--r--scene/resources/particles_material.h52
-rw-r--r--scene/resources/physics_material.cpp4
-rw-r--r--scene/resources/physics_material.h4
-rw-r--r--scene/resources/polygon_path_finder.cpp40
-rw-r--r--scene/resources/polygon_path_finder.h8
-rw-r--r--scene/resources/primitive_meshes.cpp859
-rw-r--r--scene/resources/primitive_meshes.h162
-rw-r--r--scene/resources/rectangle_shape_2d.cpp5
-rw-r--r--scene/resources/rectangle_shape_2d.h4
-rw-r--r--scene/resources/resource_format_text.cpp567
-rw-r--r--scene/resources/resource_format_text.h72
-rw-r--r--scene/resources/scene_replication_config.cpp187
-rw-r--r--scene/resources/scene_replication_config.h90
-rw-r--r--scene/resources/segment_shape_2d.cpp4
-rw-r--r--scene/resources/segment_shape_2d.h4
-rw-r--r--scene/resources/separation_ray_shape_2d.cpp20
-rw-r--r--scene/resources/separation_ray_shape_2d.h4
-rw-r--r--scene/resources/separation_ray_shape_3d.cpp13
-rw-r--r--scene/resources/separation_ray_shape_3d.h4
-rw-r--r--scene/resources/shader.cpp21
-rw-r--r--scene/resources/shader.h20
-rw-r--r--scene/resources/shape_2d.cpp4
-rw-r--r--scene/resources/shape_2d.h4
-rw-r--r--scene/resources/shape_3d.cpp17
-rw-r--r--scene/resources/shape_3d.h8
-rw-r--r--scene/resources/skeleton_modification_2d.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d.h6
-rw-r--r--scene/resources/skeleton_modification_2d_ccdik.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d_ccdik.h4
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.h4
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.cpp6
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.h4
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.h4
-rw-r--r--scene/resources/skeleton_modification_2d_physicalbones.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d_physicalbones.h6
-rw-r--r--scene/resources/skeleton_modification_2d_stackholder.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d_stackholder.h4
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.h4
-rw-r--r--scene/resources/skeleton_modification_3d.cpp7
-rw-r--r--scene/resources/skeleton_modification_3d.h6
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.h4
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h4
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp6
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.h4
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.h4
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.h4
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.h4
-rw-r--r--scene/resources/skeleton_modification_stack_2d.cpp8
-rw-r--r--scene/resources/skeleton_modification_stack_2d.h4
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp10
-rw-r--r--scene/resources/skeleton_modification_stack_3d.h4
-rw-r--r--scene/resources/skin.cpp5
-rw-r--r--scene/resources/skin.h10
-rw-r--r--scene/resources/sky.cpp6
-rw-r--r--scene/resources/sky.h4
-rw-r--r--scene/resources/sky_material.cpp160
-rw-r--r--scene/resources/sky_material.h49
-rw-r--r--scene/resources/sphere_shape_3d.cpp8
-rw-r--r--scene/resources/sphere_shape_3d.h6
-rw-r--r--scene/resources/sprite_frames.cpp50
-rw-r--r--scene/resources/sprite_frames.h18
-rw-r--r--scene/resources/style_box.cpp122
-rw-r--r--scene/resources/style_box.h18
-rw-r--r--scene/resources/surface_tool.cpp95
-rw-r--r--scene/resources/surface_tool.h16
-rw-r--r--scene/resources/syntax_highlighter.cpp72
-rw-r--r--scene/resources/syntax_highlighter.h8
-rw-r--r--scene/resources/text_file.cpp11
-rw-r--r--scene/resources/text_file.h4
-rw-r--r--scene/resources/text_line.cpp100
-rw-r--r--scene/resources/text_line.h16
-rw-r--r--scene/resources/text_paragraph.cpp126
-rw-r--r--scene/resources/text_paragraph.h16
-rw-r--r--scene/resources/texture.cpp956
-rw-r--r--scene/resources/texture.h300
-rw-r--r--scene/resources/theme.cpp611
-rw-r--r--scene/resources/theme.h67
-rw-r--r--scene/resources/tile_set.cpp209
-rw-r--r--scene/resources/tile_set.h58
-rw-r--r--scene/resources/video_stream.h4
-rw-r--r--scene/resources/visual_shader.cpp1933
-rw-r--r--scene/resources/visual_shader.h216
-rw-r--r--scene/resources/visual_shader_nodes.cpp2585
-rw-r--r--scene/resources/visual_shader_nodes.h433
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp402
-rw-r--r--scene/resources/visual_shader_particle_nodes.h25
-rw-r--r--scene/resources/visual_shader_sdf_nodes.cpp46
-rw-r--r--scene/resources/visual_shader_sdf_nodes.h6
-rw-r--r--scene/resources/world_2d.cpp8
-rw-r--r--scene/resources/world_2d.h10
-rw-r--r--scene/resources/world_3d.cpp6
-rw-r--r--scene/resources/world_3d.h8
-rw-r--r--scene/resources/world_boundary_shape_2d.cpp6
-rw-r--r--scene/resources/world_boundary_shape_2d.h4
-rw-r--r--scene/resources/world_boundary_shape_3d.cpp27
-rw-r--r--scene/resources/world_boundary_shape_3d.h4
-rw-r--r--scene/scene_string_names.cpp5
-rw-r--r--scene/scene_string_names.h5
708 files changed, 31490 insertions, 15497 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index fad4784d51..b33c2f43d4 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -122,13 +122,13 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &property) const {
}
property.hint_string += String(E->get());
- if (animation == E) {
+ if (animation == E->get()) {
current_found = true;
}
}
if (!current_found) {
- if (property.hint_string == String()) {
+ if (property.hint_string.is_empty()) {
property.hint_string = String(animation);
} else {
property.hint_string = String(animation) + "," + property.hint_string;
@@ -158,14 +158,14 @@ void AnimatedSprite2D::_notification(int p_what) {
return;
}
- double speed = frames->get_animation_speed(animation) * speed_scale;
- if (speed == 0) {
- return; //do nothing
- }
-
double remaining = get_process_delta_time();
while (remaining) {
+ double speed = frames->get_animation_speed(animation) * speed_scale;
+ if (speed == 0) {
+ return; // Do nothing.
+ }
+
if (timeout <= 0) {
timeout = _get_frame_duration();
@@ -247,7 +247,6 @@ void AnimatedSprite2D::_notification(int p_what) {
}
texture->draw_rect_region(ci, dst_rect, Rect2(Vector2(), texture->get_size()), Color(1, 1, 1), false);
-
} break;
}
}
@@ -366,7 +365,7 @@ void AnimatedSprite2D::_res_changed() {
update();
}
-void AnimatedSprite2D::_set_playing(bool p_playing) {
+void AnimatedSprite2D::set_playing(bool p_playing) {
if (playing == p_playing) {
return;
}
@@ -375,7 +374,7 @@ void AnimatedSprite2D::_set_playing(bool p_playing) {
set_process_internal(playing);
}
-bool AnimatedSprite2D::_is_playing() const {
+bool AnimatedSprite2D::is_playing() const {
return playing;
}
@@ -389,15 +388,12 @@ void AnimatedSprite2D::play(const StringName &p_animation, const bool p_backward
}
}
- _set_playing(true);
+ is_over = false;
+ set_playing(true);
}
void AnimatedSprite2D::stop() {
- _set_playing(false);
-}
-
-bool AnimatedSprite2D::is_playing() const {
- return playing;
+ set_playing(false);
}
double AnimatedSprite2D::_get_frame_duration() {
@@ -421,7 +417,7 @@ void AnimatedSprite2D::_reset_timeout() {
void AnimatedSprite2D::set_animation(const StringName &p_animation) {
ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation));
- ERR_FAIL_COND_MSG(frames->get_animation_names().find(p_animation) == -1, vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation));
if (animation == p_animation) {
return;
@@ -442,12 +438,23 @@ TypedArray<String> AnimatedSprite2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (frames.is_null()) {
- warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames."));
+ warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames."));
}
return warnings;
}
+void AnimatedSprite2D::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ if (p_idx == 0 && p_function == "play" && frames.is_valid()) {
+ List<StringName> al;
+ frames->get_animation_list(&al);
+ for (const StringName &name : al) {
+ r_options->push_back(String(name).quote());
+ }
+ }
+ Node::get_argument_options(p_function, p_idx, r_options);
+}
+
void AnimatedSprite2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite2D::set_sprite_frames);
ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite2D::get_sprite_frames);
@@ -455,12 +462,11 @@ void AnimatedSprite2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation", "animation"), &AnimatedSprite2D::set_animation);
ClassDB::bind_method(D_METHOD("get_animation"), &AnimatedSprite2D::get_animation);
- ClassDB::bind_method(D_METHOD("_set_playing", "playing"), &AnimatedSprite2D::_set_playing);
- ClassDB::bind_method(D_METHOD("_is_playing"), &AnimatedSprite2D::_is_playing);
+ ClassDB::bind_method(D_METHOD("set_playing", "playing"), &AnimatedSprite2D::set_playing);
+ ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite2D::is_playing);
ClassDB::bind_method(D_METHOD("play", "anim", "backwards"), &AnimatedSprite2D::play, DEFVAL(StringName()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite2D::stop);
- ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite2D::is_playing);
ClassDB::bind_method(D_METHOD("set_centered", "centered"), &AnimatedSprite2D::set_centered);
ClassDB::bind_method(D_METHOD("is_centered"), &AnimatedSprite2D::is_centered);
@@ -488,7 +494,7 @@ void AnimatedSprite2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale"), "set_speed_scale", "get_speed_scale");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "set_playing", "is_playing");
ADD_GROUP("Offset", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h
index ac4b20a6d9..3a41f810dc 100644
--- a/scene/2d/animated_sprite_2d.h
+++ b/scene/2d/animated_sprite_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,8 +57,6 @@ class AnimatedSprite2D : public Node2D {
double _get_frame_duration();
void _reset_timeout();
- void _set_playing(bool p_playing);
- bool _is_playing() const;
Rect2 _get_rect() const;
protected:
@@ -85,6 +83,8 @@ public:
void play(const StringName &p_animation = StringName(), const bool p_backwards = false);
void stop();
+
+ void set_playing(bool p_playing);
bool is_playing() const;
void set_animation(const StringName &p_animation);
@@ -109,6 +109,8 @@ public:
bool is_flipped_v() const;
TypedArray<String> get_configuration_warnings() const override;
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
+
AnimatedSprite2D();
};
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 8db1491953..9699f784a2 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -137,14 +137,14 @@ void Area2D::_body_enter_tree(ObjectID p_id) {
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
- Map<ObjectID, BodyState>::Element *E = body_map.find(p_id);
+ HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(E->get().in_tree);
+ ERR_FAIL_COND(E->value.in_tree);
- E->get().in_tree = true;
+ E->value.in_tree = true;
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape);
}
}
@@ -152,13 +152,13 @@ void Area2D::_body_exit_tree(ObjectID p_id) {
Object *obj = ObjectDB::get_instance(p_id);
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
- Map<ObjectID, BodyState>::Element *E = body_map.find(p_id);
+ HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(!E->get().in_tree);
- E->get().in_tree = false;
+ ERR_FAIL_COND(!E->value.in_tree);
+ E->value.in_tree = false;
emit_signal(SceneStringNames::get_singleton()->body_exited, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape);
}
}
@@ -169,7 +169,7 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i
Object *obj = ObjectDB::get_instance(objid);
Node *node = Object::cast_to<Node>(obj);
- Map<ObjectID, BodyState>::Element *E = body_map.find(objid);
+ HashMap<ObjectID, BodyState>::Iterator E = body_map.find(objid);
if (!body_in && !E) {
return; //does not exist because it was likely removed from the tree
@@ -180,36 +180,36 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i
if (body_in) {
if (!E) {
E = body_map.insert(objid, BodyState());
- E->get().rid = p_body;
- E->get().rc = 0;
- E->get().in_tree = node && node->is_inside_tree();
+ E->value.rid = p_body;
+ 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));
- if (E->get().in_tree) {
+ if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
}
}
}
- E->get().rc++;
+ E->value.rc++;
if (node) {
- E->get().shapes.insert(ShapePair(p_body_shape, p_area_shape));
+ E->value.shapes.insert(ShapePair(p_body_shape, p_area_shape));
}
- if (!node || E->get().in_tree) {
+ if (!node || E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape);
}
} else {
- E->get().rc--;
+ E->value.rc--;
if (node) {
- E->get().shapes.erase(ShapePair(p_body_shape, p_area_shape));
+ E->value.shapes.erase(ShapePair(p_body_shape, p_area_shape));
}
- bool in_tree = E->get().in_tree;
- if (E->get().rc == 0) {
- body_map.erase(E);
+ bool in_tree = E->value.in_tree;
+ if (E->value.rc == 0) {
+ body_map.remove(E);
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree));
@@ -231,14 +231,14 @@ void Area2D::_area_enter_tree(ObjectID p_id) {
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
- Map<ObjectID, AreaState>::Element *E = area_map.find(p_id);
+ HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(E->get().in_tree);
+ ERR_FAIL_COND(E->value.in_tree);
- E->get().in_tree = true;
+ E->value.in_tree = true;
emit_signal(SceneStringNames::get_singleton()->area_entered, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape);
}
}
@@ -246,13 +246,13 @@ void Area2D::_area_exit_tree(ObjectID p_id) {
Object *obj = ObjectDB::get_instance(p_id);
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
- Map<ObjectID, AreaState>::Element *E = area_map.find(p_id);
+ HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(!E->get().in_tree);
- E->get().in_tree = false;
+ ERR_FAIL_COND(!E->value.in_tree);
+ E->value.in_tree = false;
emit_signal(SceneStringNames::get_singleton()->area_exited, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape);
}
}
@@ -263,7 +263,7 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i
Object *obj = ObjectDB::get_instance(objid);
Node *node = Object::cast_to<Node>(obj);
- Map<ObjectID, AreaState>::Element *E = area_map.find(objid);
+ HashMap<ObjectID, AreaState>::Iterator E = area_map.find(objid);
if (!area_in && !E) {
return; //likely removed from the tree
@@ -273,36 +273,36 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i
if (area_in) {
if (!E) {
E = area_map.insert(objid, AreaState());
- E->get().rid = p_area;
- E->get().rc = 0;
- E->get().in_tree = node && node->is_inside_tree();
+ E->value.rid = p_area;
+ 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));
- if (E->get().in_tree) {
+ if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_entered, node);
}
}
}
- E->get().rc++;
+ E->value.rc++;
if (node) {
- E->get().shapes.insert(AreaShapePair(p_area_shape, p_self_shape));
+ E->value.shapes.insert(AreaShapePair(p_area_shape, p_self_shape));
}
- if (!node || E->get().in_tree) {
+ if (!node || E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, node, p_area_shape, p_self_shape);
}
} else {
- E->get().rc--;
+ E->value.rc--;
if (node) {
- E->get().shapes.erase(AreaShapePair(p_area_shape, p_self_shape));
+ E->value.shapes.erase(AreaShapePair(p_area_shape, p_self_shape));
}
- bool in_tree = E->get().in_tree;
- if (E->get().rc == 0) {
- area_map.erase(E);
+ bool in_tree = E->value.in_tree;
+ if (E->value.rc == 0) {
+ area_map.remove(E);
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree));
@@ -323,7 +323,7 @@ void Area2D::_clear_monitoring() {
ERR_FAIL_COND_MSG(locked, "This function can't be used during the in/out signal.");
{
- Map<ObjectID, BodyState> bmcopy = body_map;
+ HashMap<ObjectID, BodyState> bmcopy = body_map;
body_map.clear();
//disconnect all monitored stuff
@@ -351,7 +351,7 @@ void Area2D::_clear_monitoring() {
}
{
- Map<ObjectID, AreaState> bmcopy = area_map;
+ HashMap<ObjectID, AreaState> bmcopy = area_map;
area_map.clear();
//disconnect all monitored stuff
@@ -461,20 +461,20 @@ TypedArray<Area2D> Area2D::get_overlapping_areas() const {
bool Area2D::overlaps_area(Node *p_area) const {
ERR_FAIL_NULL_V(p_area, false);
- const Map<ObjectID, AreaState>::Element *E = area_map.find(p_area->get_instance_id());
+ HashMap<ObjectID, AreaState>::ConstIterator E = area_map.find(p_area->get_instance_id());
if (!E) {
return false;
}
- return E->get().in_tree;
+ return E->value.in_tree;
}
bool Area2D::overlaps_body(Node *p_body) const {
ERR_FAIL_NULL_V(p_body, false);
- const Map<ObjectID, BodyState>::Element *E = body_map.find(p_body->get_instance_id());
+ HashMap<ObjectID, BodyState>::ConstIterator E = body_map.find(p_body->get_instance_id());
if (!E) {
return false;
}
- return E->get().in_tree;
+ return E->value.in_tree;
}
void Area2D::set_audio_bus_override(bool p_override) {
diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h
index 98ba270a61..a584420ced 100644
--- a/scene/2d/area_2d.h
+++ b/scene/2d/area_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,7 +49,7 @@ public:
private:
SpaceOverride gravity_space_override = SPACE_OVERRIDE_DISABLED;
Vector2 gravity_vec;
- real_t gravity;
+ real_t gravity = 0.0;
bool gravity_is_point = false;
real_t gravity_distance_scale = 0.0;
@@ -94,7 +94,7 @@ private:
VSet<ShapePair> shapes;
};
- Map<ObjectID, BodyState> body_map;
+ HashMap<ObjectID, BodyState> body_map;
void _area_inout(int p_status, const RID &p_area, ObjectID p_instance, int p_area_shape, int p_self_shape);
@@ -126,7 +126,7 @@ private:
VSet<AreaShapePair> shapes;
};
- Map<ObjectID, AreaState> area_map;
+ HashMap<ObjectID, AreaState> area_map;
void _clear_monitoring();
bool audio_bus_override = false;
diff --git a/scene/2d/audio_listener_2d.cpp b/scene/2d/audio_listener_2d.cpp
index f16e359a1d..eb463864e1 100644
--- a/scene/2d/audio_listener_2d.cpp
+++ b/scene/2d/audio_listener_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -67,6 +67,7 @@ void AudioListener2D::_notification(int p_what) {
make_current();
}
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (!get_tree()->is_node_being_edited(this)) {
if (is_current()) {
diff --git a/scene/2d/audio_listener_2d.h b/scene/2d/audio_listener_2d.h
index 454053bc4a..172d388efc 100644
--- a/scene/2d/audio_listener_2d.h
+++ b/scene/2d/audio_listener_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 24da2ce9ce..624ef70468 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,72 +33,75 @@
#include "scene/2d/area_2d.h"
#include "scene/2d/audio_listener_2d.h"
#include "scene/main/window.h"
+#include "scene/resources/world_2d.h"
void AudioStreamPlayer2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this);
- if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
- play();
- }
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this);
+ if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
+ play();
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- stop();
- AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this);
- }
+ case NOTIFICATION_EXIT_TREE: {
+ stop();
+ AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this);
+ } break;
- if (p_what == NOTIFICATION_PAUSED) {
- if (!can_process()) {
- // Node can't process so we start fading out to silence
- set_stream_paused(true);
- }
- }
+ case NOTIFICATION_PAUSED: {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence.
+ set_stream_paused(true);
+ }
+ } break;
- if (p_what == NOTIFICATION_UNPAUSED) {
- set_stream_paused(false);
- }
+ case NOTIFICATION_UNPAUSED: {
+ set_stream_paused(false);
+ } break;
- if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- //update anything related to position first, if possible of course
- if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
- _update_panning();
- }
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ // Update anything related to position first, if possible of course.
+ if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
+ _update_panning();
+ }
- if (setplay.get() >= 0 && stream.is_valid()) {
- active.set();
- Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
- ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
- AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
- stream_playbacks.push_back(new_playback);
- setplay.set(-1);
- }
+ if (setplay.get() >= 0 && stream.is_valid()) {
+ active.set();
+ Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+ ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
+ AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
+ stream_playbacks.push_back(new_playback);
+ setplay.set(-1);
+ }
- if (!stream_playbacks.is_empty() && active.is_set()) {
- // Stop playing if no longer active.
- Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
- for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
- if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
- playbacks_to_remove.push_back(playback);
+ if (!stream_playbacks.is_empty() && active.is_set()) {
+ // Stop playing if no longer active.
+ Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
+ playbacks_to_remove.push_back(playback);
+ }
+ }
+ // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
+ for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
+ stream_playbacks.erase(playback);
+ }
+ if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
+ // This node is no longer actively playing audio.
+ active.clear();
+ set_physics_process_internal(false);
+ }
+ if (!playbacks_to_remove.is_empty()) {
+ emit_signal(SNAME("finished"));
}
}
- // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
- for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
- stream_playbacks.erase(playback);
- }
- if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
- // This node is no longer actively playing audio.
- active.clear();
- set_physics_process_internal(false);
- }
- if (!playbacks_to_remove.is_empty()) {
- emit_signal(SNAME("finished"));
- }
- }
- while (stream_playbacks.size() > max_polyphony) {
- AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
- stream_playbacks.remove(0);
- }
+ while (stream_playbacks.size() > max_polyphony) {
+ AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+ stream_playbacks.remove_at(0);
+ }
+ } break;
}
}
@@ -145,7 +148,7 @@ void AudioStreamPlayer2D::_update_panning() {
Vector2 global_pos = get_global_position();
- Set<Viewport *> viewports = world_2d->get_viewports();
+ RBSet<Viewport *> viewports = world_2d->get_viewports();
viewports.insert(get_viewport()); // TODO: This is a mediocre workaround for #50958. Remove when that bug is fixed!
volume_vector.resize(4);
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 5360fd4934..73b09e432f 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp
index 539a66b881..c411aaf411 100644
--- a/scene/2d/back_buffer_copy.cpp
+++ b/scene/2d/back_buffer_copy.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h
index 6bdb3aaab2..4e7cac1f3e 100644
--- a/scene/2d/back_buffer_copy.h
+++ b/scene/2d/back_buffer_copy.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index bf5671be19..f61dbc071d 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,7 @@
#include "camera_2d.h"
+#include "core/config/project_settings.h"
#include "scene/main/window.h"
void Camera2D::_update_scroll() {
@@ -56,7 +57,7 @@ void Camera2D::_update_scroll() {
Size2 screen_size = _get_camera_screen_size();
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform, screen_offset);
+ get_tree()->call_group(group_name, "_camera_moved", xform, screen_offset);
};
}
@@ -78,6 +79,7 @@ void Camera2D::set_zoom(const Vector2 &p_zoom) {
ERR_FAIL_COND_MSG(Math::is_zero_approx(p_zoom.x) || Math::is_zero_approx(p_zoom.y), "Zoom level must be different from 0 (can be negative).");
zoom = p_zoom;
+ zoom_scale = Vector2(1, 1) / zoom;
Point2 old_smoothed_camera_pos = smoothed_camera_pos;
_update_scroll();
smoothed_camera_pos = old_smoothed_camera_pos;
@@ -102,8 +104,8 @@ Transform2D Camera2D::get_camera_transform() {
if (!first) {
if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
if (drag_horizontal_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_horizontal_offset_changed) {
- camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom.x * drag_margin[SIDE_LEFT]));
- camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom.x * drag_margin[SIDE_RIGHT]));
+ camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_LEFT]));
+ camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_RIGHT]));
} else {
if (drag_horizontal_offset < 0) {
camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_RIGHT] * drag_horizontal_offset;
@@ -115,8 +117,8 @@ Transform2D Camera2D::get_camera_transform() {
}
if (drag_vertical_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_vertical_offset_changed) {
- camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom.y * drag_margin[SIDE_TOP]));
- camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom.y * drag_margin[SIDE_BOTTOM]));
+ camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_TOP]));
+ camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_BOTTOM]));
} else {
if (drag_vertical_offset < 0) {
@@ -132,8 +134,8 @@ Transform2D Camera2D::get_camera_transform() {
camera_pos = new_camera_pos;
}
- Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom) : Point2());
- Rect2 screen_rect(-screen_offset + camera_pos, screen_size * zoom);
+ Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom_scale) : Point2());
+ Rect2 screen_rect(-screen_offset + camera_pos, screen_size * zoom_scale);
if (limit_smoothing_enabled) {
if (screen_rect.position.x < limit[SIDE_LEFT]) {
@@ -167,16 +169,16 @@ Transform2D Camera2D::get_camera_transform() {
first = false;
}
- Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom) : Point2());
+ Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom_scale) : Point2());
real_t angle = get_global_rotation();
if (rotating) {
screen_offset = screen_offset.rotated(angle);
}
- Rect2 screen_rect(-screen_offset + ret_camera_pos, screen_size * zoom);
+ Rect2 screen_rect(-screen_offset + ret_camera_pos, screen_size * zoom_scale);
- if (!limit_smoothing_enabled) {
+ if (!smoothing_enabled || !limit_smoothing_enabled) {
if (screen_rect.position.x < limit[SIDE_LEFT]) {
screen_rect.position.x = limit[SIDE_LEFT];
}
@@ -201,19 +203,11 @@ Transform2D Camera2D::get_camera_transform() {
camera_screen_center = screen_rect.get_center();
Transform2D xform;
- xform.scale_basis(zoom);
+ xform.scale_basis(zoom_scale);
if (rotating) {
xform.set_rotation(angle);
}
- xform.set_origin(screen_rect.position /*.floor()*/);
-
- /*
- if (0) {
- xform = get_global_transform() * xform;
- } else {
- xform.elements[2]+=get_global_transform().get_origin();
- }
-*/
+ xform.set_origin(screen_rect.position);
return (xform).affine_inverse();
}
@@ -223,14 +217,14 @@ void Camera2D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PROCESS:
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
_update_scroll();
-
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
if (!is_processing_internal() && !is_physics_processing_internal()) {
_update_scroll();
}
-
} break;
+
case NOTIFICATION_ENTER_TREE: {
ERR_FAIL_COND(!is_inside_tree());
if (custom_viewport && ObjectDB::get_instance(custom_viewport_id)) {
@@ -255,8 +249,8 @@ void Camera2D::_notification(int p_what) {
_update_process_callback();
_update_scroll();
first = true;
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (is_current()) {
if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) {
@@ -268,8 +262,8 @@ void Camera2D::_notification(int p_what) {
remove_from_group(group_name);
remove_from_group(canvas_group_name);
viewport = nullptr;
-
} break;
+
#ifdef TOOLS_ENABLED
case NOTIFICATION_DRAW: {
if (!is_inside_tree() || !Engine::get_singleton()->is_editor_hint()) {
@@ -427,7 +421,7 @@ bool Camera2D::is_current() const {
void Camera2D::make_current() {
if (is_inside_tree()) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", this);
+ get_tree()->call_group(group_name, "_make_current", this);
} else {
current = true;
}
@@ -436,7 +430,7 @@ void Camera2D::make_current() {
void Camera2D::clear_current() {
if (is_inside_tree()) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", (Object *)nullptr);
+ get_tree()->call_group(group_name, "_make_current", (Object *)nullptr);
} else {
current = false;
}
@@ -530,7 +524,7 @@ Point2 Camera2D::get_camera_screen_center() const {
Size2 Camera2D::_get_camera_screen_size() const {
// special case if the camera2D is in the root viewport
if (Engine::get_singleton()->is_editor_hint() && get_viewport()->get_parent_viewport() == get_tree()->get_root()) {
- return Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ return Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
}
return get_viewport_rect().size;
}
diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h
index d697515547..294a6fcb80 100644
--- a/scene/2d/camera_2d.h
+++ b/scene/2d/camera_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -61,6 +61,7 @@ protected:
RID canvas;
Vector2 offset;
Vector2 zoom = Vector2(1, 1);
+ Vector2 zoom_scale = Vector2(1, 1);
AnchorMode anchor_mode = ANCHOR_MODE_DRAG_CENTER;
bool rotating = false;
bool current = false;
diff --git a/scene/2d/canvas_group.cpp b/scene/2d/canvas_group.cpp
index ee025b6dfc..37a858330c 100644
--- a/scene/2d/canvas_group.cpp
+++ b/scene/2d/canvas_group.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/canvas_group.h b/scene/2d/canvas_group.h
index b487d7a098..9bc1772ee2 100644
--- a/scene/2d/canvas_group.h
+++ b/scene/2d/canvas_group.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index 4de99959a3..61a17a4845 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,27 +31,32 @@
#include "canvas_modulate.h"
void CanvasModulate::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_CANVAS) {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
- add_to_group("_canvas_modulate_" + itos(get_canvas().get_id()));
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_CANVAS: {
+ if (is_visible_in_tree()) {
+ RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
+ add_to_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ }
+ } break;
- } else if (p_what == NOTIFICATION_EXIT_CANVAS) {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1));
- remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id()));
- }
- } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- if (is_visible_in_tree()) {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
- add_to_group("_canvas_modulate_" + itos(get_canvas().get_id()));
- } else {
- RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1));
- remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id()));
- }
+ case NOTIFICATION_EXIT_CANVAS: {
+ if (is_visible_in_tree()) {
+ RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1));
+ remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ }
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (is_visible_in_tree()) {
+ RS::get_singleton()->canvas_set_modulate(get_canvas(), color);
+ add_to_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ } else {
+ RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1));
+ remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id()));
+ }
- update_configuration_warnings();
+ update_configuration_warnings();
+ } break;
}
}
@@ -81,7 +86,7 @@ TypedArray<String> CanvasModulate::get_configuration_warnings() const {
get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes);
if (nodes.size() > 1) {
- warnings.push_back(TTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored."));
+ warnings.push_back(RTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored."));
}
}
diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h
index 3d85a92a11..ec37449f8f 100644
--- a/scene/2d/canvas_modulate.h
+++ b/scene/2d/canvas_modulate.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index 4b348f12e6..a8c12f4893 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,7 @@
#include "collision_object_2d.h"
+#include "scene/resources/world_2d.h"
#include "scene/scene_string_names.h"
void CollisionObject2D::_notification(int p_what) {
@@ -268,7 +269,7 @@ uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) {
id = shapes.back()->key() + 1;
}
- sd.owner = p_owner;
+ sd.owner_id = p_owner ? p_owner->get_instance_id() : ObjectID();
shapes[id] = sd;
@@ -382,7 +383,7 @@ Transform2D CollisionObject2D::shape_owner_get_transform(uint32_t p_owner) const
Object *CollisionObject2D::shape_owner_get_owner(uint32_t p_owner) const {
ERR_FAIL_COND_V(!shapes.has(p_owner), nullptr);
- return shapes[p_owner].owner;
+ return ObjectDB::get_instance(shapes[p_owner].owner_id);
}
void CollisionObject2D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2D> &p_shape) {
@@ -434,7 +435,7 @@ void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int p_shape)
PhysicsServer2D::get_singleton()->body_remove_shape(rid, index_to_remove);
}
- shapes[p_owner].shapes.remove(p_shape);
+ shapes[p_owner].shapes.remove_at(p_shape);
for (KeyValue<uint32_t, ShapeData> &E : shapes) {
for (int i = 0; i < E.value.shapes.size(); i++) {
@@ -557,7 +558,7 @@ TypedArray<String> CollisionObject2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (shapes.is_empty()) {
- warnings.push_back(TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."));
+ warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."));
}
return warnings;
@@ -605,7 +606,7 @@ void CollisionObject2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("mouse_shape_entered", PropertyInfo(Variant::INT, "shape_idx")));
ADD_SIGNAL(MethodInfo("mouse_shape_exited", PropertyInfo(Variant::INT, "shape_idx")));
- ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,Make Static,Keep Active"), "set_disable_mode", "get_disable_mode");
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index 19abacb201..997afee6c4 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -59,7 +59,7 @@ private:
PhysicsServer2D::BodyMode body_mode = PhysicsServer2D::BODY_MODE_STATIC;
struct ShapeData {
- Object *owner = nullptr;
+ ObjectID owner_id;
Transform2D xform;
struct Shape {
Ref<Shape2D> shape;
@@ -75,7 +75,7 @@ private:
int total_subshapes = 0;
- Map<uint32_t, ShapeData> shapes;
+ RBMap<uint32_t, ShapeData> shapes;
bool only_update_transform_changes = false; // This is used for sync to physics.
void _apply_disabled();
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 00bfa62449..20840f5aea 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,6 +32,7 @@
#include "collision_object_2d.h"
#include "core/math/geometry_2d.h"
+#include "scene/2d/area_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
@@ -102,26 +103,20 @@ void CollisionPolygon2D::_notification(int p_what) {
_build_polygon();
_update_in_shape_owner();
}
-
- /*if (Engine::get_singleton()->is_editor_hint()) {
- //display above all else
- set_z_as_relative(false);
- set_z_index(RS::CANVAS_ITEM_Z_MAX - 1);
- }*/
-
} break;
+
case NOTIFICATION_ENTER_TREE: {
if (parent) {
_update_in_shape_owner();
}
-
} break;
+
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
if (parent) {
_update_in_shape_owner(true);
}
-
} break;
+
case NOTIFICATION_UNPARENTED: {
if (parent) {
parent->remove_shape_owner(owner_id);
@@ -164,15 +159,15 @@ void CollisionPolygon2D::_notification(int p_what) {
dcol.a = 1.0;
Vector2 line_to(0, 20);
draw_line(Vector2(), line_to, dcol, 3);
- Vector<Vector2> pts;
real_t tsize = 8;
- pts.push_back(line_to + (Vector2(0, tsize)));
- pts.push_back(line_to + (Vector2(Math_SQRT12 * tsize, 0)));
- pts.push_back(line_to + (Vector2(-Math_SQRT12 * tsize, 0)));
- Vector<Color> cols;
- for (int i = 0; i < 3; i++) {
- cols.push_back(dcol);
- }
+
+ Vector<Vector2> pts = {
+ line_to + Vector2(0, tsize),
+ line_to + Vector2(Math_SQRT12 * tsize, 0),
+ line_to + Vector2(-Math_SQRT12 * tsize, 0)
+ };
+
+ Vector<Color> cols{ dcol, dcol, dcol };
draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
}
@@ -244,22 +239,25 @@ TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
- warnings.push_back(TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape."));
+ warnings.push_back(RTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape."));
}
int polygon_count = polygon.size();
if (polygon_count == 0) {
- warnings.push_back(TTR("An empty CollisionPolygon2D has no effect on collision."));
+ warnings.push_back(RTR("An empty CollisionPolygon2D has no effect on collision."));
} else {
bool solids = build_mode == BUILD_SOLIDS;
if (solids) {
if (polygon_count < 3) {
- warnings.push_back(TTR("Invalid polygon. At least 3 points are needed in 'Solids' build mode."));
+ warnings.push_back(RTR("Invalid polygon. At least 3 points are needed in 'Solids' build mode."));
}
} else if (polygon_count < 2) {
- warnings.push_back(TTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode."));
+ warnings.push_back(RTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode."));
}
}
+ if (one_way_collision && Object::cast_to<Area2D>(get_parent())) {
+ warnings.push_back(RTR("The One Way Collision property will be ignored when the parent is an Area2D."));
+ }
return warnings;
}
@@ -282,6 +280,7 @@ void CollisionPolygon2D::set_one_way_collision(bool p_enable) {
if (parent) {
parent->shape_owner_set_one_way_collision(owner_id, p_enable);
}
+ update_configuration_warnings();
}
bool CollisionPolygon2D::is_one_way_collision_enabled() const {
diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h
index 6b32923010..e18022ab7e 100644
--- a/scene/2d/collision_polygon_2d.h
+++ b/scene/2d/collision_polygon_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index c7742c7ba5..5a25b1705a 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,7 @@
#include "collision_shape_2d.h"
#include "collision_object_2d.h"
+#include "scene/2d/area_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
@@ -59,34 +60,28 @@ void CollisionShape2D::_notification(int p_what) {
}
_update_in_shape_owner();
}
-
- /*if (Engine::get_singleton()->is_editor_hint()) {
- //display above all else
- set_z_as_relative(false);
- set_z_index(RS::CANVAS_ITEM_Z_MAX - 1);
- }*/
-
} break;
+
case NOTIFICATION_ENTER_TREE: {
if (parent) {
_update_in_shape_owner();
}
-
} break;
+
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
if (parent) {
_update_in_shape_owner(true);
}
-
} break;
+
case NOTIFICATION_UNPARENTED: {
if (parent) {
parent->remove_shape_owner(owner_id);
}
owner_id = 0;
parent = nullptr;
-
} break;
+
case NOTIFICATION_DRAW: {
ERR_FAIL_COND(!is_inside_tree());
@@ -121,15 +116,15 @@ void CollisionShape2D::_notification(int p_what) {
}
Vector2 line_to(0, 20);
draw_line(Vector2(), line_to, draw_col, 2);
- Vector<Vector2> pts;
real_t tsize = 8;
- pts.push_back(line_to + (Vector2(0, tsize)));
- pts.push_back(line_to + (Vector2(Math_SQRT12 * tsize, 0)));
- pts.push_back(line_to + (Vector2(-Math_SQRT12 * tsize, 0)));
- Vector<Color> cols;
- for (int i = 0; i < 3; i++) {
- cols.push_back(draw_col);
- }
+
+ Vector<Vector2> pts{
+ line_to + Vector2(0, tsize),
+ line_to + Vector2(Math_SQRT12 * tsize, 0),
+ line_to + Vector2(-Math_SQRT12 * tsize, 0)
+ };
+
+ Vector<Color> cols{ draw_col, draw_col, draw_col };
draw_primitive(pts, cols, Vector<Vector2>());
}
@@ -177,16 +172,19 @@ TypedArray<String> CollisionShape2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
- warnings.push_back(TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape."));
+ warnings.push_back(RTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape."));
}
if (!shape.is_valid()) {
- warnings.push_back(TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"));
+ warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"));
+ }
+ if (one_way_collision && Object::cast_to<Area2D>(get_parent())) {
+ warnings.push_back(RTR("The One Way Collision property will be ignored when the parent is an Area2D."));
}
Ref<ConvexPolygonShape2D> convex = shape;
Ref<ConcavePolygonShape2D> concave = shape;
if (convex.is_valid() || concave.is_valid()) {
- warnings.push_back(TTR("Polygon-based shapes are not meant be used nor edited directly through the CollisionShape2D node. Please use the CollisionPolygon2D node instead."));
+ warnings.push_back(RTR("Polygon-based shapes are not meant be used nor edited directly through the CollisionShape2D node. Please use the CollisionPolygon2D node instead."));
}
return warnings;
@@ -210,6 +208,7 @@ void CollisionShape2D::set_one_way_collision(bool p_enable) {
if (parent) {
parent->shape_owner_set_one_way_collision(owner_id, p_enable);
}
+ update_configuration_warnings();
}
bool CollisionShape2D::is_one_way_collision_enabled() const {
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
index eaf72627c8..dbc81e8424 100644
--- a/scene/2d/collision_shape_2d.h
+++ b/scene/2d/collision_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 80c17b6e88..f5c14b6845 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -152,11 +152,14 @@ void CPUParticles2D::_update_mesh_texture() {
} else {
tex_size = Size2(1, 1);
}
- Vector<Vector2> vertices;
- vertices.push_back(-tex_size * 0.5);
- vertices.push_back(-tex_size * 0.5 + Vector2(tex_size.x, 0));
- vertices.push_back(-tex_size * 0.5 + tex_size);
- vertices.push_back(-tex_size * 0.5 + Vector2(0, tex_size.y));
+
+ Vector<Vector2> vertices = {
+ -tex_size * 0.5,
+ -tex_size * 0.5 + Vector2(tex_size.x, 0),
+ -tex_size * 0.5 + tex_size,
+ -tex_size * 0.5 + Vector2(0, tex_size.y)
+ };
+
Vector<Vector2> uvs;
AtlasTexture *atlas_texure = Object::cast_to<AtlasTexture>(*texture);
if (atlas_texure && atlas_texure->get_atlas().is_valid()) {
@@ -172,18 +175,15 @@ void CPUParticles2D::_update_mesh_texture() {
uvs.push_back(Vector2(1, 1));
uvs.push_back(Vector2(0, 1));
}
- Vector<Color> colors;
- colors.push_back(Color(1, 1, 1, 1));
- colors.push_back(Color(1, 1, 1, 1));
- colors.push_back(Color(1, 1, 1, 1));
- colors.push_back(Color(1, 1, 1, 1));
- Vector<int> indices;
- indices.push_back(0);
- indices.push_back(1);
- indices.push_back(2);
- indices.push_back(2);
- indices.push_back(3);
- indices.push_back(0);
+
+ Vector<Color> colors = {
+ Color(1, 1, 1, 1),
+ Color(1, 1, 1, 1),
+ Color(1, 1, 1, 1),
+ Color(1, 1, 1, 1)
+ };
+
+ Vector<int> indices = { 0, 1, 2, 2, 3, 0 };
Array arr;
arr.resize(RS::ARRAY_MAX);
@@ -250,7 +250,7 @@ TypedArray<String> CPUParticles2D::get_configuration_warnings() const {
if (get_material().is_null() || (mat && !mat->get_particles_animation())) {
if (get_param_max(PARAM_ANIM_SPEED) != 0.0 || get_param_max(PARAM_ANIM_OFFSET) != 0.0 ||
get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid()) {
- warnings.push_back(TTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."));
+ warnings.push_back(RTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."));
}
}
@@ -398,6 +398,14 @@ Ref<Gradient> CPUParticles2D::get_color_ramp() const {
return color_ramp;
}
+void CPUParticles2D::set_color_initial_ramp(const Ref<Gradient> &p_ramp) {
+ color_initial_ramp = p_ramp;
+}
+
+Ref<Gradient> CPUParticles2D::get_color_initial_ramp() const {
+ return color_initial_ramp;
+}
+
void CPUParticles2D::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) {
ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX);
particle_flags[p_particle_flag] = p_enable;
@@ -492,7 +500,7 @@ bool CPUParticles2D::get_split_scale() {
}
void CPUParticles2D::_validate_property(PropertyInfo &property) const {
- if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) {
+ if (property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) {
property.usage = PROPERTY_USAGE_NONE;
}
@@ -727,9 +735,15 @@ void CPUParticles2D::_particles_process(double p_delta) {
p.hue_rot_rand = Math::randf();
p.anim_offset_rand = Math::randf();
+ if (color_initial_ramp.is_valid()) {
+ p.start_color_rand = color_initial_ramp->get_color_at_offset(Math::randf());
+ } else {
+ p.start_color_rand = Color(1, 1, 1, 1);
+ }
+
real_t angle1_rad = direction.angle() + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread);
Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad));
- p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], Math::randf());
+ p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf());
real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand);
p.rotation = Math::deg2rad(base_angle);
@@ -748,6 +762,11 @@ void CPUParticles2D::_particles_process(double p_delta) {
//do none
} break;
case EMISSION_SHAPE_SPHERE: {
+ real_t t = Math_TAU * Math::randf();
+ real_t radius = emission_sphere_radius * Math::randf();
+ p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius;
+ } break;
+ case EMISSION_SHAPE_SPHERE_SURFACE: {
real_t s = Math::randf(), t = Math_TAU * Math::randf();
real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s);
p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius;
@@ -769,8 +788,8 @@ void CPUParticles2D::_particles_process(double p_delta) {
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) {
Vector2 normal = emission_normals.get(random_idx);
Transform2D m2;
- m2.set_axis(0, normal);
- m2.set_axis(1, normal.orthogonal());
+ m2.columns[0] = normal;
+ m2.columns[1] = normal.orthogonal();
p.velocity = m2.basis_xform(p.velocity);
}
@@ -890,7 +909,7 @@ void CPUParticles2D::_particles_process(double p_delta) {
real_t base_angle = (tex_angle)*Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand);
base_angle += p.custom[1] * lifetime * tex_angular_velocity * Math::lerp(parameters_min[PARAM_ANGULAR_VELOCITY], parameters_max[PARAM_ANGULAR_VELOCITY], rand_from_seed(alt_seed));
p.rotation = Math::deg2rad(base_angle); //angle
- p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + p.custom[1] * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed));
+ p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + tv * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed));
}
//apply color
//apply hue rotation
@@ -946,17 +965,17 @@ void CPUParticles2D::_particles_process(double p_delta) {
p.color.g = color_rgb.y;
p.color.b = color_rgb.z;
- p.color *= p.base_color;
+ p.color *= p.base_color * p.start_color_rand;
if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) {
if (p.velocity.length() > 0.0) {
- p.transform.elements[1] = p.velocity.normalized();
- p.transform.elements[0] = p.transform.elements[1].orthogonal();
+ p.transform.columns[1] = p.velocity.normalized();
+ p.transform.columns[0] = p.transform.columns[1].orthogonal();
}
} else {
- p.transform.elements[0] = Vector2(Math::cos(p.rotation), -Math::sin(p.rotation));
- p.transform.elements[1] = Vector2(Math::sin(p.rotation), Math::cos(p.rotation));
+ p.transform.columns[0] = Vector2(Math::cos(p.rotation), -Math::sin(p.rotation));
+ p.transform.columns[1] = Vector2(Math::sin(p.rotation), Math::cos(p.rotation));
}
//scale by scale
@@ -967,8 +986,8 @@ void CPUParticles2D::_particles_process(double p_delta) {
if (base_scale.y < 0.00001) {
base_scale.y = 0.00001;
}
- p.transform.elements[0] *= base_scale.x;
- p.transform.elements[1] *= base_scale.y;
+ p.transform.columns[0] *= base_scale.x;
+ p.transform.columns[1] *= base_scale.y;
p.transform[2] += p.velocity * local_delta;
}
@@ -1010,14 +1029,14 @@ void CPUParticles2D::_update_particle_data_buffer() {
}
if (r[idx].active) {
- ptr[0] = t.elements[0][0];
- ptr[1] = t.elements[1][0];
+ ptr[0] = t.columns[0][0];
+ ptr[1] = t.columns[1][0];
ptr[2] = 0;
- ptr[3] = t.elements[2][0];
- ptr[4] = t.elements[0][1];
- ptr[5] = t.elements[1][1];
+ ptr[3] = t.columns[2][0];
+ ptr[4] = t.columns[0][1];
+ ptr[5] = t.columns[1][1];
ptr[6] = 0;
- ptr[7] = t.elements[2][1];
+ ptr[7] = t.columns[2][1];
} else {
memset(ptr, 0, sizeof(float) * 8);
@@ -1077,9 +1096,11 @@ void CPUParticles2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
set_process_internal(emitting);
} break;
+
case NOTIFICATION_EXIT_TREE: {
_set_redraw(false);
} break;
+
case NOTIFICATION_DRAW: {
// first update before rendering to avoid one frame delay after emitting starts
if (emitting && (time == 0)) {
@@ -1097,9 +1118,11 @@ void CPUParticles2D::_notification(int p_what) {
RS::get_singleton()->canvas_item_add_multimesh(get_canvas_item(), multimesh, texrid);
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
_update_internal();
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
inv_emission_transform = get_global_transform().affine_inverse();
@@ -1114,14 +1137,14 @@ void CPUParticles2D::_notification(int p_what) {
Transform2D t = inv_emission_transform * r[i].transform;
if (r[i].active) {
- ptr[0] = t.elements[0][0];
- ptr[1] = t.elements[1][0];
+ ptr[0] = t.columns[0][0];
+ ptr[1] = t.columns[1][0];
ptr[2] = 0;
- ptr[3] = t.elements[2][0];
- ptr[4] = t.elements[0][1];
- ptr[5] = t.elements[1][1];
+ ptr[3] = t.columns[2][0];
+ ptr[4] = t.columns[0][1];
+ ptr[5] = t.columns[1][1];
ptr[6] = 0;
- ptr[7] = t.elements[2][1];
+ ptr[7] = t.columns[2][1];
} else {
memset(ptr, 0, sizeof(float) * 8);
@@ -1173,6 +1196,11 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
set_color_ramp(gt->get_gradient());
}
+ Ref<GradientTexture1D> gti = material->get_color_initial_ramp();
+ if (gti.is_valid()) {
+ set_color_initial_ramp(gti->get_gradient());
+ }
+
set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY));
set_emission_shape(EmissionShape(material->get_emission_shape()));
@@ -1295,6 +1323,9 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles2D::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles2D::get_color_ramp);
+ ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &CPUParticles2D::set_color_initial_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &CPUParticles2D::get_color_initial_ramp);
+
ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &CPUParticles2D::set_particle_flag);
ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &CPUParticles2D::get_particle_flag);
@@ -1331,7 +1362,7 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles2D::convert_from_particles);
ADD_GROUP("Emission Shape", "emission_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Sphere Surface,Box,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "emission_rect_extents"), "set_emission_rect_extents", "get_emission_rect_extents");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "emission_points"), "set_emission_points", "get_emission_points");
@@ -1386,6 +1417,7 @@ void CPUParticles2D::_bind_methods() {
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_initial_ramp", "get_color_initial_ramp");
ADD_GROUP("Hue Variation", "hue_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION);
@@ -1420,6 +1452,7 @@ void CPUParticles2D::_bind_methods() {
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE_SURFACE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_RECTANGLE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 391f51224e..51d58723b4 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -69,6 +69,7 @@ public:
enum EmissionShape {
EMISSION_SHAPE_POINT,
EMISSION_SHAPE_SPHERE,
+ EMISSION_SHAPE_SPHERE_SURFACE,
EMISSION_SHAPE_RECTANGLE,
EMISSION_SHAPE_POINTS,
EMISSION_SHAPE_DIRECTED_POINTS,
@@ -89,6 +90,7 @@ private:
real_t scale_rand = 0.0;
real_t hue_rot_rand = 0.0;
real_t anim_offset_rand = 0.0;
+ Color start_color_rand;
double time = 0.0;
double lifetime = 0.0;
Color base_color;
@@ -135,7 +137,7 @@ private:
real_t randomness_ratio = 0.0;
double lifetime_randomness = 0.0;
double speed_scale = 1.0;
- bool local_coords;
+ bool local_coords = false;
int fixed_fps = 0;
bool fractional_delta = true;
@@ -156,6 +158,7 @@ private:
Ref<Curve> curve_parameters[PARAM_MAX];
Color color;
Ref<Gradient> color_ramp;
+ Ref<Gradient> color_initial_ramp;
bool particle_flags[PARTICLE_FLAG_MAX];
@@ -250,6 +253,9 @@ public:
void set_color_ramp(const Ref<Gradient> &p_ramp);
Ref<Gradient> get_color_ramp() const;
+ void set_color_initial_ramp(const Ref<Gradient> &p_ramp);
+ Ref<Gradient> get_color_initial_ramp() const;
+
void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable);
bool get_particle_flag(ParticleFlags p_particle_flag) const;
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index f1f4d1b769..d509d93172 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -114,8 +114,8 @@ void GPUParticles2D::set_use_local_coordinates(bool p_enable) {
void GPUParticles2D::_update_particle_emission_transform() {
Transform2D xf2d = get_global_transform();
Transform3D xf;
- xf.basis.set_axis(0, Vector3(xf2d.get_axis(0).x, xf2d.get_axis(0).y, 0));
- xf.basis.set_axis(1, Vector3(xf2d.get_axis(1).x, xf2d.get_axis(1).y, 0));
+ xf.basis.set_column(0, Vector3(xf2d.columns[0].x, xf2d.columns[0].y, 0));
+ xf.basis.set_column(1, Vector3(xf2d.columns[1].x, xf2d.columns[1].y, 0));
xf.set_origin(Vector3(xf2d.get_origin().x, xf2d.get_origin().y, 0));
RS::get_singleton()->particles_set_emission_transform(particles, xf);
@@ -170,6 +170,13 @@ void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) {
update();
}
+#ifdef TOOLS_ENABLED
+void GPUParticles2D::set_show_visibility_rect(bool p_show_visibility_rect) {
+ show_visibility_rect = p_show_visibility_rect;
+ update();
+}
+#endif
+
bool GPUParticles2D::is_trail_enabled() const {
return trail_enabled;
}
@@ -280,15 +287,24 @@ bool GPUParticles2D::get_fractional_delta() const {
return fractional_delta;
}
+void GPUParticles2D::set_interpolate(bool p_enable) {
+ interpolate = p_enable;
+ RS::get_singleton()->particles_set_interpolate(particles, p_enable);
+}
+
+bool GPUParticles2D::get_interpolate() const {
+ return interpolate;
+}
+
TypedArray<String> GPUParticles2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose."));
+ warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose."));
}
if (process_material.is_null()) {
- warnings.push_back(TTR("A material to process the particles is not assigned, so no behavior is imprinted."));
+ warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted."));
} else {
CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr());
@@ -297,7 +313,7 @@ TypedArray<String> GPUParticles2D::get_configuration_warnings() const {
if (process &&
(process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 ||
process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) {
- warnings.push_back(TTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."));
+ warnings.push_back(RTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."));
}
}
}
@@ -328,153 +344,199 @@ Ref<Texture2D> GPUParticles2D::get_texture() const {
void GPUParticles2D::_validate_property(PropertyInfo &property) const {
}
-void GPUParticles2D::restart() {
- RS::get_singleton()->particles_restart(particles);
- RS::get_singleton()->particles_set_emitting(particles, true);
+void GPUParticles2D::emit_particle(const Transform2D &p_transform2d, const Vector2 &p_velocity2d, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
+ Transform3D transform;
+ transform.basis.set_column(0, Vector3(p_transform2d.columns[0].x, p_transform2d.columns[0].y, 0));
+ transform.basis.set_column(1, Vector3(p_transform2d.columns[1].x, p_transform2d.columns[1].y, 0));
+ transform.set_origin(Vector3(p_transform2d.get_origin().x, p_transform2d.get_origin().y, 0));
+ Vector3 velocity = Vector3(p_velocity2d.x, p_velocity2d.y, 0);
+
+ RS::get_singleton()->particles_emit(particles, transform, velocity, p_color, p_custom, p_emit_flags);
}
-void GPUParticles2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- RID texture_rid;
- Size2 size;
- if (texture.is_valid()) {
- texture_rid = texture->get_rid();
- size = texture->get_size();
- } else {
- size = Size2(1, 1);
+void GPUParticles2D::_attach_sub_emitter() {
+ Node *n = get_node_or_null(sub_emitter);
+ if (n) {
+ GPUParticles2D *sen = Object::cast_to<GPUParticles2D>(n);
+ if (sen && sen != this) {
+ RS::get_singleton()->particles_set_subemitter(particles, sen->particles);
}
+ }
+}
- if (trail_enabled) {
- RS::get_singleton()->mesh_clear(mesh);
- PackedVector2Array points;
- PackedVector2Array uvs;
- PackedInt32Array bone_indices;
- PackedFloat32Array bone_weights;
- PackedInt32Array indices;
+void GPUParticles2D::set_sub_emitter(const NodePath &p_path) {
+ if (is_inside_tree()) {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ }
- int total_segments = trail_sections * trail_section_subdivisions;
- real_t depth = size.height * trail_sections;
+ sub_emitter = p_path;
- for (int j = 0; j <= total_segments; j++) {
- real_t v = j;
- v /= total_segments;
+ if (is_inside_tree() && sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+}
- real_t y = depth * v;
- y = (depth * 0.5) - y;
+NodePath GPUParticles2D::get_sub_emitter() const {
+ return sub_emitter;
+}
- int bone = j / trail_section_subdivisions;
- real_t blend = 1.0 - real_t(j % trail_section_subdivisions) / real_t(trail_section_subdivisions);
+void GPUParticles2D::restart() {
+ RS::get_singleton()->particles_restart(particles);
+ RS::get_singleton()->particles_set_emitting(particles, true);
+}
- real_t s = size.width;
+void GPUParticles2D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ RID texture_rid;
+ Size2 size;
+ if (texture.is_valid()) {
+ texture_rid = texture->get_rid();
+ size = texture->get_size();
+ } else {
+ size = Size2(1, 1);
+ }
- points.push_back(Vector2(-s * 0.5, 0));
- points.push_back(Vector2(+s * 0.5, 0));
+ if (trail_enabled) {
+ RS::get_singleton()->mesh_clear(mesh);
+ PackedVector2Array points;
+ PackedVector2Array uvs;
+ PackedInt32Array bone_indices;
+ PackedFloat32Array bone_weights;
+ PackedInt32Array indices;
+
+ int total_segments = trail_sections * trail_section_subdivisions;
+ real_t depth = size.height * trail_sections;
+
+ for (int j = 0; j <= total_segments; j++) {
+ real_t v = j;
+ v /= total_segments;
+
+ real_t y = depth * v;
+ y = (depth * 0.5) - y;
+
+ int bone = j / trail_section_subdivisions;
+ real_t blend = 1.0 - real_t(j % trail_section_subdivisions) / real_t(trail_section_subdivisions);
+
+ real_t s = size.width;
+
+ points.push_back(Vector2(-s * 0.5, 0));
+ points.push_back(Vector2(+s * 0.5, 0));
+
+ uvs.push_back(Vector2(0, v));
+ uvs.push_back(Vector2(1, v));
+
+ for (int i = 0; i < 2; i++) {
+ bone_indices.push_back(bone);
+ bone_indices.push_back(MIN(trail_sections, bone + 1));
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+
+ bone_weights.push_back(blend);
+ bone_weights.push_back(1.0 - blend);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+ }
+
+ if (j > 0) {
+ int base = j * 2 - 2;
+ indices.push_back(base + 0);
+ indices.push_back(base + 1);
+ indices.push_back(base + 2);
+
+ indices.push_back(base + 1);
+ indices.push_back(base + 3);
+ indices.push_back(base + 2);
+ }
+ }
- uvs.push_back(Vector2(0, v));
- uvs.push_back(Vector2(1, v));
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ arr[RS::ARRAY_VERTEX] = points;
+ arr[RS::ARRAY_TEX_UV] = uvs;
+ arr[RS::ARRAY_BONES] = bone_indices;
+ arr[RS::ARRAY_WEIGHTS] = bone_weights;
+ arr[RS::ARRAY_INDEX] = indices;
+
+ RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
+
+ Vector<Transform3D> xforms;
+ for (int i = 0; i <= trail_sections; i++) {
+ Transform3D xform;
+ /*
+ xform.origin.y = depth / 2.0 - size.height * real_t(i);
+ xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y */
+ xforms.push_back(xform);
+ }
- for (int i = 0; i < 2; i++) {
- bone_indices.push_back(bone);
- bone_indices.push_back(MIN(trail_sections, bone + 1));
- bone_indices.push_back(0);
- bone_indices.push_back(0);
+ RS::get_singleton()->particles_set_trail_bind_poses(particles, xforms);
- bone_weights.push_back(blend);
- bone_weights.push_back(1.0 - blend);
- bone_weights.push_back(0);
- bone_weights.push_back(0);
- }
+ } else {
+ RS::get_singleton()->mesh_clear(mesh);
- if (j > 0) {
- int base = j * 2 - 2;
- indices.push_back(base + 0);
- indices.push_back(base + 1);
- indices.push_back(base + 2);
+ Vector<Vector2> points = {
+ Vector2(-size.x / 2.0, -size.y / 2.0),
+ Vector2(size.x / 2.0, -size.y / 2.0),
+ Vector2(size.x / 2.0, size.y / 2.0),
+ Vector2(-size.x / 2.0, size.y / 2.0)
+ };
- indices.push_back(base + 1);
- indices.push_back(base + 3);
- indices.push_back(base + 2);
- }
- }
+ Vector<Vector2> uvs = {
+ Vector2(0, 0),
+ Vector2(1, 0),
+ Vector2(1, 1),
+ Vector2(0, 1)
+ };
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- arr[RS::ARRAY_VERTEX] = points;
- arr[RS::ARRAY_TEX_UV] = uvs;
- arr[RS::ARRAY_BONES] = bone_indices;
- arr[RS::ARRAY_WEIGHTS] = bone_weights;
- arr[RS::ARRAY_INDEX] = indices;
-
- RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
-
- Vector<Transform3D> xforms;
- for (int i = 0; i <= trail_sections; i++) {
- Transform3D xform;
- /*
- xform.origin.y = depth / 2.0 - size.height * real_t(i);
- xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y */
- xforms.push_back(xform);
- }
+ Vector<int> indices = { 0, 1, 2, 0, 2, 3 };
- RS::get_singleton()->particles_set_trail_bind_poses(particles, xforms);
-
- } else {
- RS::get_singleton()->mesh_clear(mesh);
- Vector<Vector2> points;
- points.resize(4);
- points.write[0] = Vector2(-size.x / 2.0, -size.y / 2.0);
- points.write[1] = Vector2(size.x / 2.0, -size.y / 2.0);
- points.write[2] = Vector2(size.x / 2.0, size.y / 2.0);
- points.write[3] = Vector2(-size.x / 2.0, size.y / 2.0);
- Vector<Vector2> uvs;
- uvs.resize(4);
- uvs.write[0] = Vector2(0, 0);
- uvs.write[1] = Vector2(1, 0);
- uvs.write[2] = Vector2(1, 1);
- uvs.write[3] = Vector2(0, 1);
- Vector<int> indices;
- indices.resize(6);
- indices.write[0] = 0;
- indices.write[1] = 1;
- indices.write[2] = 2;
- indices.write[3] = 0;
- indices.write[4] = 2;
- indices.write[5] = 3;
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- arr[RS::ARRAY_VERTEX] = points;
- arr[RS::ARRAY_TEX_UV] = uvs;
- arr[RS::ARRAY_INDEX] = indices;
-
- RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
- RS::get_singleton()->particles_set_trail_bind_poses(particles, Vector<Transform3D>());
- }
- RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid);
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ arr[RS::ARRAY_VERTEX] = points;
+ arr[RS::ARRAY_TEX_UV] = uvs;
+ arr[RS::ARRAY_INDEX] = indices;
+
+ RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
+ RS::get_singleton()->particles_set_trail_bind_poses(particles, Vector<Transform3D>());
+ }
+ RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid);
#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) {
- draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
- }
+ if (show_visibility_rect) {
+ draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
+ }
#endif
- }
+ } break;
- if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
- if (can_process()) {
- RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
- } else {
- RS::get_singleton()->particles_set_speed_scale(particles, 0);
- }
- }
+ case NOTIFICATION_ENTER_TREE: {
+ if (sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ } break;
+
+ case NOTIFICATION_PAUSED:
+ case NOTIFICATION_UNPAUSED: {
+ if (can_process()) {
+ RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
+ } else {
+ RS::get_singleton()->particles_set_speed_scale(particles, 0);
+ }
+ } break;
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- _update_particle_emission_transform();
- }
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ _update_particle_emission_transform();
+ } break;
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- if (one_shot && !is_emitting()) {
- notify_property_list_changed();
- set_process_internal(false);
- }
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (one_shot && !is_emitting()) {
+ notify_property_list_changed();
+ set_process_internal(false);
+ }
+ } break;
}
}
@@ -490,6 +552,7 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &GPUParticles2D::set_use_local_coordinates);
ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &GPUParticles2D::set_fixed_fps);
ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &GPUParticles2D::set_fractional_delta);
+ ClassDB::bind_method(D_METHOD("set_interpolate", "enable"), &GPUParticles2D::set_interpolate);
ClassDB::bind_method(D_METHOD("set_process_material", "material"), &GPUParticles2D::set_process_material);
ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &GPUParticles2D::set_speed_scale);
ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles2D::set_collision_base_size);
@@ -505,6 +568,7 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &GPUParticles2D::get_use_local_coordinates);
ClassDB::bind_method(D_METHOD("get_fixed_fps"), &GPUParticles2D::get_fixed_fps);
ClassDB::bind_method(D_METHOD("get_fractional_delta"), &GPUParticles2D::get_fractional_delta);
+ ClassDB::bind_method(D_METHOD("get_interpolate"), &GPUParticles2D::get_interpolate);
ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles2D::get_process_material);
ClassDB::bind_method(D_METHOD("get_speed_scale"), &GPUParticles2D::get_speed_scale);
ClassDB::bind_method(D_METHOD("get_collision_base_size"), &GPUParticles2D::get_collision_base_size);
@@ -519,6 +583,11 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("restart"), &GPUParticles2D::restart);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter", "path"), &GPUParticles2D::set_sub_emitter);
+ ClassDB::bind_method(D_METHOD("get_sub_emitter"), &GPUParticles2D::get_sub_emitter);
+
+ ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles2D::emit_particle);
+
ClassDB::bind_method(D_METHOD("set_trail_enabled", "enabled"), &GPUParticles2D::set_trail_enabled);
ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles2D::set_trail_length);
@@ -534,6 +603,7 @@ void GPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles2D"), "set_sub_emitter", "get_sub_emitter");
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
@@ -542,6 +612,7 @@ void GPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size");
@@ -562,6 +633,12 @@ void GPUParticles2D::_bind_methods() {
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
BIND_ENUM_CONSTANT(DRAW_ORDER_REVERSE_LIFETIME);
+
+ BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_ROTATION_SCALE);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
}
GPUParticles2D::GPUParticles2D() {
@@ -579,6 +656,7 @@ GPUParticles2D::GPUParticles2D() {
set_lifetime(1);
set_fixed_fps(0);
set_fractional_delta(true);
+ set_interpolate(true);
set_pre_process_time(0);
set_explosiveness_ratio(0);
set_randomness_ratio(0);
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index d7eee461b4..3c7f4cd9b5 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,18 +47,21 @@ public:
private:
RID particles;
- bool one_shot;
- int amount;
- double lifetime;
- double pre_process_time;
- real_t explosiveness_ratio;
- real_t randomness_ratio;
- double speed_scale;
+ bool one_shot = false;
+ int amount = 0;
+ double lifetime = 0.0;
+ double pre_process_time = 0.0;
+ real_t explosiveness_ratio = 0.0;
+ real_t randomness_ratio = 0.0;
+ double speed_scale = 0.0;
Rect2 visibility_rect;
- bool local_coords;
- int fixed_fps;
- bool fractional_delta;
-
+ bool local_coords = false;
+ int fixed_fps = 0;
+ bool fractional_delta = false;
+ bool interpolate = true;
+#ifdef TOOLS_ENABLED
+ bool show_visibility_rect = false;
+#endif
Ref<Material> process_material;
DrawOrder draw_order;
@@ -77,11 +80,12 @@ private:
RID mesh;
+ void _attach_sub_emitter();
+
protected:
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
void _notification(int p_what);
-
void _update_collision_size();
public:
@@ -102,6 +106,10 @@ public:
void set_trail_sections(int p_sections);
void set_trail_section_subdivisions(int p_subdivisions);
+#ifdef TOOLS_ENABLED
+ void set_show_visibility_rect(bool p_show_visibility_rect);
+#endif
+
bool is_emitting() const;
int get_amount() const;
double get_lifetime() const;
@@ -126,6 +134,9 @@ public:
void set_fractional_delta(bool p_enable);
bool get_fractional_delta() const;
+ void set_interpolate(bool p_enable);
+ bool get_interpolate() const;
+
void set_draw_order(DrawOrder p_order);
DrawOrder get_draw_order() const;
@@ -134,6 +145,19 @@ public:
TypedArray<String> get_configuration_warnings() const override;
+ void set_sub_emitter(const NodePath &p_path);
+ NodePath get_sub_emitter() const;
+
+ enum EmitFlags {
+ EMIT_FLAG_POSITION = RS::PARTICLES_EMIT_FLAG_POSITION,
+ EMIT_FLAG_ROTATION_SCALE = RS::PARTICLES_EMIT_FLAG_ROTATION_SCALE,
+ EMIT_FLAG_VELOCITY = RS::PARTICLES_EMIT_FLAG_VELOCITY,
+ EMIT_FLAG_COLOR = RS::PARTICLES_EMIT_FLAG_COLOR,
+ EMIT_FLAG_CUSTOM = RS::PARTICLES_EMIT_FLAG_CUSTOM
+ };
+
+ void emit_particle(const Transform2D &p_transform, const Vector2 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
+
void restart();
Rect2 capture_rect() const;
GPUParticles2D();
@@ -141,5 +165,6 @@ public:
};
VARIANT_ENUM_CAST(GPUParticles2D::DrawOrder)
+VARIANT_ENUM_CAST(GPUParticles2D::EmitFlags)
#endif // PARTICLES_2D_H
diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp
index 8a528151cf..cd82b47333 100644
--- a/scene/2d/joint_2d.cpp
+++ b/scene/2d/joint_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -77,15 +77,15 @@ void Joint2D::_update_joint(bool p_only_free) {
bool valid = false;
if (node_a && !body_a && node_b && !body_b) {
- warning = TTR("Node A and Node B must be PhysicsBody2Ds");
+ warning = RTR("Node A and Node B must be PhysicsBody2Ds");
} else if (node_a && !body_a) {
- warning = TTR("Node A must be a PhysicsBody2D");
+ warning = RTR("Node A must be a PhysicsBody2D");
} else if (node_b && !body_b) {
- warning = TTR("Node B must be a PhysicsBody2D");
+ warning = RTR("Node B must be a PhysicsBody2D");
} else if (!body_a || !body_b) {
- warning = TTR("Joint is not connected to two PhysicsBody2Ds");
+ warning = RTR("Joint is not connected to two PhysicsBody2Ds");
} else if (body_a == body_b) {
- warning = TTR("Node A and Node B must be different PhysicsBody2Ds");
+ warning = RTR("Node A and Node B must be different PhysicsBody2Ds");
} else {
warning = String();
valid = true;
@@ -128,7 +128,7 @@ void Joint2D::set_node_a(const NodePath &p_node_a) {
return;
}
- if (joint.is_valid()) {
+ if (is_configured()) {
_disconnect_signals();
}
@@ -145,7 +145,7 @@ void Joint2D::set_node_b(const NodePath &p_node_b) {
return;
}
- if (joint.is_valid()) {
+ if (is_configured()) {
_disconnect_signals();
}
@@ -159,14 +159,18 @@ NodePath Joint2D::get_node_b() const {
void Joint2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
+ case NOTIFICATION_POST_ENTER_TREE: {
+ if (is_configured()) {
+ _disconnect_signals();
+ }
_update_joint();
} break;
+
case NOTIFICATION_EXIT_TREE: {
- if (joint.is_valid()) {
+ if (is_configured()) {
_disconnect_signals();
- _update_joint(true);
}
+ _update_joint(true);
} break;
}
}
@@ -186,7 +190,9 @@ void Joint2D::set_exclude_nodes_from_collision(bool p_enable) {
if (exclude_from_collision == p_enable) {
return;
}
-
+ if (is_configured()) {
+ _disconnect_signals();
+ }
_update_joint(true);
exclude_from_collision = p_enable;
_update_joint();
diff --git a/scene/2d/joint_2d.h b/scene/2d/joint_2d.h
index 0c3956e463..e3cd600cbd 100644
--- a/scene/2d/joint_2d.h
+++ b/scene/2d/joint_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 66c0f979ae..b09b1b5047 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -192,21 +192,24 @@ Light2D::BlendMode Light2D::get_blend_mode() const {
}
void Light2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- RS::get_singleton()->canvas_light_attach_to_canvas(canvas_light, get_canvas());
- _update_light_visibility();
- }
-
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- RS::get_singleton()->canvas_light_set_transform(canvas_light, get_global_transform());
- }
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- _update_light_visibility();
- }
-
- if (p_what == NOTIFICATION_EXIT_TREE) {
- RS::get_singleton()->canvas_light_attach_to_canvas(canvas_light, RID());
- _update_light_visibility();
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ RS::get_singleton()->canvas_light_attach_to_canvas(canvas_light, get_canvas());
+ _update_light_visibility();
+ } break;
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ RS::get_singleton()->canvas_light_set_transform(canvas_light, get_global_transform());
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ _update_light_visibility();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ RS::get_singleton()->canvas_light_attach_to_canvas(canvas_light, RID());
+ _update_light_visibility();
+ } break;
}
}
@@ -391,7 +394,7 @@ TypedArray<String> PointLight2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!texture.is_valid()) {
- warnings.push_back(TTR("A texture with the shape of the light must be supplied to the \"Texture\" property."));
+ warnings.push_back(RTR("A texture with the shape of the light must be supplied to the \"Texture\" property."));
}
return warnings;
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index d9ecd81f1c..f7b1f420e3 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index fdc28f81c2..14188d7120 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,9 +29,9 @@
/*************************************************************************/
#include "light_occluder_2d.h"
-#include "core/math/geometry_2d.h"
#include "core/config/engine.h"
+#include "core/math/geometry_2d.h"
#define LINE_GRAB_WIDTH 8
@@ -158,42 +158,46 @@ void LightOccluder2D::_poly_changed() {
}
void LightOccluder2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_CANVAS) {
- RS::get_singleton()->canvas_light_occluder_attach_to_canvas(occluder, get_canvas());
- RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform());
- RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, is_visible_in_tree());
- }
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform());
- }
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, is_visible_in_tree());
- }
-
- if (p_what == NOTIFICATION_DRAW) {
- if (Engine::get_singleton()->is_editor_hint()) {
- if (occluder_polygon.is_valid()) {
- Vector<Vector2> poly = occluder_polygon->get_polygon();
-
- if (poly.size()) {
- if (occluder_polygon->is_closed()) {
- Vector<Color> color;
- color.push_back(Color(0, 0, 0, 0.6));
- draw_polygon(Variant(poly), color);
- } else {
- int ps = poly.size();
- const Vector2 *r = poly.ptr();
- for (int i = 0; i < ps - 1; i++) {
- draw_line(r[i], r[i + 1], Color(0, 0, 0, 0.6), 3);
+ switch (p_what) {
+ case NOTIFICATION_ENTER_CANVAS: {
+ RS::get_singleton()->canvas_light_occluder_attach_to_canvas(occluder, get_canvas());
+ RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform());
+ RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, is_visible_in_tree());
+ } break;
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform());
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, is_visible_in_tree());
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (occluder_polygon.is_valid()) {
+ Vector<Vector2> poly = occluder_polygon->get_polygon();
+
+ if (poly.size()) {
+ if (occluder_polygon->is_closed()) {
+ Vector<Color> color;
+ color.push_back(Color(0, 0, 0, 0.6));
+ draw_polygon(Variant(poly), color);
+ } else {
+ int ps = poly.size();
+ const Vector2 *r = poly.ptr();
+ for (int i = 0; i < ps - 1; i++) {
+ draw_line(r[i], r[i + 1], Color(0, 0, 0, 0.6), 3);
+ }
}
}
}
}
- }
- }
+ } break;
- if (p_what == NOTIFICATION_EXIT_CANVAS) {
- RS::get_singleton()->canvas_light_occluder_attach_to_canvas(occluder, RID());
+ case NOTIFICATION_EXIT_CANVAS: {
+ RS::get_singleton()->canvas_light_occluder_attach_to_canvas(occluder, RID());
+ } break;
}
}
@@ -246,11 +250,11 @@ TypedArray<String> LightOccluder2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!occluder_polygon.is_valid()) {
- warnings.push_back(TTR("An occluder polygon must be set (or drawn) for this occluder to take effect."));
+ warnings.push_back(RTR("An occluder polygon must be set (or drawn) for this occluder to take effect."));
}
if (occluder_polygon.is_valid() && occluder_polygon->get_polygon().size() == 0) {
- warnings.push_back(TTR("The occluder polygon for this occluder is empty. Please draw a polygon."));
+ warnings.push_back(RTR("The occluder polygon for this occluder is empty. Please draw a polygon."));
}
return warnings;
@@ -281,7 +285,6 @@ void LightOccluder2D::_bind_methods() {
LightOccluder2D::LightOccluder2D() {
occluder = RS::get_singleton()->canvas_light_occluder_create();
- mask = 1;
set_notify_transform(true);
set_as_sdf_collision(true);
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index b4a48d1062..4acfeaf781 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -81,10 +81,9 @@ class LightOccluder2D : public Node2D {
GDCLASS(LightOccluder2D, Node2D);
RID occluder;
- bool enabled;
- int mask;
+ int mask = 1;
Ref<OccluderPolygon2D> occluder_polygon;
- bool sdf_collision;
+ bool sdf_collision = false;
void _poly_changed();
protected:
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 37eb45c21d..8cbcc9acf6 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -133,7 +133,7 @@ int Line2D::get_point_count() const {
void Line2D::clear_points() {
int count = _points.size();
if (count > 0) {
- _points.resize(0);
+ _points.clear();
update();
}
}
@@ -148,7 +148,7 @@ void Line2D::add_point(Vector2 p_pos, int p_atpos) {
}
void Line2D::remove_point(int i) {
- _points.remove(i);
+ _points.remove_at(i);
update();
}
@@ -228,9 +228,9 @@ Line2D::LineCapMode Line2D::get_end_cap_mode() const {
void Line2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_DRAW:
+ case NOTIFICATION_DRAW: {
_draw();
- break;
+ } break;
}
}
@@ -247,10 +247,7 @@ float Line2D::get_sharp_limit() const {
}
void Line2D::set_round_precision(int p_precision) {
- if (p_precision < 1) {
- p_precision = 1;
- }
- _round_precision = p_precision;
+ _round_precision = MAX(1, p_precision);
update();
}
@@ -268,15 +265,15 @@ bool Line2D::get_antialiased() const {
}
void Line2D::_draw() {
- if (_points.size() <= 1 || _width == 0.f) {
+ int len = _points.size();
+ if (len <= 1 || _width == 0.f) {
return;
}
// TODO Is this really needed?
// Copy points for faster access
Vector<Vector2> points;
- points.resize(_points.size());
- int len = points.size();
+ points.resize(len);
{
const Vector2 *points_read = _points.ptr();
for (int i = 0; i < len; ++i) {
@@ -409,7 +406,7 @@ void Line2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "end_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_end_cap_mode", "get_end_cap_mode");
ADD_GROUP("Border", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sharp_limit"), "set_sharp_limit", "get_sharp_limit");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "round_precision"), "set_round_precision", "get_round_precision");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "round_precision", PROPERTY_HINT_RANGE, "1,32,1"), "set_round_precision", "get_round_precision");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased");
BIND_ENUM_CONSTANT(LINE_JOINT_SHARP);
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 5e7eb4bac9..5322c5a5fe 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp
index 05d77f8224..25eb9b9851 100644
--- a/scene/2d/line_builder.cpp
+++ b/scene/2d/line_builder.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h
index 16c88d00e9..e50acc9ce4 100644
--- a/scene/2d/line_builder.h
+++ b/scene/2d/line_builder.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index 58bff97da9..178addd62d 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,13 +29,16 @@
/*************************************************************************/
#include "mesh_instance_2d.h"
+
#include "scene/scene_string_names.h"
void MeshInstance2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- if (mesh.is_valid()) {
- draw_mesh(mesh, texture);
- }
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ if (mesh.is_valid()) {
+ draw_mesh(mesh, texture);
+ }
+ } break;
}
}
diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h
index f94d53da7d..0647d1ddd9 100644
--- a/scene/2d/mesh_instance_2d.h
+++ b/scene/2d/mesh_instance_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp
index 1bff2f337d..8f72ff1757 100644
--- a/scene/2d/multimesh_instance_2d.cpp
+++ b/scene/2d/multimesh_instance_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,13 +29,16 @@
/*************************************************************************/
#include "multimesh_instance_2d.h"
+
#include "scene/scene_string_names.h"
void MultiMeshInstance2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- if (multimesh.is_valid()) {
- draw_multimesh(multimesh, texture);
- }
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ if (multimesh.is_valid()) {
+ draw_multimesh(multimesh, texture);
+ }
+ } break;
}
}
diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h
index 213cbd19b0..37d0d24f8f 100644
--- a/scene/2d/multimesh_instance_2d.h
+++ b/scene/2d/multimesh_instance_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index 7faa964407..91549d75f0 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,7 @@
#include "navigation_agent_2d.h"
#include "core/math/geometry_2d.h"
+#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
void NavigationAgent2D::_bind_methods() {
@@ -57,6 +58,9 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent2D::set_path_max_distance);
ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent2D::get_path_max_distance);
+ ClassDB::bind_method(D_METHOD("set_navigable_layers", "navigable_layers"), &NavigationAgent2D::set_navigable_layers);
+ ClassDB::bind_method(D_METHOD("get_navigable_layers"), &NavigationAgent2D::get_navigable_layers);
+
ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location);
ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location);
ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location);
@@ -78,6 +82,7 @@ void NavigationAgent2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_time_horizon", "get_time_horizon");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1"), "set_path_max_distance", "get_path_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "navigable_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigable_layers", "get_navigable_layers");
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
@@ -96,10 +101,12 @@ void NavigationAgent2D::_notification(int p_what) {
}
set_physics_process_internal(true);
} break;
+
case NOTIFICATION_EXIT_TREE: {
agent_parent = nullptr;
set_physics_process_internal(false);
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent) {
NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
@@ -124,8 +131,11 @@ NavigationAgent2D::~NavigationAgent2D() {
}
void NavigationAgent2D::set_navigable_layers(uint32_t p_layers) {
+ bool layers_changed = navigable_layers != p_layers;
navigable_layers = p_layers;
- update_navigation();
+ if (layers_changed) {
+ _request_repath();
+ }
}
uint32_t NavigationAgent2D::get_navigable_layers() const {
@@ -171,10 +181,7 @@ real_t NavigationAgent2D::get_path_max_distance() {
void NavigationAgent2D::set_target_location(Vector2 p_location) {
target_location = p_location;
- navigation_path.clear();
- target_reached = false;
- navigation_finished = false;
- update_frame_id = 0;
+ _request_repath();
}
Vector2 NavigationAgent2D::get_target_location() const {
@@ -241,7 +248,7 @@ TypedArray<String> NavigationAgent2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node2D>(get_parent())) {
- warnings.push_back(TTR("The NavigationAgent2D can be used only under a Node2D node"));
+ warnings.push_back(RTR("The NavigationAgent2D can be used only under a Node2D node."));
}
return warnings;
@@ -309,6 +316,13 @@ void NavigationAgent2D::update_navigation() {
}
}
+void NavigationAgent2D::_request_repath() {
+ navigation_path.clear();
+ target_reached = false;
+ navigation_finished = false;
+ update_frame_id = 0;
+}
+
void NavigationAgent2D::_check_distance_to_target() {
if (!target_reached) {
if (distance_to_target() < target_desired_distance) {
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index 052cd78a56..1447e25e8c 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,17 +45,17 @@ class NavigationAgent2D : public Node {
uint32_t navigable_layers = 1;
real_t target_desired_distance = 1.0;
- real_t radius;
- real_t neighbor_dist;
- int max_neighbors;
- real_t time_horizon;
- real_t max_speed;
+ real_t radius = 0.0;
+ real_t neighbor_dist = 0.0;
+ int max_neighbors = 0;
+ real_t time_horizon = 0.0;
+ real_t max_speed = 0.0;
real_t path_max_distance = 3.0;
Vector2 target_location;
Vector<Vector2> navigation_path;
- int nav_path_index;
+ int nav_path_index = 0;
bool velocity_submitted = false;
Vector2 prev_safe_velocity;
/// The submitted target velocity
@@ -63,7 +63,7 @@ class NavigationAgent2D : public Node {
bool target_reached = false;
bool navigation_finished = true;
// No initialized on purpose
- uint32_t update_frame_id;
+ uint32_t update_frame_id = 0;
protected:
static void _bind_methods();
@@ -139,6 +139,7 @@ public:
private:
void update_navigation();
+ void _request_repath();
void _check_distance_to_target();
};
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index 8802a1098a..b594aa3bb2 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,13 @@
#include "navigation_obstacle_2d.h"
#include "scene/2d/collision_shape_2d.h"
+#include "scene/2d/physics_body_2d.h"
+#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
void NavigationObstacle2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid);
+
ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius);
ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius);
@@ -53,28 +57,32 @@ void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
void NavigationObstacle2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
- initialize_agent();
+ case NOTIFICATION_ENTER_TREE: {
parent_node2d = Object::cast_to<Node2D>(get_parent());
+ reevaluate_agent_radius();
if (parent_node2d != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map());
}
set_physics_process_internal(true);
} break;
+
case NOTIFICATION_EXIT_TREE: {
parent_node2d = nullptr;
set_physics_process_internal(false);
} break;
+
case NOTIFICATION_PARENTED: {
parent_node2d = Object::cast_to<Node2D>(get_parent());
reevaluate_agent_radius();
} break;
+
case NOTIFICATION_UNPARENTED: {
parent_node2d = nullptr;
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (parent_node2d) {
+ if (parent_node2d && parent_node2d->is_inside_tree()) {
NavigationServer2D::get_singleton()->agent_set_position(agent, parent_node2d->get_global_position());
}
} break;
@@ -83,6 +91,7 @@ void NavigationObstacle2D::_notification(int p_what) {
NavigationObstacle2D::NavigationObstacle2D() {
agent = NavigationServer2D::get_singleton()->agent_create();
+ initialize_agent();
}
NavigationObstacle2D::~NavigationObstacle2D() {
@@ -94,7 +103,12 @@ TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node2D>(get_parent())) {
- warnings.push_back(TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object."));
+ warnings.push_back(RTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object."));
+ }
+
+ if (Object::cast_to<StaticBody2D>(get_parent())) {
+ warnings.push_back(RTR("The NavigationObstacle2D is intended for constantly moving bodies like CharacterBody2D or RigidDynamicBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
+ "\nNot constantly moving or complete static objects should be captured with a refreshed NavigationPolygon so agents can not only avoid them but also move along those objects outline at high detail"));
}
return warnings;
@@ -110,19 +124,19 @@ void NavigationObstacle2D::initialize_agent() {
void NavigationObstacle2D::reevaluate_agent_radius() {
if (!estimate_radius) {
NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
- } else if (parent_node2d) {
+ } else if (parent_node2d && parent_node2d->is_inside_tree()) {
NavigationServer2D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
}
}
real_t NavigationObstacle2D::estimate_agent_radius() const {
- if (parent_node2d) {
+ if (parent_node2d && parent_node2d->is_inside_tree()) {
// Estimate the radius of this physics body
real_t radius = 0.0;
for (int i(0); i < parent_node2d->get_child_count(); i++) {
// For each collision shape
CollisionShape2D *cs = Object::cast_to<CollisionShape2D>(parent_node2d->get_child(i));
- if (cs) {
+ if (cs && cs->is_inside_tree()) {
// Take the distance between the Body center to the shape center
real_t r = cs->get_transform().get_origin().length();
if (cs->get_shape().is_valid()) {
@@ -133,6 +147,9 @@ real_t NavigationObstacle2D::estimate_agent_radius() const {
r *= MAX(s.x, s.y);
// Takes the biggest radius
radius = MAX(radius, r);
+ } else if (cs && !cs->is_inside_tree()) {
+ WARN_PRINT("A CollisionShape2D of the NavigationObstacle2D parent node was not inside the SceneTree when estimating the obstacle radius."
+ "\nMove the NavigationObstacle2D to a child position below any CollisionShape2D node of the parent node so the CollisionShape2D is already inside the SceneTree.");
}
}
Vector2 s = parent_node2d->get_global_scale();
diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h
index a5603f059f..2a0ef14e73 100644
--- a/scene/2d/navigation_obstacle_2d.h
+++ b/scene/2d/navigation_obstacle_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 204ed72878..f46453283c 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,7 @@
#include "core/core_string_names.h"
#include "core/math/geometry_2d.h"
#include "core/os/mutex.h"
+#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
#include "thirdparty/misc/polypartition.h"
@@ -104,8 +105,8 @@ void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array
}
}
-Array NavigationPolygon::_get_polygons() const {
- Array ret;
+TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const {
+ TypedArray<Vector<int32_t>> ret;
ret.resize(polygons.size());
for (int i = 0; i < ret.size(); i++) {
ret[i] = polygons[i].indices;
@@ -122,8 +123,8 @@ void NavigationPolygon::_set_outlines(const TypedArray<Vector<Vector2>> &p_array
rect_cache_dirty = true;
}
-Array NavigationPolygon::_get_outlines() const {
- Array ret;
+TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const {
+ TypedArray<Vector<Vector2>> ret;
ret.resize(outlines.size());
for (int i = 0; i < ret.size(); i++) {
ret[i] = outlines[i];
@@ -207,7 +208,7 @@ void NavigationPolygon::set_outline(int p_idx, const Vector<Vector2> &p_outline)
void NavigationPolygon::remove_outline(int p_idx) {
ERR_FAIL_INDEX(p_idx, outlines.size());
- outlines.remove(p_idx);
+ outlines.remove_at(p_idx);
rect_cache_dirty = true;
}
@@ -299,21 +300,21 @@ void NavigationPolygon::make_polygons_from_outlines() {
}
polygons.clear();
- vertices.resize(0);
+ vertices.clear();
- Map<Vector2, int> points;
+ HashMap<Vector2, int> points;
for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) {
TPPLPoly &tp = I->get();
struct Polygon p;
for (int64_t i = 0; i < tp.GetNumPoints(); i++) {
- Map<Vector2, int>::Element *E = points.find(tp[i]);
+ HashMap<Vector2, int>::Iterator E = points.find(tp[i]);
if (!E) {
E = points.insert(tp[i], vertices.size());
vertices.push_back(tp[i]);
}
- p.indices.push_back(E->get());
+ p.indices.push_back(E->value);
}
polygons.push_back(p);
@@ -363,10 +364,10 @@ void NavigationRegion2D::set_enabled(bool p_enabled) {
if (!enabled) {
NavigationServer2D::get_singleton()->region_set_map(region, RID());
- NavigationServer2D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer2D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
} else {
NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- NavigationServer2D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ 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()) {
@@ -386,6 +387,10 @@ uint32_t NavigationRegion2D::get_layers() const {
return NavigationServer2D::get_singleton()->region_get_layers(region);
}
+RID NavigationRegion2D::get_region_rid() const {
+ return region;
+}
+
/////////////////////////////
#ifdef TOOLS_ENABLED
Rect2 NavigationRegion2D::_edit_get_rect() const {
@@ -402,18 +407,21 @@ void NavigationRegion2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled) {
NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- NavigationServer2D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer2D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
}
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
NavigationServer2D::get_singleton()->region_set_transform(region, get_global_transform());
} break;
+
case NOTIFICATION_EXIT_TREE: {
NavigationServer2D::get_singleton()->region_set_map(region, RID());
if (enabled) {
- NavigationServer2D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer2D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
}
} break;
+
case NOTIFICATION_DRAW: {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) {
Vector<Vector2> verts = navpoly->get_vertices();
@@ -513,7 +521,7 @@ TypedArray<String> NavigationRegion2D::get_configuration_warnings() const {
if (is_visible_in_tree() && is_inside_tree()) {
if (!navpoly.is_valid()) {
- warnings.push_back(TTR("A NavigationMesh resource must be set or created for this node to work. Please set a property or draw a polygon."));
+ warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work. Please set a property or draw a polygon."));
}
}
@@ -530,6 +538,8 @@ void NavigationRegion2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layers", "layers"), &NavigationRegion2D::set_layers);
ClassDB::bind_method(D_METHOD("get_layers"), &NavigationRegion2D::get_layers);
+ ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion2D::get_region_rid);
+
ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationRegion2D::_navpoly_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon");
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 2db8d70791..3c4a4e81d9 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -55,10 +55,10 @@ protected:
static void _bind_methods();
void _set_polygons(const TypedArray<Vector<int32_t>> &p_array);
- Array _get_polygons() const;
+ TypedArray<Vector<int32_t>> _get_polygons() const;
void _set_outlines(const TypedArray<Vector<Vector2>> &p_array);
- Array _get_outlines() const;
+ TypedArray<Vector<Vector2>> _get_outlines() const;
public:
#ifdef TOOLS_ENABLED
@@ -117,6 +117,8 @@ public:
void set_layers(uint32_t p_layers);
uint32_t get_layers() const;
+ RID get_region_rid() const;
+
void set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly);
Ref<NavigationPolygon> get_navigation_polygon() const;
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 6a8788ee6e..0e8bb1aad7 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,9 +42,9 @@ Dictionary Node2D::_edit_get_state() const {
}
void Node2D::_edit_set_state(const Dictionary &p_state) {
- pos = p_state["position"];
- angle = p_state["rotation"];
- _scale = p_state["scale"];
+ position = p_state["position"];
+ rotation = p_state["rotation"];
+ scale = p_state["scale"];
skew = p_state["skew"];
_update_transform();
@@ -55,7 +55,7 @@ void Node2D::_edit_set_position(const Point2 &p_position) {
}
Point2 Node2D::_edit_get_position() const {
- return pos;
+ return position;
}
void Node2D::_edit_set_scale(const Size2 &p_scale) {
@@ -63,16 +63,16 @@ void Node2D::_edit_set_scale(const Size2 &p_scale) {
}
Size2 Node2D::_edit_get_scale() const {
- return _scale;
+ return scale;
}
void Node2D::_edit_set_rotation(real_t p_rotation) {
- angle = p_rotation;
+ rotation = p_rotation;
_update_transform();
}
real_t Node2D::_edit_get_rotation() const {
- return angle;
+ return rotation;
}
bool Node2D::_edit_use_rotation() const {
@@ -85,48 +85,44 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
Rect2 r = _edit_get_rect();
Vector2 zero_offset;
- if (r.size.x != 0) {
- zero_offset.x = -r.position.x / r.size.x;
- }
- if (r.size.y != 0) {
- zero_offset.y = -r.position.y / r.size.y;
- }
-
Size2 new_scale(1, 1);
if (r.size.x != 0) {
+ zero_offset.x = -r.position.x / r.size.x;
new_scale.x = p_edit_rect.size.x / r.size.x;
}
+
if (r.size.y != 0) {
+ zero_offset.y = -r.position.y / r.size.y;
new_scale.y = p_edit_rect.size.y / r.size.y;
}
Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset;
Transform2D postxf;
- postxf.set_rotation_scale_and_skew(angle, _scale, skew);
+ postxf.set_rotation_scale_and_skew(rotation, scale, skew);
new_pos = postxf.xform(new_pos);
- pos += new_pos;
- _scale *= new_scale;
+ position += new_pos;
+ scale *= new_scale;
_update_transform();
}
#endif
void Node2D::_update_xform_values() {
- pos = _mat.elements[2];
- angle = _mat.get_rotation();
- _scale = _mat.get_scale();
- skew = _mat.get_skew();
+ position = transform.columns[2];
+ rotation = transform.get_rotation();
+ scale = transform.get_scale();
+ skew = transform.get_skew();
_xform_dirty = false;
}
void Node2D::_update_transform() {
- _mat.set_rotation_scale_and_skew(angle, _scale, skew);
- _mat.elements[2] = pos;
+ transform.set_rotation_scale_and_skew(rotation, scale, skew);
+ transform.columns[2] = position;
- RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), _mat);
+ RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform);
if (!is_inside_tree()) {
return;
@@ -137,23 +133,23 @@ void Node2D::_update_transform() {
void Node2D::set_position(const Point2 &p_pos) {
if (_xform_dirty) {
- ((Node2D *)this)->_update_xform_values();
+ const_cast<Node2D *>(this)->_update_xform_values();
}
- pos = p_pos;
+ position = p_pos;
_update_transform();
}
void Node2D::set_rotation(real_t p_radians) {
if (_xform_dirty) {
- ((Node2D *)this)->_update_xform_values();
+ const_cast<Node2D *>(this)->_update_xform_values();
}
- angle = p_radians;
+ rotation = p_radians;
_update_transform();
}
void Node2D::set_skew(real_t p_radians) {
if (_xform_dirty) {
- ((Node2D *)this)->_update_xform_values();
+ const_cast<Node2D *>(this)->_update_xform_values();
}
skew = p_radians;
_update_transform();
@@ -161,37 +157,37 @@ void Node2D::set_skew(real_t p_radians) {
void Node2D::set_scale(const Size2 &p_scale) {
if (_xform_dirty) {
- ((Node2D *)this)->_update_xform_values();
+ const_cast<Node2D *>(this)->_update_xform_values();
}
- _scale = p_scale;
+ scale = p_scale;
// Avoid having 0 scale values, can lead to errors in physics and rendering.
- if (Math::is_zero_approx(_scale.x)) {
- _scale.x = CMP_EPSILON;
+ if (Math::is_zero_approx(scale.x)) {
+ scale.x = CMP_EPSILON;
}
- if (Math::is_zero_approx(_scale.y)) {
- _scale.y = CMP_EPSILON;
+ if (Math::is_zero_approx(scale.y)) {
+ scale.y = CMP_EPSILON;
}
_update_transform();
}
Point2 Node2D::get_position() const {
if (_xform_dirty) {
- ((Node2D *)this)->_update_xform_values();
+ const_cast<Node2D *>(this)->_update_xform_values();
}
- return pos;
+ return position;
}
real_t Node2D::get_rotation() const {
if (_xform_dirty) {
- ((Node2D *)this)->_update_xform_values();
+ const_cast<Node2D *>(this)->_update_xform_values();
}
- return angle;
+ return rotation;
}
real_t Node2D::get_skew() const {
if (_xform_dirty) {
- ((Node2D *)this)->_update_xform_values();
+ const_cast<Node2D *>(this)->_update_xform_values();
}
return skew;
@@ -199,14 +195,14 @@ real_t Node2D::get_skew() const {
Size2 Node2D::get_scale() const {
if (_xform_dirty) {
- ((Node2D *)this)->_update_xform_values();
+ const_cast<Node2D *>(this)->_update_xform_values();
}
- return _scale;
+ return scale;
}
Transform2D Node2D::get_transform() const {
- return _mat;
+ return transform;
}
void Node2D::rotate(real_t p_radians) {
@@ -248,10 +244,9 @@ Point2 Node2D::get_global_position() const {
}
void Node2D::set_global_position(const Point2 &p_pos) {
- Transform2D inv;
CanvasItem *pi = get_parent_item();
if (pi) {
- inv = pi->get_global_transform().affine_inverse();
+ Transform2D inv = pi->get_global_transform().affine_inverse();
set_position(inv.xform(p_pos));
} else {
set_position(p_pos);
@@ -287,10 +282,10 @@ void Node2D::set_global_scale(const Size2 &p_scale) {
}
void Node2D::set_transform(const Transform2D &p_transform) {
- _mat = p_transform;
+ transform = p_transform;
_xform_dirty = true;
- RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), _mat);
+ RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform);
if (!is_inside_tree()) {
return;
@@ -417,7 +412,7 @@ void Node2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent);
ADD_GROUP("Transform", "");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_lesser,or_greater,noslider,suffix:px"), "set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_lesser,or_greater,noslider,suffix:px"), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew", PROPERTY_HINT_RANGE, "-89.9,89.9,0.1,radians"), "set_skew", "get_skew");
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index 3e66541e32..69d14f82ad 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,15 +36,15 @@
class Node2D : public CanvasItem {
GDCLASS(Node2D, CanvasItem);
- Point2 pos;
- real_t angle = 0.0;
- Size2 _scale = Vector2(1, 1);
+ Point2 position;
+ real_t rotation = 0.0;
+ Size2 scale = Vector2(1, 1);
real_t skew = 0.0;
int z_index = 0;
bool z_relative = true;
bool y_sort_enabled = false;
- Transform2D _mat;
+ Transform2D transform;
bool _xform_dirty = false;
diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp
index 4870ae614b..335f2404f2 100644
--- a/scene/2d/parallax_background.cpp
+++ b/scene/2d/parallax_background.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "parallax_background.h"
+
#include "parallax_layer.h"
void ParallaxBackground::_notification(int p_what) {
@@ -36,8 +37,8 @@ void ParallaxBackground::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
group_name = "__cameras_" + itos(get_viewport().get_id());
add_to_group(group_name);
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
remove_from_group(group_name);
} break;
@@ -162,15 +163,15 @@ Vector2 ParallaxBackground::get_final_offset() const {
void ParallaxBackground::_bind_methods() {
ClassDB::bind_method(D_METHOD("_camera_moved"), &ParallaxBackground::_camera_moved);
- ClassDB::bind_method(D_METHOD("set_scroll_offset", "ofs"), &ParallaxBackground::set_scroll_offset);
+ ClassDB::bind_method(D_METHOD("set_scroll_offset", "offset"), &ParallaxBackground::set_scroll_offset);
ClassDB::bind_method(D_METHOD("get_scroll_offset"), &ParallaxBackground::get_scroll_offset);
- ClassDB::bind_method(D_METHOD("set_scroll_base_offset", "ofs"), &ParallaxBackground::set_scroll_base_offset);
+ ClassDB::bind_method(D_METHOD("set_scroll_base_offset", "offset"), &ParallaxBackground::set_scroll_base_offset);
ClassDB::bind_method(D_METHOD("get_scroll_base_offset"), &ParallaxBackground::get_scroll_base_offset);
ClassDB::bind_method(D_METHOD("set_scroll_base_scale", "scale"), &ParallaxBackground::set_scroll_base_scale);
ClassDB::bind_method(D_METHOD("get_scroll_base_scale"), &ParallaxBackground::get_scroll_base_scale);
- ClassDB::bind_method(D_METHOD("set_limit_begin", "ofs"), &ParallaxBackground::set_limit_begin);
+ ClassDB::bind_method(D_METHOD("set_limit_begin", "offset"), &ParallaxBackground::set_limit_begin);
ClassDB::bind_method(D_METHOD("get_limit_begin"), &ParallaxBackground::get_limit_begin);
- ClassDB::bind_method(D_METHOD("set_limit_end", "ofs"), &ParallaxBackground::set_limit_end);
+ ClassDB::bind_method(D_METHOD("set_limit_end", "offset"), &ParallaxBackground::set_limit_end);
ClassDB::bind_method(D_METHOD("get_limit_end"), &ParallaxBackground::get_limit_end);
ClassDB::bind_method(D_METHOD("set_ignore_camera_zoom", "ignore"), &ParallaxBackground::set_ignore_camera_zoom);
ClassDB::bind_method(D_METHOD("is_ignore_camera_zoom"), &ParallaxBackground::is_ignore_camera_zoom);
diff --git a/scene/2d/parallax_background.h b/scene/2d/parallax_background.h
index 3745c5b587..1a3cb43999 100644
--- a/scene/2d/parallax_background.h
+++ b/scene/2d/parallax_background.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 797e2e59cb..9e8ab224dc 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -99,6 +99,7 @@ void ParallaxLayer::_notification(int p_what) {
orig_scale = get_scale();
_update_mirroring();
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (Engine::get_singleton()->is_editor_hint()) {
break;
@@ -142,7 +143,7 @@ TypedArray<String> ParallaxLayer::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<ParallaxBackground>(get_parent())) {
- warnings.push_back(TTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."));
+ warnings.push_back(RTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."));
}
return warnings;
diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h
index cc2d2e096e..b4dcf0ea61 100644
--- a/scene/2d/parallax_layer.h
+++ b/scene/2d/parallax_layer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index ed30e871d7..a21cb7db62 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -86,30 +86,41 @@ bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc
#endif
void Path2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW && curve.is_valid()) {
- //draw the curve!!
+ switch (p_what) {
+ // Draw the curve if navigation debugging is enabled.
+ case NOTIFICATION_DRAW: {
+ if (!curve.is_valid()) {
+ break;
+ }
- if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_navigation_hint()) {
- return;
- }
+ if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_navigation_hint()) {
+ return;
+ }
+
+ if (curve->get_point_count() < 2) {
+ return;
+ }
#ifdef TOOLS_ENABLED
- const real_t line_width = 2 * EDSCALE;
+ const real_t line_width = 2 * EDSCALE;
#else
- const real_t line_width = 2;
+ const real_t line_width = 2;
#endif
- const Color color = Color(0.5, 0.6, 1.0, 0.7);
+ const Color color = Color(0.5, 0.6, 1.0, 0.7);
- for (int i = 0; i < curve->get_point_count(); i++) {
- Vector2 prev_p = curve->get_point_position(i);
+ _cached_draw_pts.resize(curve->get_point_count() * 8);
+ int count = 0;
- for (int j = 1; j <= 8; j++) {
- real_t frac = j / 8.0;
- Vector2 p = curve->interpolate(i, frac);
- draw_line(prev_p, p, color, line_width);
- prev_p = p;
+ for (int i = 0; i < curve->get_point_count(); i++) {
+ for (int j = 0; j < 8; j++) {
+ real_t frac = j * (1.0 / 8.0);
+ Vector2 p = curve->interpolate(i, frac);
+ _cached_draw_pts.set(count++, p);
+ }
}
- }
+
+ draw_polyline(_cached_draw_pts, color, line_width, true);
+ } break;
}
}
@@ -220,8 +231,8 @@ void PathFollow2D::_notification(int p_what) {
if (path) {
_update_transform();
}
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
path = nullptr;
} break;
@@ -252,7 +263,7 @@ TypedArray<String> PathFollow2D::get_configuration_warnings() const {
if (is_visible_in_tree() && is_inside_tree()) {
if (!Object::cast_to<Path2D>(get_parent())) {
- warnings.push_back(TTR("PathFollow2D only works when set as a child of a Path2D node."));
+ warnings.push_back(RTR("PathFollow2D only works when set as a child of a Path2D node."));
}
}
@@ -300,7 +311,7 @@ void PathFollow2D::set_offset(real_t p_offset) {
if (path->get_curve().is_valid()) {
real_t path_length = path->get_curve()->get_baked_length();
- if (loop) {
+ if (loop && path_length) {
offset = Math::fposmod(offset, path_length);
if (!Math::is_zero_approx(p_offset) && Math::is_zero_approx(offset)) {
offset = path_length;
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 3b12f025fc..bc55f84831 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,6 +38,7 @@ class Path2D : public Node2D {
GDCLASS(Path2D, Node2D);
Ref<Curve2D> curve;
+ Vector<Vector2> _cached_draw_pts;
void _curve_changed();
diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp
index c1b0bc35dd..2999736d64 100644
--- a/scene/2d/physical_bone_2d.cpp
+++ b/scene/2d/physical_bone_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -110,15 +110,15 @@ TypedArray<String> PhysicalBone2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!parent_skeleton) {
- warnings.push_back(TTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"));
+ warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"));
}
if (parent_skeleton && bone2d_index <= -1) {
- warnings.push_back(TTR("A PhysicalBone2D needs to be assigned to a Bone2D node in order to function! Please set a Bone2D node in the inspector."));
+ warnings.push_back(RTR("A PhysicalBone2D needs to be assigned to a Bone2D node in order to function! Please set a Bone2D node in the inspector."));
}
if (!child_joint) {
PhysicalBone2D *parent_bone = Object::cast_to<PhysicalBone2D>(get_parent());
if (parent_bone) {
- warnings.push_back(TTR("A PhysicalBone2D node should have a Joint2D-based child node to keep bones connected! Please add a Joint2D-based node as a child to this node!"));
+ warnings.push_back(RTR("A PhysicalBone2D node should have a Joint2D-based child node to keep bones connected! Please add a Joint2D-based node as a child to this node!"));
}
}
diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h
index 8b41f75c3e..22d329c320 100644
--- a/scene/2d/physical_bone_2d.h
+++ b/scene/2d/physical_bone_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,7 +49,7 @@ private:
NodePath bone2d_nodepath;
bool follow_bone_when_simulating = false;
- Joint2D *child_joint;
+ Joint2D *child_joint = nullptr;
bool auto_configure_joint = true;
bool simulate_physics = false;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index f0e51097db..f345d8c3c9 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,8 +34,8 @@
#include "scene/scene_string_names.h"
void PhysicsBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08));
- ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08));
+ ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08));
+ ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08));
ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with);
@@ -54,14 +54,15 @@ PhysicsBody2D::~PhysicsBody2D() {
}
}
-Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_linear_velocity, bool p_test_only, real_t p_margin) {
- // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
- double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
-
- PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin);
+Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_distance, bool p_test_only, real_t p_margin) {
+ PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_distance, p_margin);
PhysicsServer2D::MotionResult result;
- if (move_and_collide(parameters, result, p_test_only)) {
+
+ bool collided = move_and_collide(parameters, result, p_test_only);
+
+ // Don't report collision when the whole motion is done.
+ if (collided && result.collision_safe_fraction < 1) {
// Create a new instance when the cached reference is invalid or still in use in script.
if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) {
motion_cache.instantiate();
@@ -122,14 +123,14 @@ bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_
if (!p_test_only) {
Transform2D gt = p_parameters.from;
- gt.elements[2] += r_result.travel;
+ gt.columns[2] += r_result.travel;
set_global_transform(gt);
}
return colliding;
}
-bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
+bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
ERR_FAIL_COND_V(!is_inside_tree(), false);
PhysicsServer2D::MotionResult *r = nullptr;
@@ -141,10 +142,7 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear
r = &temp_result;
}
- // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
- double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
-
- PhysicsServer2D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
+ PhysicsServer2D::MotionParameters parameters(p_from, p_distance, p_margin);
bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r);
@@ -283,7 +281,7 @@ void AnimatableBody2D::_update_kinematic_motion() {
}
void AnimatableBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) {
- AnimatableBody2D *body = (AnimatableBody2D *)p_instance;
+ AnimatableBody2D *body = static_cast<AnimatableBody2D *>(p_instance);
body->_body_state_changed(p_state);
}
@@ -341,17 +339,17 @@ void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) {
ERR_FAIL_COND(!node);
ERR_FAIL_COND(!contact_monitor);
- Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id);
+ HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(E->get().in_scene);
+ ERR_FAIL_COND(E->value.in_scene);
contact_monitor->locked = true;
- E->get().in_scene = true;
+ E->value.in_scene = true;
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape);
}
contact_monitor->locked = false;
@@ -362,17 +360,17 @@ void RigidDynamicBody2D::_body_exit_tree(ObjectID p_id) {
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
ERR_FAIL_COND(!contact_monitor);
- Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id);
+ HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(!E->get().in_scene);
- E->get().in_scene = false;
+ ERR_FAIL_COND(!E->value.in_scene);
+ E->value.in_scene = false;
contact_monitor->locked = true;
emit_signal(SceneStringNames::get_singleton()->body_exited, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape);
}
contact_monitor->locked = false;
@@ -386,45 +384,45 @@ void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!contact_monitor);
- Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid);
+ HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(objid);
ERR_FAIL_COND(!body_in && !E);
if (body_in) {
if (!E) {
E = contact_monitor->body_map.insert(objid, BodyState());
- E->get().rid = p_body;
- //E->get().rc=0;
- E->get().in_scene = node && node->is_inside_tree();
+ E->value.rid = p_body;
+ //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));
- if (E->get().in_scene) {
+ if (E->value.in_scene) {
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
}
}
- //E->get().rc++;
+ //E->value.rc++;
}
if (node) {
- E->get().shapes.insert(ShapePair(p_body_shape, p_local_shape));
+ E->value.shapes.insert(ShapePair(p_body_shape, p_local_shape));
}
- if (E->get().in_scene) {
+ if (E->value.in_scene) {
emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_local_shape);
}
} else {
- //E->get().rc--;
+ //E->value.rc--;
if (node) {
- E->get().shapes.erase(ShapePair(p_body_shape, p_local_shape));
+ E->value.shapes.erase(ShapePair(p_body_shape, p_local_shape));
}
- bool in_scene = E->get().in_scene;
+ bool in_scene = E->value.in_scene;
- if (E->get().shapes.is_empty()) {
+ if (E->value.shapes.is_empty()) {
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree));
@@ -433,7 +431,7 @@ void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p
}
}
- contact_monitor->body_map.erase(E);
+ contact_monitor->body_map.remove(E);
}
if (node && in_scene) {
emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, node, p_body_shape, p_local_shape);
@@ -449,7 +447,7 @@ struct _RigidDynamicBody2DInOut {
};
void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) {
- RigidDynamicBody2D *body = (RigidDynamicBody2D *)p_instance;
+ RigidDynamicBody2D *body = static_cast<RigidDynamicBody2D *>(p_instance);
body->_body_state_changed(p_state);
}
@@ -496,7 +494,7 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state)
int local_shape = p_state->get_contact_local_shape(i);
int shape = p_state->get_contact_collider_shape(i);
- Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj);
+ HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(obj);
if (!E) {
toadd[toadd_count].rid = rid;
toadd[toadd_count].local_shape = local_shape;
@@ -507,7 +505,7 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state)
}
ShapePair sp(shape, local_shape);
- int idx = E->get().shapes.find(sp);
+ int idx = E->value.shapes.find(sp);
if (idx == -1) {
toadd[toadd_count].rid = rid;
toadd[toadd_count].local_shape = local_shape;
@@ -517,7 +515,7 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state)
continue;
}
- E->get().shapes[idx].tagged = true;
+ E->value.shapes[idx].tagged = true;
}
//put the ones to remove
@@ -809,32 +807,44 @@ void RigidDynamicBody2D::apply_torque_impulse(real_t p_torque) {
PhysicsServer2D::get_singleton()->body_apply_torque_impulse(get_rid(), p_torque);
}
-void RigidDynamicBody2D::set_applied_force(const Vector2 &p_force) {
- PhysicsServer2D::get_singleton()->body_set_applied_force(get_rid(), p_force);
-};
+void RigidDynamicBody2D::apply_central_force(const Vector2 &p_force) {
+ PhysicsServer2D::get_singleton()->body_apply_central_force(get_rid(), p_force);
+}
-Vector2 RigidDynamicBody2D::get_applied_force() const {
- return PhysicsServer2D::get_singleton()->body_get_applied_force(get_rid());
-};
+void RigidDynamicBody2D::apply_force(const Vector2 &p_force, const Vector2 &p_position) {
+ PhysicsServer2D::get_singleton()->body_apply_force(get_rid(), p_force, p_position);
+}
-void RigidDynamicBody2D::set_applied_torque(const real_t p_torque) {
- PhysicsServer2D::get_singleton()->body_set_applied_torque(get_rid(), p_torque);
-};
+void RigidDynamicBody2D::apply_torque(real_t p_torque) {
+ PhysicsServer2D::get_singleton()->body_apply_torque(get_rid(), p_torque);
+}
-real_t RigidDynamicBody2D::get_applied_torque() const {
- return PhysicsServer2D::get_singleton()->body_get_applied_torque(get_rid());
-};
+void RigidDynamicBody2D::add_constant_central_force(const Vector2 &p_force) {
+ PhysicsServer2D::get_singleton()->body_add_constant_central_force(get_rid(), p_force);
+}
+
+void RigidDynamicBody2D::add_constant_force(const Vector2 &p_force, const Vector2 &p_position) {
+ PhysicsServer2D::get_singleton()->body_add_constant_force(get_rid(), p_force, p_position);
+}
+
+void RigidDynamicBody2D::add_constant_torque(const real_t p_torque) {
+ PhysicsServer2D::get_singleton()->body_add_constant_torque(get_rid(), p_torque);
+}
+
+void RigidDynamicBody2D::set_constant_force(const Vector2 &p_force) {
+ PhysicsServer2D::get_singleton()->body_set_constant_force(get_rid(), p_force);
+}
-void RigidDynamicBody2D::add_central_force(const Vector2 &p_force) {
- PhysicsServer2D::get_singleton()->body_add_central_force(get_rid(), p_force);
+Vector2 RigidDynamicBody2D::get_constant_force() const {
+ return PhysicsServer2D::get_singleton()->body_get_constant_force(get_rid());
}
-void RigidDynamicBody2D::add_force(const Vector2 &p_force, const Vector2 &p_position) {
- PhysicsServer2D::get_singleton()->body_add_force(get_rid(), p_force, p_position);
+void RigidDynamicBody2D::set_constant_torque(real_t p_torque) {
+ PhysicsServer2D::get_singleton()->body_set_constant_torque(get_rid(), p_torque);
}
-void RigidDynamicBody2D::add_torque(const real_t p_torque) {
- PhysicsServer2D::get_singleton()->body_add_torque(get_rid(), p_torque);
+real_t RigidDynamicBody2D::get_constant_torque() const {
+ return PhysicsServer2D::get_singleton()->body_get_constant_torque(get_rid());
}
void RigidDynamicBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) {
@@ -900,7 +910,7 @@ void RigidDynamicBody2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint()) {
- set_notify_local_transform(true); //used for warnings and only in editor
+ set_notify_local_transform(true); // Used for warnings and only in editor.
}
} break;
@@ -918,8 +928,8 @@ TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const {
TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings();
- if (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05) {
- warnings.push_back(TTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ if (ABS(t.columns[0].length() - 1.0) > 0.05 || ABS(t.columns[1].length() - 1.0) > 0.05) {
+ warnings.push_back(RTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
}
return warnings;
@@ -979,15 +989,19 @@ void RigidDynamicBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidDynamicBody2D::apply_impulse, Vector2());
ClassDB::bind_method(D_METHOD("apply_torque_impulse", "torque"), &RigidDynamicBody2D::apply_torque_impulse);
- ClassDB::bind_method(D_METHOD("set_applied_force", "force"), &RigidDynamicBody2D::set_applied_force);
- ClassDB::bind_method(D_METHOD("get_applied_force"), &RigidDynamicBody2D::get_applied_force);
+ ClassDB::bind_method(D_METHOD("apply_central_force", "force"), &RigidDynamicBody2D::apply_central_force);
+ ClassDB::bind_method(D_METHOD("apply_force", "force", "position"), &RigidDynamicBody2D::apply_force, Vector2());
+ ClassDB::bind_method(D_METHOD("apply_torque", "torque"), &RigidDynamicBody2D::apply_torque);
- ClassDB::bind_method(D_METHOD("set_applied_torque", "torque"), &RigidDynamicBody2D::set_applied_torque);
- ClassDB::bind_method(D_METHOD("get_applied_torque"), &RigidDynamicBody2D::get_applied_torque);
+ ClassDB::bind_method(D_METHOD("add_constant_central_force", "force"), &RigidDynamicBody2D::add_constant_central_force);
+ ClassDB::bind_method(D_METHOD("add_constant_force", "force", "position"), &RigidDynamicBody2D::add_constant_force, Vector2());
+ ClassDB::bind_method(D_METHOD("add_constant_torque", "torque"), &RigidDynamicBody2D::add_constant_torque);
- ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidDynamicBody2D::add_central_force);
- ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &RigidDynamicBody2D::add_force, Vector2());
- ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidDynamicBody2D::add_torque);
+ ClassDB::bind_method(D_METHOD("set_constant_force", "force"), &RigidDynamicBody2D::set_constant_force);
+ ClassDB::bind_method(D_METHOD("get_constant_force"), &RigidDynamicBody2D::get_constant_force);
+
+ ClassDB::bind_method(D_METHOD("set_constant_torque", "torque"), &RigidDynamicBody2D::set_constant_torque);
+ ClassDB::bind_method(D_METHOD("get_constant_torque"), &RigidDynamicBody2D::get_constant_torque);
ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidDynamicBody2D::set_sleeping);
ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidDynamicBody2D::is_sleeping);
@@ -1032,9 +1046,9 @@ void RigidDynamicBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
- ADD_GROUP("Applied Forces", "applied_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "applied_torque"), "set_applied_torque", "get_applied_torque");
+ ADD_GROUP("Constant Forces", "constant_");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_force"), "set_constant_force", "get_constant_force");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_torque"), "set_constant_torque", "get_constant_torque");
ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index")));
ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index")));
@@ -1096,7 +1110,7 @@ bool CharacterBody2D::move_and_slide() {
Vector2 current_platform_velocity = platform_velocity;
Transform2D gt = get_global_transform();
- previous_position = gt.elements[2];
+ previous_position = gt.columns[2];
if ((on_floor || on_wall) && platform_rid.is_valid()) {
bool excluded = false;
@@ -1109,7 +1123,7 @@ bool CharacterBody2D::move_and_slide() {
//this approach makes sure there is less delay between the actual body velocity and the one we saved
PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);
if (bs) {
- Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2];
+ Vector2 local_position = gt.columns[2] - bs->get_transform().columns[2];
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
} else {
// Body is removed or destroyed, invalidate floor.
@@ -1146,7 +1160,7 @@ bool CharacterBody2D::move_and_slide() {
if (motion_mode == MOTION_MODE_GROUNDED) {
_move_and_slide_grounded(delta, was_on_floor);
} else {
- _move_and_slide_free(delta);
+ _move_and_slide_floating(delta);
}
// Compute real velocity.
@@ -1158,7 +1172,7 @@ bool CharacterBody2D::move_and_slide() {
if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) {
current_platform_velocity = current_platform_velocity.slide(up_direction);
}
- motion_velocity += current_platform_velocity;
+ velocity += current_platform_velocity;
}
}
@@ -1166,7 +1180,7 @@ bool CharacterBody2D::move_and_slide() {
}
void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) {
- Vector2 motion = motion_velocity * p_delta;
+ Vector2 motion = velocity * p_delta;
Vector2 motion_slide_up = motion.slide(up_direction);
Vector2 prev_floor_normal = floor_normal;
@@ -1184,13 +1198,13 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// If the platform's ceiling push down the body.
bool apply_ceiling_velocity = false;
bool first_slide = true;
- bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0;
+ bool vel_dir_facing_up = velocity.dot(up_direction) > 0;
Vector2 last_travel;
for (int iteration = 0; iteration < max_slides; ++iteration) {
PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin);
- Vector2 prev_position = parameters.from.elements[2];
+ Vector2 prev_position = parameters.from.columns[2];
PhysicsServer2D::MotionResult result;
bool collided = move_and_collide(parameters, result, false, !sliding_enabled);
@@ -1201,26 +1215,26 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
motion_results.push_back(result);
_set_collision_direction(result);
- // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one.
+ // If we hit a ceiling platform, we set the vertical velocity to at least the platform one.
if (on_ceiling && result.collider_velocity != Vector2() && result.collider_velocity.dot(up_direction) < 0) {
// If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward.
if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (result.collision_normal + up_direction).length() < 0.01) {
apply_ceiling_velocity = true;
Vector2 ceiling_vertical_velocity = up_direction * up_direction.dot(result.collider_velocity);
- Vector2 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity);
+ Vector2 motion_vertical_velocity = up_direction * up_direction.dot(velocity);
if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) {
- motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction);
+ velocity = ceiling_vertical_velocity + velocity.slide(up_direction);
}
}
}
- if (on_floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
+ if (on_floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
if (result.travel.length() <= margin + CMP_EPSILON) {
- gt.elements[2] -= result.travel;
+ gt.columns[2] -= result.travel;
}
set_global_transform(gt);
- motion_velocity = Vector2();
+ velocity = Vector2();
last_motion = Vector2();
motion = Vector2();
break;
@@ -1239,12 +1253,12 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (result.travel.length() <= margin + CMP_EPSILON) {
// Cancels the motion.
Transform2D gt = get_global_transform();
- gt.elements[2] -= result.travel;
+ gt.columns[2] -= result.travel;
set_global_transform(gt);
}
// Determines if you are on the ground.
- _snap_on_floor(true, false);
- motion_velocity = Vector2();
+ _snap_on_floor(true, false, true);
+ velocity = Vector2();
last_motion = Vector2();
motion = Vector2();
break;
@@ -1266,7 +1280,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) {
Vector2 slide_motion = result.remainder.slide(result.collision_normal);
- if (slide_motion.dot(motion_velocity) > 0.0) {
+ if (slide_motion.dot(velocity) > 0.0) {
motion = slide_motion;
} else {
motion = Vector2();
@@ -1274,10 +1288,10 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (slide_on_ceiling && on_ceiling) {
// Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall.
if (vel_dir_facing_up) {
- motion_velocity = motion_velocity.slide(result.collision_normal);
+ velocity = velocity.slide(result.collision_normal);
} else {
// Avoid acceleration in slope when falling.
- motion_velocity = up_direction * up_direction.dot(motion_velocity);
+ velocity = up_direction * up_direction.dot(velocity);
}
}
}
@@ -1285,7 +1299,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
else {
motion = result.remainder;
if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) {
- motion_velocity = motion_velocity.slide(up_direction);
+ velocity = velocity.slide(up_direction);
motion = motion.slide(up_direction);
}
}
@@ -1298,7 +1312,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
can_apply_constant_speed = false;
sliding_enabled = true;
Transform2D gt = get_global_transform();
- gt.elements[2] = prev_position;
+ gt.columns[2] = prev_position;
set_global_transform(gt);
Vector2 motion_slide_norm = motion.slide(prev_floor_normal).normalized();
@@ -1319,23 +1333,23 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Scales the horizontal velocity according to the wall slope.
if (is_on_wall_only() && motion_slide_up.dot(motion_results.get(0).collision_normal) < 0) {
- Vector2 slide_motion = motion_velocity.slide(motion_results.get(0).collision_normal);
+ Vector2 slide_motion = velocity.slide(motion_results.get(0).collision_normal);
if (motion_slide_up.dot(slide_motion) < 0) {
- motion_velocity = up_direction * up_direction.dot(motion_velocity);
+ velocity = up_direction * up_direction.dot(velocity);
} else {
- // Keeps the vertical motion from motion_velocity and add the horizontal motion of the projection.
- motion_velocity = up_direction * up_direction.dot(motion_velocity) + slide_motion.slide(up_direction);
+ // Keeps the vertical motion from velocity and add the horizontal motion of the projection.
+ velocity = up_direction * up_direction.dot(velocity) + slide_motion.slide(up_direction);
}
}
// Reset the gravity accumulation when touching the ground.
if (on_floor && !vel_dir_facing_up) {
- motion_velocity = motion_velocity.slide(up_direction);
+ velocity = velocity.slide(up_direction);
}
}
-void CharacterBody2D::_move_and_slide_free(double p_delta) {
- Vector2 motion = motion_velocity * p_delta;
+void CharacterBody2D::_move_and_slide_floating(double p_delta) {
+ Vector2 motion = velocity * p_delta;
platform_rid = RID();
platform_object_id = ObjectID();
@@ -1360,7 +1374,7 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
break;
}
- if (free_mode_min_slide_angle != 0 && result.get_angle(-motion_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (wall_min_slide_angle != 0 && result.get_angle(-velocity.normalized()) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
motion = Vector2();
} else if (first_slide) {
Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized();
@@ -1369,7 +1383,7 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
motion = result.remainder.slide(result.collision_normal);
}
- if (motion.dot(motion_velocity) <= 0.0) {
+ if (motion.dot(velocity) <= 0.0) {
motion = Vector2();
}
}
@@ -1382,8 +1396,8 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
}
}
-void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) {
- if (on_floor || !was_on_floor || vel_dir_facing_up) {
+void CharacterBody2D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up, bool p_wall_as_floor) {
+ if (on_floor || !p_was_on_floor || p_vel_dir_facing_up) {
return;
}
@@ -1395,7 +1409,8 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
PhysicsServer2D::MotionResult result;
if (move_and_collide(parameters, result, true, false)) {
- if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
+ if ((result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) ||
+ (p_wall_as_floor && result.get_angle(-up_direction) > floor_max_angle + FLOOR_ANGLE_THRESHOLD)) {
on_floor = true;
floor_normal = result.collision_normal;
_set_platform_data(result);
@@ -1410,14 +1425,14 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
}
}
- parameters.from.elements[2] += result.travel;
+ parameters.from.columns[2] += result.travel;
set_global_transform(parameters.from);
}
}
}
-bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) {
- if (up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) {
+bool CharacterBody2D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up) {
+ if (up_direction == Vector2() || on_floor || !p_was_on_floor || p_vel_dir_facing_up) {
return false;
}
@@ -1461,12 +1476,12 @@ void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_
platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid);
}
-const Vector2 &CharacterBody2D::get_motion_velocity() const {
- return motion_velocity;
+const Vector2 &CharacterBody2D::get_velocity() const {
+ return velocity;
}
-void CharacterBody2D::set_motion_velocity(const Vector2 &p_velocity) {
- motion_velocity = p_velocity;
+void CharacterBody2D::set_velocity(const Vector2 &p_velocity) {
+ velocity = p_velocity;
}
bool CharacterBody2D::is_on_floor() const {
@@ -1506,7 +1521,7 @@ const Vector2 &CharacterBody2D::get_last_motion() const {
}
Vector2 CharacterBody2D::get_position_delta() const {
- return get_global_transform().elements[2] - previous_position;
+ return get_global_transform().columns[2] - previous_position;
}
const Vector2 &CharacterBody2D::get_real_velocity() const {
@@ -1652,12 +1667,12 @@ void CharacterBody2D::set_floor_snap_length(real_t p_floor_snap_length) {
floor_snap_length = p_floor_snap_length;
}
-real_t CharacterBody2D::get_free_mode_min_slide_angle() const {
- return free_mode_min_slide_angle;
+real_t CharacterBody2D::get_wall_min_slide_angle() const {
+ return wall_min_slide_angle;
}
-void CharacterBody2D::set_free_mode_min_slide_angle(real_t p_radians) {
- free_mode_min_slide_angle = p_radians;
+void CharacterBody2D::set_wall_min_slide_angle(real_t p_radians) {
+ wall_min_slide_angle = p_radians;
}
const Vector2 &CharacterBody2D::get_up_direction() const {
@@ -1665,7 +1680,7 @@ const Vector2 &CharacterBody2D::get_up_direction() const {
}
void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) {
- ERR_FAIL_COND_MSG(p_up_direction == Vector2(), "up_direction can't be equal to Vector2.ZERO, consider using Free motion mode instead.");
+ ERR_FAIL_COND_MSG(p_up_direction == Vector2(), "up_direction can't be equal to Vector2.ZERO, consider using Floating motion mode instead.");
up_direction = p_up_direction.normalized();
}
@@ -1687,8 +1702,8 @@ void CharacterBody2D::_notification(int p_what) {
void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody2D::move_and_slide);
- ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody2D::set_motion_velocity);
- ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody2D::get_motion_velocity);
+ ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &CharacterBody2D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &CharacterBody2D::get_velocity);
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin);
@@ -1712,8 +1727,8 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_floor_max_angle", "radians"), &CharacterBody2D::set_floor_max_angle);
ClassDB::bind_method(D_METHOD("get_floor_snap_length"), &CharacterBody2D::get_floor_snap_length);
ClassDB::bind_method(D_METHOD("set_floor_snap_length", "floor_snap_length"), &CharacterBody2D::set_floor_snap_length);
- ClassDB::bind_method(D_METHOD("get_free_mode_min_slide_angle"), &CharacterBody2D::get_free_mode_min_slide_angle);
- ClassDB::bind_method(D_METHOD("set_free_mode_min_slide_angle", "radians"), &CharacterBody2D::set_free_mode_min_slide_angle);
+ ClassDB::bind_method(D_METHOD("get_wall_min_slide_angle"), &CharacterBody2D::get_wall_min_slide_angle);
+ ClassDB::bind_method(D_METHOD("set_wall_min_slide_angle", "radians"), &CharacterBody2D::set_wall_min_slide_angle);
ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody2D::get_up_direction);
ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction);
ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody2D::set_motion_mode);
@@ -1738,28 +1753,26 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision);
ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody2D::_get_last_slide_collision);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Floating", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides");
-
- ADD_GROUP("Free Mode", "free_mode_");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "free_mode_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_free_mode_min_slide_angle", "get_free_mode_min_slide_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle");
ADD_GROUP("Floor", "floor_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length");
- ADD_GROUP("Moving platform", "moving_platform");
+ ADD_GROUP("Moving Platform", "moving_platform");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED);
- BIND_ENUM_CONSTANT(MOTION_MODE_FREE);
+ BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING);
BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS);
BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY);
@@ -1767,12 +1780,12 @@ void CharacterBody2D::_bind_methods() {
}
void CharacterBody2D::_validate_property(PropertyInfo &property) const {
- if (motion_mode == MOTION_MODE_FREE) {
+ if (motion_mode == MOTION_MODE_FLOATING) {
if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") {
property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
} else {
- if (property.name == "free_mode_min_slide_angle") {
+ if (property.name == "wall_min_slide_angle") {
property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 2abce4b0a5..7401fc7578 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,11 +47,11 @@ protected:
Ref<KinematicCollision2D> motion_cache;
- Ref<KinematicCollision2D> _move(const Vector2 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.08);
+ Ref<KinematicCollision2D> _move(const Vector2 &p_distance, bool p_test_only = false, real_t p_margin = 0.08);
public:
bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
- bool test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
+ bool test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
TypedArray<PhysicsBody2D> get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
@@ -200,7 +200,7 @@ private:
struct ContactMonitor {
bool locked = false;
- Map<ObjectID, BodyState> body_map;
+ HashMap<ObjectID, BodyState> body_map;
};
ContactMonitor *contact_monitor = nullptr;
@@ -292,15 +292,19 @@ public:
void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2());
void apply_torque_impulse(real_t p_torque);
- void set_applied_force(const Vector2 &p_force);
- Vector2 get_applied_force() const;
+ void apply_central_force(const Vector2 &p_force);
+ void apply_force(const Vector2 &p_force, const Vector2 &p_position = Vector2());
+ void apply_torque(real_t p_torque);
- void set_applied_torque(const real_t p_torque);
- real_t get_applied_torque() const;
+ void add_constant_central_force(const Vector2 &p_force);
+ void add_constant_force(const Vector2 &p_force, const Vector2 &p_position = Vector2());
+ void add_constant_torque(real_t p_torque);
- void add_central_force(const Vector2 &p_force);
- void add_force(const Vector2 &p_force, const Vector2 &p_position = Vector2());
- void add_torque(real_t p_torque);
+ void set_constant_force(const Vector2 &p_force);
+ Vector2 get_constant_force() const;
+
+ void set_constant_torque(real_t p_torque);
+ real_t get_constant_torque() const;
TypedArray<Node2D> get_colliding_bodies() const; //function for script
@@ -324,7 +328,7 @@ class CharacterBody2D : public PhysicsBody2D {
public:
enum MotionMode {
MOTION_MODE_GROUNDED,
- MOTION_MODE_FREE,
+ MOTION_MODE_FLOATING,
};
enum MovingPlatformApplyVelocityOnLeave {
PLATFORM_VEL_ON_LEAVE_ALWAYS,
@@ -333,8 +337,8 @@ public:
};
bool move_and_slide();
- const Vector2 &get_motion_velocity() const;
- void set_motion_velocity(const Vector2 &p_velocity);
+ const Vector2 &get_velocity() const;
+ void set_velocity(const Vector2 &p_velocity);
bool is_on_floor() const;
bool is_on_floor_only() const;
@@ -370,11 +374,11 @@ private:
int platform_layer = 0;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
real_t floor_snap_length = 1;
- real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0);
+ real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector2 up_direction = Vector2(0.0, -1.0);
uint32_t moving_platform_floor_layers = UINT32_MAX;
uint32_t moving_platform_wall_layers = 0;
- Vector2 motion_velocity;
+ Vector2 velocity;
Vector2 floor_normal;
Vector2 platform_velocity;
@@ -416,8 +420,8 @@ private:
real_t get_floor_snap_length();
void set_floor_snap_length(real_t p_floor_snap_length);
- real_t get_free_mode_min_slide_angle() const;
- void set_free_mode_min_slide_angle(real_t p_radians);
+ real_t get_wall_min_slide_angle() const;
+ void set_wall_min_slide_angle(real_t p_radians);
uint32_t get_moving_platform_floor_layers() const;
void set_moving_platform_floor_layers(const uint32_t p_exclude_layer);
@@ -431,17 +435,17 @@ private:
void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity);
MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const;
- void _move_and_slide_free(double p_delta);
+ void _move_and_slide_floating(double p_delta);
void _move_and_slide_grounded(double p_delta, bool p_was_on_floor);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
Ref<KinematicCollision2D> _get_last_slide_collision();
const Vector2 &get_up_direction() const;
- bool _on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up);
+ bool _on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up);
void set_up_direction(const Vector2 &p_up_direction);
void _set_collision_direction(const PhysicsServer2D::MotionResult &p_result);
void _set_platform_data(const PhysicsServer2D::MotionResult &p_result);
- void _snap_on_floor(bool was_on_floor, bool vel_dir_facing_up);
+ void _snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up, bool p_wall_as_floor = false);
protected:
void _notification(int p_what);
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index f00959bbb6..f9986c2f30 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -295,14 +295,14 @@ void Polygon2D::_notification(int p_what) {
}
Vector<Color> colors;
+ colors.resize(len);
+
if (vertex_colors.size() == points.size()) {
- colors.resize(len);
const Color *color_r = vertex_colors.ptr();
for (int i = 0; i < len; i++) {
colors.write[i] = color_r[i];
}
} else {
- colors.resize(len);
for (int i = 0; i < len; i++) {
colors.write[i] = color;
}
@@ -428,15 +428,6 @@ Vector<Color> Polygon2D::get_vertex_colors() const {
void Polygon2D::set_texture(const Ref<Texture2D> &p_texture) {
texture = p_texture;
-
- /*if (texture.is_valid()) {
- uint32_t flags=texture->get_flags();
- flags&=~Texture::FLAG_REPEAT;
- if (tex_tile)
- flags|=Texture::FLAG_REPEAT;
-
- texture->set_flags(flags);
- }*/
update();
}
@@ -532,7 +523,7 @@ Vector<float> Polygon2D::get_bone_weights(int p_index) const {
void Polygon2D::erase_bone(int p_idx) {
ERR_FAIL_INDEX(p_idx, bone_weights.size());
- bone_weights.remove(p_idx);
+ bone_weights.remove_at(p_idx);
}
void Polygon2D::clear_bones() {
@@ -662,7 +653,7 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones");
ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count");
}
diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h
index bf386b9ace..d6a1be0f6d 100644
--- a/scene/2d/polygon_2d.h
+++ b/scene/2d/polygon_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index 4f053ff8b0..d946ea63bf 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,43 +30,45 @@
#include "position_2d.h"
-const real_t DEFAULT_GIZMO_EXTENTS = 10.0;
-
void Position2D::_draw_cross() {
const real_t extents = get_gizmo_extents();
// Add more points to create a "hard stop" in the color gradient.
- PackedVector2Array points_x;
- points_x.push_back(Point2(+extents, 0));
- points_x.push_back(Point2());
- points_x.push_back(Point2());
- points_x.push_back(Point2(-extents, 0));
-
- PackedVector2Array points_y;
- points_y.push_back(Point2(0, +extents));
- points_y.push_back(Point2());
- points_y.push_back(Point2());
- points_y.push_back(Point2(0, -extents));
+ PackedVector2Array points_x = {
+ Point2(+extents, 0),
+ Point2(),
+ Point2(),
+ Point2(-extents, 0)
+ };
+
+ PackedVector2Array points_y = {
+ Point2(0, +extents),
+ Point2(),
+ Point2(),
+ Point2(0, -extents)
+ };
// Use the axis color which is brighter for the positive axis.
// Use a darkened axis color for the negative axis.
// This makes it possible to see in which direction the Position3D node is rotated
// (which can be important depending on how it's used).
// Axis colors are taken from `axis_x_color` and `axis_y_color` (defined in `editor/editor_themes.cpp`).
- PackedColorArray colors_x;
const Color color_x = Color(0.96, 0.20, 0.32);
- colors_x.push_back(color_x);
- colors_x.push_back(color_x);
- colors_x.push_back(color_x.lerp(Color(0, 0, 0), 0.5));
- colors_x.push_back(color_x.lerp(Color(0, 0, 0), 0.5));
+ PackedColorArray colors_x = {
+ color_x,
+ color_x,
+ color_x.lerp(Color(0, 0, 0), 0.5),
+ color_x.lerp(Color(0, 0, 0), 0.5)
+ };
draw_multiline_colors(points_x, colors_x);
- PackedColorArray colors_y;
const Color color_y = Color(0.53, 0.84, 0.01);
- colors_y.push_back(color_y);
- colors_y.push_back(color_y);
- colors_y.push_back(color_y.lerp(Color(0, 0, 0), 0.5));
- colors_y.push_back(color_y.lerp(Color(0, 0, 0), 0.5));
+ PackedColorArray colors_y = {
+ color_y,
+ color_y,
+ color_y.lerp(Color(0, 0, 0), 0.5),
+ color_y.lerp(Color(0, 0, 0), 0.5)
+ };
draw_multiline_colors(points_y, colors_y);
}
@@ -86,6 +88,7 @@ void Position2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
update();
} break;
+
case NOTIFICATION_DRAW: {
if (!is_inside_tree()) {
break;
@@ -93,34 +96,24 @@ void Position2D::_notification(int p_what) {
if (Engine::get_singleton()->is_editor_hint()) {
_draw_cross();
}
-
} break;
}
}
void Position2D::set_gizmo_extents(real_t p_extents) {
- if (p_extents == DEFAULT_GIZMO_EXTENTS) {
- set_meta("_gizmo_extents_", Variant());
- } else {
- set_meta("_gizmo_extents_", p_extents);
- }
-
+ gizmo_extents = p_extents;
update();
}
real_t Position2D::get_gizmo_extents() const {
- if (has_meta("_gizmo_extents_")) {
- return get_meta("_gizmo_extents_");
- } else {
- return DEFAULT_GIZMO_EXTENTS;
- }
+ return gizmo_extents;
}
void Position2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_set_gizmo_extents", "extents"), &Position2D::set_gizmo_extents);
- ClassDB::bind_method(D_METHOD("_get_gizmo_extents"), &Position2D::get_gizmo_extents);
+ ClassDB::bind_method(D_METHOD("set_gizmo_extents", "extents"), &Position2D::set_gizmo_extents);
+ ClassDB::bind_method(D_METHOD("get_gizmo_extents"), &Position2D::get_gizmo_extents);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_gizmo_extents", "_get_gizmo_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater"), "set_gizmo_extents", "get_gizmo_extents");
}
Position2D::Position2D() {
diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h
index 9ed622c8f6..99b0266130 100644
--- a/scene/2d/position_2d.h
+++ b/scene/2d/position_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,6 +36,8 @@
class Position2D : public Node2D {
GDCLASS(Position2D, Node2D);
+ real_t gizmo_extents = 10.0;
+
void _draw_cross();
protected:
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index f9830a8743..37db9211e1 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,7 @@
#include "ray_cast_2d.h"
#include "collision_object_2d.h"
+#include "scene/resources/world_2d.h"
void RayCast2D::set_target_position(const Vector2 &p_point) {
target_position = p_point;
@@ -149,11 +150,11 @@ void RayCast2D::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (enabled) {
set_physics_process_internal(false);
}
-
} break;
case NOTIFICATION_DRAW: {
@@ -162,16 +163,13 @@ void RayCast2D::_notification(int p_what) {
break;
}
_draw_debug_shape();
-
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled) {
break;
}
-
_update_raycast_state();
-
} break;
}
}
@@ -244,15 +242,13 @@ void RayCast2D::_draw_debug_shape() {
xf.rotate(target_position.angle());
xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
- Vector<Vector2> pts;
- pts.push_back(xf.xform(Vector2(arrow_size, 0)));
- pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size)));
- pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size)));
+ Vector<Vector2> pts = {
+ xf.xform(Vector2(arrow_size, 0)),
+ xf.xform(Vector2(0, 0.5 * arrow_size)),
+ xf.xform(Vector2(0, -0.5 * arrow_size))
+ };
- Vector<Color> cols;
- for (int i = 0; i < 3; i++) {
- cols.push_back(draw_col);
- }
+ Vector<Color> cols = { draw_col, draw_col, draw_col };
draw_primitive(pts, cols, Vector<Vector2>());
}
@@ -265,30 +261,29 @@ void RayCast2D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void RayCast2D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
+void RayCast2D::add_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ add_exception_rid(p_node->get_rid());
}
void RayCast2D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void RayCast2D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- remove_exception_rid(co->get_rid());
+void RayCast2D::remove_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ remove_exception_rid(p_node->get_rid());
}
void RayCast2D::clear_exceptions() {
exclude.clear();
+
+ if (exclude_parent_body && is_inside_tree()) {
+ CollisionObject2D *parent = Object::cast_to<CollisionObject2D>(get_parent());
+ if (parent) {
+ exclude.insert(parent->get_rid());
+ }
+ }
}
void RayCast2D::set_collide_with_areas(bool p_enabled) {
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
index 3ee09fad32..b809bc4b8e 100644
--- a/scene/2d/ray_cast_2d.h
+++ b/scene/2d/ray_cast_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,8 @@
#include "scene/2d/node_2d.h"
+class CollisionObject2D;
+
class RayCast2D : public Node2D {
GDCLASS(RayCast2D, Node2D);
@@ -42,7 +44,7 @@ class RayCast2D : public Node2D {
int against_shape = 0;
Vector2 collision_point;
Vector2 collision_normal;
- Set<RID> exclude;
+ RBSet<RID> exclude;
uint32_t collision_mask = 1;
bool exclude_parent_body = true;
@@ -94,9 +96,9 @@ public:
Vector2 get_collision_normal() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject2D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject2D *p_node);
void clear_exceptions();
RayCast2D();
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index fe3e867424..6c4bfd58ce 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -115,8 +115,8 @@ void RemoteTransform2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_update_cache();
-
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
if (!is_inside_tree()) {
break;
@@ -125,7 +125,6 @@ void RemoteTransform2D::_notification(int p_what) {
if (cache.is_valid()) {
_update_remote();
}
-
} break;
}
}
@@ -188,7 +187,7 @@ TypedArray<String> RemoteTransform2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) {
- warnings.push_back(TTR("Path property must point to a valid Node2D node to work."));
+ warnings.push_back(RTR("Path property must point to a valid Node2D node to work."));
}
return warnings;
diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h
index 36fddb58c7..bd352e1054 100644
--- a/scene/2d/remote_transform_2d.h
+++ b/scene/2d/remote_transform_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
index 50b44eb4ef..a2f4b16ed3 100644
--- a/scene/2d/shape_cast_2d.cpp
+++ b/scene/2d/shape_cast_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -201,6 +201,7 @@ void ShapeCast2D::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (enabled) {
set_physics_process_internal(false);
@@ -223,7 +224,7 @@ void ShapeCast2D::_notification(int p_what) {
draw_col.g = g;
draw_col.b = g;
}
- // Draw continuos chain of shapes along the cast.
+ // Draw continuous chain of shapes along the cast.
const int steps = MAX(2, target_position.length() / shape->get_rect().get_size().length() * 4);
for (int i = 0; i <= steps; ++i) {
Vector2 t = (real_t(i) / steps) * target_position;
@@ -239,19 +240,22 @@ void ShapeCast2D::_notification(int p_what) {
xf.translate(Vector2(target_position.length(), 0));
draw_line(Vector2(), target_position, draw_col, 2);
- Vector<Vector2> pts;
+
float tsize = 8;
- pts.push_back(xf.xform(Vector2(tsize, 0)));
- pts.push_back(xf.xform(Vector2(0, Math_SQRT12 * tsize)));
- pts.push_back(xf.xform(Vector2(0, -Math_SQRT12 * tsize)));
- Vector<Color> cols;
- for (int i = 0; i < 3; i++)
- cols.push_back(draw_col);
+
+ Vector<Vector2> pts = {
+ xf.xform(Vector2(tsize, 0)),
+ xf.xform(Vector2(0, Math_SQRT12 * tsize)),
+ xf.xform(Vector2(0, -Math_SQRT12 * tsize))
+ };
+
+ Vector<Color> cols = { draw_col, draw_col, draw_col };
draw_primitive(pts, cols, Vector<Vector2>());
}
#endif
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled) {
break;
@@ -320,26 +324,18 @@ void ShapeCast2D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void ShapeCast2D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
+void ShapeCast2D::add_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ add_exception_rid(p_node->get_rid());
}
void ShapeCast2D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void ShapeCast2D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- remove_exception_rid(co->get_rid());
+void ShapeCast2D::remove_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ remove_exception_rid(p_node->get_rid());
}
void ShapeCast2D::clear_exceptions() {
@@ -386,7 +382,7 @@ TypedArray<String> ShapeCast2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node2D::get_configuration_warnings();
if (shape.is_null()) {
- warnings.push_back(TTR("This node cannot interact with other objects unless a Shape2D is assigned."));
+ warnings.push_back(RTR("This node cannot interact with other objects unless a Shape2D is assigned."));
}
return warnings;
}
diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h
index fca6b46155..78125b08bd 100644
--- a/scene/2d/shape_cast_2d.h
+++ b/scene/2d/shape_cast_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,9 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
+#include "scene/resources/world_2d.h"
+
+class CollisionObject2D;
class ShapeCast2D : public Node2D {
GDCLASS(ShapeCast2D, Node2D);
@@ -43,7 +46,7 @@ class ShapeCast2D : public Node2D {
RID shape_rid;
Vector2 target_position = Vector2(0, 50);
- Set<RID> exclude;
+ RBSet<RID> exclude;
real_t margin = 0.0;
uint32_t collision_mask = 1;
bool exclude_parent_body = true;
@@ -109,9 +112,9 @@ public:
real_t get_closest_collision_unsafe_fraction() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject2D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject2D *p_node);
void clear_exceptions();
TypedArray<String> get_configuration_warnings() const override;
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 63a0fb9b89..aa039e07ee 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,7 @@
#include "skeleton_2d.h"
#ifdef TOOLS_ENABLED
+#include "editor/editor_data.h"
#include "editor/editor_settings.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#endif //TOOLS_ENABLED
@@ -92,223 +93,228 @@ void Bone2D::_get_property_list(List<PropertyInfo> *p_list) const {
}
void Bone2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- Node *parent = get_parent();
- parent_bone = Object::cast_to<Bone2D>(parent);
- skeleton = nullptr;
- while (parent) {
- skeleton = Object::cast_to<Skeleton2D>(parent);
- if (skeleton) {
- break;
- }
- if (!Object::cast_to<Bone2D>(parent)) {
- break; //skeletons must be chained to Bone2Ds.
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ Node *parent = get_parent();
+ parent_bone = Object::cast_to<Bone2D>(parent);
+ skeleton = nullptr;
+ while (parent) {
+ skeleton = Object::cast_to<Skeleton2D>(parent);
+ if (skeleton) {
+ break;
+ }
+ if (!Object::cast_to<Bone2D>(parent)) {
+ break; //skeletons must be chained to Bone2Ds.
+ }
- parent = parent->get_parent();
- }
+ parent = parent->get_parent();
+ }
- if (skeleton) {
- Skeleton2D::Bone bone;
- bone.bone = this;
- skeleton->bones.push_back(bone);
- skeleton->_make_bone_setup_dirty();
- }
+ if (skeleton) {
+ Skeleton2D::Bone bone;
+ bone.bone = this;
+ skeleton->bones.push_back(bone);
+ skeleton->_make_bone_setup_dirty();
+ }
- cache_transform = get_transform();
- copy_transform_to_cache = true;
+ cache_transform = get_transform();
+ copy_transform_to_cache = true;
#ifdef TOOLS_ENABLED
- // Only draw the gizmo in the editor!
- if (Engine::get_singleton()->is_editor_hint() == false) {
- return;
- }
+ // Only draw the gizmo in the editor!
+ if (Engine::get_singleton()->is_editor_hint() == false) {
+ return;
+ }
- update();
+ update();
#endif // TOOLS_ENABLED
- }
+ } break;
- else if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
- if (skeleton) {
- skeleton->_make_transform_dirty();
- }
- if (copy_transform_to_cache) {
- cache_transform = get_transform();
- }
+ case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+ if (skeleton) {
+ skeleton->_make_transform_dirty();
+ }
+ if (copy_transform_to_cache) {
+ cache_transform = get_transform();
+ }
#ifdef TOOLS_ENABLED
- // Only draw the gizmo in the editor!
- if (Engine::get_singleton()->is_editor_hint() == false) {
- return;
- }
+ // Only draw the gizmo in the editor!
+ if (Engine::get_singleton()->is_editor_hint() == false) {
+ return;
+ }
- update();
+ update();
- if (get_parent()) {
- Bone2D *parent_bone = Object::cast_to<Bone2D>(get_parent());
- if (parent_bone) {
- parent_bone->update();
+ if (get_parent()) {
+ Bone2D *parent_bone = Object::cast_to<Bone2D>(get_parent());
+ if (parent_bone) {
+ parent_bone->update();
+ }
}
- }
#endif // TOOLS_ENABLED
- }
+ } break;
- else if (p_what == NOTIFICATION_MOVED_IN_PARENT) {
- if (skeleton) {
- skeleton->_make_bone_setup_dirty();
- }
- if (copy_transform_to_cache) {
- cache_transform = get_transform();
- }
- }
+ case NOTIFICATION_MOVED_IN_PARENT: {
+ if (skeleton) {
+ skeleton->_make_bone_setup_dirty();
+ }
+ if (copy_transform_to_cache) {
+ cache_transform = get_transform();
+ }
+ } break;
- else if (p_what == NOTIFICATION_EXIT_TREE) {
- if (skeleton) {
- for (int i = 0; i < skeleton->bones.size(); i++) {
- if (skeleton->bones[i].bone == this) {
- skeleton->bones.remove(i);
- break;
+ case NOTIFICATION_EXIT_TREE: {
+ if (skeleton) {
+ for (int i = 0; i < skeleton->bones.size(); i++) {
+ if (skeleton->bones[i].bone == this) {
+ skeleton->bones.remove_at(i);
+ break;
+ }
}
+ skeleton->_make_bone_setup_dirty();
+ skeleton = nullptr;
}
- skeleton->_make_bone_setup_dirty();
- skeleton = nullptr;
- }
- parent_bone = nullptr;
- set_transform(cache_transform);
- }
+ parent_bone = nullptr;
+ set_transform(cache_transform);
+ } break;
+
+ case NOTIFICATION_READY: {
+ if (autocalculate_length_and_angle) {
+ calculate_length_and_rotation();
+ }
+ } break;
- else if (p_what == NOTIFICATION_READY) {
- if (autocalculate_length_and_angle) {
- calculate_length_and_rotation();
- }
- }
#ifdef TOOLS_ENABLED
- else if (p_what == NOTIFICATION_EDITOR_PRE_SAVE || p_what == NOTIFICATION_EDITOR_POST_SAVE) {
- Transform2D tmp_trans = get_transform();
- set_transform(cache_transform);
- cache_transform = tmp_trans;
- }
- // Bone2D Editor gizmo drawing:
+ case NOTIFICATION_EDITOR_PRE_SAVE:
+ case NOTIFICATION_EDITOR_POST_SAVE: {
+ Transform2D tmp_trans = get_transform();
+ set_transform(cache_transform);
+ cache_transform = tmp_trans;
+ } break;
+
+ // Bone2D Editor gizmo drawing:
#ifndef _MSC_VER
#warning TODO Bone2D gizmo drawing needs to be moved to an editor plugin
#endif
- else if (p_what == NOTIFICATION_DRAW) {
- // Only draw the gizmo in the editor!
- if (Engine::get_singleton()->is_editor_hint() == false) {
- return;
- }
-
- if (editor_gizmo_rid.is_null()) {
- editor_gizmo_rid = RenderingServer::get_singleton()->canvas_item_create();
- RenderingServer::get_singleton()->canvas_item_set_parent(editor_gizmo_rid, get_canvas_item());
- RenderingServer::get_singleton()->canvas_item_set_z_as_relative_to_parent(editor_gizmo_rid, true);
- RenderingServer::get_singleton()->canvas_item_set_z_index(editor_gizmo_rid, 10);
- }
- RenderingServer::get_singleton()->canvas_item_clear(editor_gizmo_rid);
-
- if (!_editor_show_bone_gizmo) {
- return;
- }
-
- // Undo scaling
- Transform2D editor_gizmo_trans = Transform2D();
- editor_gizmo_trans.set_scale(Vector2(1, 1) / get_global_scale());
- RenderingServer::get_singleton()->canvas_item_set_transform(editor_gizmo_rid, editor_gizmo_trans);
-
- Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1");
- Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2");
- Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
- Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color");
- Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color");
-
- bool Bone2D_found = false;
- for (int i = 0; i < get_child_count(); i++) {
- Bone2D *child_node = nullptr;
- child_node = Object::cast_to<Bone2D>(get_child(i));
- if (!child_node) {
- continue;
+ case NOTIFICATION_DRAW: {
+ // Only draw the gizmo in the editor!
+ if (Engine::get_singleton()->is_editor_hint() == false) {
+ return;
}
- Bone2D_found = true;
-
- Vector<Vector2> bone_shape;
- Vector<Vector2> bone_shape_outline;
- _editor_get_bone_shape(&bone_shape, &bone_shape_outline, child_node);
-
- Vector<Color> colors;
- if (has_meta("_local_pose_override_enabled_")) {
- colors.push_back(bone_ik_color);
- colors.push_back(bone_ik_color);
- colors.push_back(bone_ik_color);
- colors.push_back(bone_ik_color);
- } else {
- colors.push_back(bone_color1);
- colors.push_back(bone_color2);
- colors.push_back(bone_color1);
- colors.push_back(bone_color2);
+ if (editor_gizmo_rid.is_null()) {
+ editor_gizmo_rid = RenderingServer::get_singleton()->canvas_item_create();
+ RenderingServer::get_singleton()->canvas_item_set_parent(editor_gizmo_rid, get_canvas_item());
+ RenderingServer::get_singleton()->canvas_item_set_z_as_relative_to_parent(editor_gizmo_rid, true);
+ RenderingServer::get_singleton()->canvas_item_set_z_index(editor_gizmo_rid, 10);
}
+ RenderingServer::get_singleton()->canvas_item_clear(editor_gizmo_rid);
- Vector<Color> outline_colors;
- if (CanvasItemEditor::get_singleton()->editor_selection->is_selected(this)) {
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- } else {
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
+ if (!_editor_show_bone_gizmo) {
+ return;
}
- RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape_outline, outline_colors);
- RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape, colors);
- }
-
- if (!Bone2D_found) {
- Vector<Vector2> bone_shape;
- Vector<Vector2> bone_shape_outline;
+ // Undo scaling
+ Transform2D editor_gizmo_trans = Transform2D();
+ editor_gizmo_trans.set_scale(Vector2(1, 1) / get_global_scale());
+ RenderingServer::get_singleton()->canvas_item_set_transform(editor_gizmo_rid, editor_gizmo_trans);
+
+ Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1");
+ Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2");
+ Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
+ Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color");
+ Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color");
+
+ bool Bone2D_found = false;
+ for (int i = 0; i < get_child_count(); i++) {
+ Bone2D *child_node = nullptr;
+ child_node = Object::cast_to<Bone2D>(get_child(i));
+ if (!child_node) {
+ continue;
+ }
+ Bone2D_found = true;
+
+ Vector<Vector2> bone_shape;
+ Vector<Vector2> bone_shape_outline;
+
+ _editor_get_bone_shape(&bone_shape, &bone_shape_outline, child_node);
+
+ Vector<Color> colors;
+ if (has_meta("_local_pose_override_enabled_")) {
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ } else {
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ }
- _editor_get_bone_shape(&bone_shape, &bone_shape_outline, nullptr);
+ Vector<Color> outline_colors;
+ if (CanvasItemEditor::get_singleton()->editor_selection->is_selected(this)) {
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ } else {
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ }
- Vector<Color> colors;
- if (has_meta("_local_pose_override_enabled_")) {
- colors.push_back(bone_ik_color);
- colors.push_back(bone_ik_color);
- colors.push_back(bone_ik_color);
- colors.push_back(bone_ik_color);
- } else {
- colors.push_back(bone_color1);
- colors.push_back(bone_color2);
- colors.push_back(bone_color1);
- colors.push_back(bone_color2);
+ RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape_outline, outline_colors);
+ RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape, colors);
}
- Vector<Color> outline_colors;
- if (CanvasItemEditor::get_singleton()->editor_selection->is_selected(this)) {
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- outline_colors.push_back(bone_selected_color);
- } else {
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- outline_colors.push_back(bone_outline_color);
- }
+ if (!Bone2D_found) {
+ Vector<Vector2> bone_shape;
+ Vector<Vector2> bone_shape_outline;
+
+ _editor_get_bone_shape(&bone_shape, &bone_shape_outline, nullptr);
+
+ Vector<Color> colors;
+ if (has_meta("_local_pose_override_enabled_")) {
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ } else {
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ }
- RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape_outline, outline_colors);
- RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape, colors);
- }
+ Vector<Color> outline_colors;
+ if (CanvasItemEditor::get_singleton()->editor_selection->is_selected(this)) {
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ } else {
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ }
+
+ RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape_outline, outline_colors);
+ RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape, colors);
+ }
+ } break;
+#endif // TOOLS_ENABLED
}
-#endif // TOOLS_ENALBED
}
#ifdef TOOLS_ENABLED
@@ -432,14 +438,14 @@ TypedArray<String> Bone2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!skeleton) {
if (parent_bone) {
- warnings.push_back(TTR("This Bone2D chain should end at a Skeleton2D node."));
+ warnings.push_back(RTR("This Bone2D chain should end at a Skeleton2D node."));
} else {
- warnings.push_back(TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node."));
+ warnings.push_back(RTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node."));
}
}
if (rest == Transform2D(0, 0, 0, 0, 0, 0)) {
- warnings.push_back(TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one."));
+ warnings.push_back(RTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one."));
}
return warnings;
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 56fd0e8504..98fb867d99 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp
index b2302d09db..facd164a0e 100644
--- a/scene/2d/sprite_2d.cpp
+++ b/scene/2d/sprite_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -122,11 +122,6 @@ void Sprite2D::_notification(int p_what) {
RID ci = get_canvas_item();
- /*
- texture->draw(ci,Point2());
- break;
- */
-
Rect2 src_rect, dst_rect;
bool filter_clip_enabled;
_get_rects(src_rect, dst_rect, filter_clip_enabled);
diff --git a/scene/2d/sprite_2d.h b/scene/2d/sprite_2d.h
index 49df78c59d..6893e92d4a 100644
--- a/scene/2d/sprite_2d.h
+++ b/scene/2d/sprite_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 96c4164721..19c341c1e1 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,11 +31,11 @@
#include "tile_map.h"
#include "core/io/marshalls.h"
-
+#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
-Map<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const {
- Map<Vector2i, TileSet::CellNeighbor> output;
+HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const {
+ HashMap<Vector2i, TileSet::CellNeighbor> output;
Ref<TileSet> tile_set = tile_map->get_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
@@ -489,6 +489,7 @@ void TileMap::_notification(int p_what) {
_clear_internals();
_recreate_internals();
} break;
+
case NOTIFICATION_EXIT_TREE: {
_clear_internals();
} break;
@@ -576,7 +577,7 @@ void TileMap::move_layer(int p_layer, int p_to_pos) {
TileMapLayer tl = layers[p_layer];
layers.insert(p_to_pos, tl);
- layers.remove(p_to_pos < p_layer ? p_layer + 1 : p_layer);
+ layers.remove_at(p_to_pos < p_layer ? p_layer + 1 : p_layer);
_recreate_internals();
notify_property_list_changed();
@@ -595,7 +596,7 @@ void TileMap::remove_layer(int p_layer) {
// Clear before removing the layer.
_clear_internals();
- layers.remove(p_layer);
+ layers.remove_at(p_layer);
_recreate_internals();
notify_property_list_changed();
@@ -741,7 +742,7 @@ Vector2i TileMap::_coords_to_quadrant_coords(int p_layer, const Vector2i &p_coor
p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size);
}
-Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer, const Vector2i &p_qk) {
+HashMap<Vector2i, TileMapQuadrant>::Iterator TileMap::_create_quadrant(int p_layer, const Vector2i &p_qk) {
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr);
TileMapQuadrant q;
@@ -764,9 +765,9 @@ Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer,
return layers[p_layer].quadrant_map.insert(p_qk, q);
}
-void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q) {
+void TileMap::_make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) {
// Make the given quadrant dirty, then trigger an update later.
- TileMapQuadrant &q = Q->get();
+ TileMapQuadrant &q = Q->value;
if (!q.dirty_list_element.in_list()) {
layers[q.layer].dirty_quadrant_list.add(&q.dirty_list_element);
}
@@ -809,7 +810,7 @@ void TileMap::_update_dirty_quadrants() {
for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
q->self()->map_to_world.clear();
q->self()->world_to_map.clear();
- for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) {
+ for (RBSet<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) {
Vector2i pk = E->get();
Vector2i pk_world_coords = map_to_world(pk);
q->self()->map_to_world[pk] = pk_world_coords;
@@ -866,22 +867,22 @@ void TileMap::_recreate_layer_internals(int p_layer) {
return;
}
- // Upadate the layer internals.
+ // Update the layer internals.
_rendering_update_layer(p_layer);
// Recreate the quadrants.
- const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
+ const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
Vector2i qk = _coords_to_quadrant_coords(p_layer, Vector2i(E.key.x, E.key.y));
- Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk);
+ HashMap<Vector2i, TileMapQuadrant>::Iterator Q = layers[p_layer].quadrant_map.find(qk);
if (!Q) {
Q = _create_quadrant(p_layer, qk);
- layers[p_layer].dirty_quadrant_list.add(&Q->get().dirty_list_element);
+ layers[p_layer].dirty_quadrant_list.add(&Q->value.dirty_list_element);
}
Vector2i pk = E.key;
- Q->get().cells.insert(pk);
+ Q->value.cells.insert(pk);
_make_quadrant_dirty(Q);
}
@@ -895,9 +896,9 @@ void TileMap::_recreate_internals() {
}
}
-void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) {
+void TileMap::_erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) {
// Remove a quadrant.
- TileMapQuadrant *q = &(Q->get());
+ TileMapQuadrant *q = &(Q->value);
// Call the cleanup_quadrant method on plugins.
if (tile_set.is_valid()) {
@@ -916,7 +917,7 @@ void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) {
RenderingServer *rs = RenderingServer::get_singleton();
rs->free(q->debug_canvas_item);
- layers[q->layer].quadrant_map.erase(Q);
+ layers[q->layer].quadrant_map.remove(Q);
rect_cache_dirty = true;
}
@@ -925,7 +926,7 @@ void TileMap::_clear_layer_internals(int p_layer) {
// Clear quadrants.
while (layers[p_layer].quadrant_map.size()) {
- _erase_quadrant(layers[p_layer].quadrant_map.front());
+ _erase_quadrant(layers[p_layer].quadrant_map.begin());
}
// Clear the layers internals.
@@ -953,15 +954,17 @@ void TileMap::_recompute_rect_cache() {
}
Rect2 r_total;
+ bool first = true;
for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (const Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
Rect2 r;
- r.position = map_to_world(E->key() * get_effective_quadrant_size(layer));
- r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size(layer)));
- r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size(layer)));
- r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size(layer)));
- if (E == layers[layer].quadrant_map.front()) {
+ r.position = map_to_world(E.key * get_effective_quadrant_size(layer));
+ r.expand_to(map_to_world((E.key + Vector2i(1, 0)) * get_effective_quadrant_size(layer)));
+ r.expand_to(map_to_world((E.key + Vector2i(1, 1)) * get_effective_quadrant_size(layer)));
+ r.expand_to(map_to_world((E.key + Vector2i(0, 1)) * get_effective_quadrant_size(layer)));
+ if (first) {
r_total = r;
+ first = false;
} else {
r_total = r_total.merge(r);
}
@@ -980,7 +983,7 @@ void TileMap::_recompute_rect_cache() {
void TileMap::_rendering_notification(int p_what) {
switch (p_what) {
- case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
+ case NOTIFICATION_VISIBILITY_CHANGED: {
bool visible = is_visible_in_tree();
for (int layer = 0; layer < (int)layers.size(); layer++) {
for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) {
@@ -997,7 +1000,8 @@ void TileMap::_rendering_notification(int p_what) {
}
}
} break;
- case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
if (!is_inside_tree()) {
return;
}
@@ -1016,7 +1020,8 @@ void TileMap::_rendering_notification(int p_what) {
}
}
} break;
- case CanvasItem::NOTIFICATION_DRAW: {
+
+ case NOTIFICATION_DRAW: {
if (tile_set.is_valid()) {
RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled());
}
@@ -1119,7 +1124,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
if (q.runtime_tile_data_cache.has(E_cell.value)) {
tile_data = q.runtime_tile_data_cache[E_cell.value];
} else {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
Ref<ShaderMaterial> mat = tile_data->get_material();
@@ -1198,7 +1203,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
for (int layer = 0; layer < (int)layers.size(); layer++) {
// Sort the quadrants coords per world coordinates
- Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map;
+ RBMap<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map;
for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
world_to_map[map_to_world(E.key)] = E.key;
}
@@ -1245,7 +1250,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
// Draw a placeholder for scenes needing one.
RenderingServer *rs = RenderingServer::get_singleton();
Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
- for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ for (RBSet<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true);
TileSetSource *source;
@@ -1311,7 +1316,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
}
// Get tile data.
- const TileData *tile_data = p_tile_data_override ? p_tile_data_override : Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
+ const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile);
// Get the tile modulation.
Color modulate = tile_data->get_modulate() * p_modulation;
@@ -1369,20 +1374,21 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
void TileMap::_physics_notification(int p_what) {
switch (p_what) {
- case CanvasItem::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
bool in_editor = false;
#ifdef TOOLS_ENABLED
in_editor = Engine::get_singleton()->is_editor_hint();
#endif
if (is_inside_tree() && collision_animatable && !in_editor) {
- // Update tranform on the physics tick when in animatable mode.
+ // Update transform on the physics tick when in animatable mode.
last_valid_transform = new_transform;
set_notify_local_transform(false);
set_global_transform(new_transform);
set_notify_local_transform(true);
}
} break;
- case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
bool in_editor = false;
#ifdef TOOLS_ENABLED
in_editor = Engine::get_singleton()->is_editor_hint();
@@ -1404,6 +1410,7 @@ void TileMap::_physics_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
bool in_editor = false;
#ifdef TOOLS_ENABLED
@@ -1457,7 +1464,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
q.bodies.clear();
// Recreate bodies and shapes.
- for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ for (RBSet<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
TileMapCell c = get_cell(q.layer, E_cell->get(), true);
TileSetSource *source;
@@ -1474,7 +1481,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
if (q.runtime_tile_data_cache.has(E_cell->get())) {
tile_data = q.runtime_tile_data_cache[E_cell->get()];
} else {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) {
Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer);
@@ -1600,7 +1607,7 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
void TileMap::_navigation_notification(int p_what) {
switch (p_what) {
- case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+ case NOTIFICATION_TRANSFORM_CHANGED: {
if (is_inside_tree()) {
for (int layer = 0; layer < (int)layers.size(); layer++) {
Transform2D tilemap_xform = get_global_transform();
@@ -1654,7 +1661,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
q.navigation_regions.clear();
// Get the navigation polygons and create regions.
- for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ for (RBSet<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
TileMapCell c = get_cell(q.layer, E_cell->get(), true);
TileSetSource *source;
@@ -1671,7 +1678,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
if (q.runtime_tile_data_cache.has(E_cell->get())) {
tile_data = q.runtime_tile_data_cache[E_cell->get()];
} else {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count());
@@ -1743,7 +1750,7 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
- for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ for (RBSet<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true);
TileSetSource *source;
@@ -1760,7 +1767,7 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
if (p_quadrant->runtime_tile_data_cache.has(E_cell->get())) {
tile_data = p_quadrant->runtime_tile_data_cache[E_cell->get()];
} else {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
Transform2D xform;
@@ -1809,7 +1816,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
// Clear the scenes.
for (const KeyValue<Vector2i, String> &E : q.scenes) {
- Node *node = get_node(E.value);
+ Node *node = get_node_or_null(E.value);
if (node) {
node->queue_delete();
}
@@ -1818,7 +1825,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
q.scenes.clear();
// Recreate the scenes.
- for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ for (RBSet<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
const TileMapCell &c = get_cell(q.layer, E_cell->get(), true);
TileSetSource *source;
@@ -1857,7 +1864,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Clear the scenes.
for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) {
- Node *node = get_node(E.value);
+ Node *node = get_node_or_null(E.value);
if (node) {
node->queue_delete();
}
@@ -1876,7 +1883,7 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
// Draw a placeholder for scenes needing one.
RenderingServer *rs = RenderingServer::get_singleton();
Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
- for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ for (RBSet<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true);
TileSetSource *source;
@@ -1918,9 +1925,9 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c
ERR_FAIL_INDEX(p_layer, (int)layers.size());
// Set the current cell tile (using integer position).
- Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
+ HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
Vector2i pk(p_coords);
- Map<Vector2i, TileMapCell>::Element *E = tile_map.find(pk);
+ HashMap<Vector2i, TileMapCell>::Iterator E = tile_map.find(pk);
int source_id = p_source_id;
Vector2i atlas_coords = p_atlas_coords;
@@ -1928,7 +1935,7 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c
if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) &&
(source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) {
- WARN_PRINT("Setting a cell a cell as empty requires both source_id, atlas_coord and alternative_tile to be set to their respective \"invalid\" values. Values were thus changes accordingly.");
+ WARN_PRINT("Setting a cell as empty requires both source_id, atlas_coord and alternative_tile to be set to their respective \"invalid\" values. Values were thus changes accordingly.");
source_id = TileSet::INVALID_SOURCE;
atlas_coords = TileSetSource::INVALID_ATLAS_COORDS;
alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
@@ -1941,7 +1948,7 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c
// Get the quadrant
Vector2i qk = _coords_to_quadrant_coords(p_layer, pk);
- Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk);
+ HashMap<Vector2i, TileMapQuadrant>::Iterator Q = layers[p_layer].quadrant_map.find(qk);
if (source_id == TileSet::INVALID_SOURCE) {
// Erase existing cell in the tile map.
@@ -1949,7 +1956,7 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c
// Erase existing cell in the quadrant.
ERR_FAIL_COND(!Q);
- TileMapQuadrant &q = Q->get();
+ TileMapQuadrant &q = Q->value;
q.cells.erase(pk);
@@ -1970,18 +1977,18 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c
if (!Q) {
Q = _create_quadrant(p_layer, qk);
}
- TileMapQuadrant &q = Q->get();
+ TileMapQuadrant &q = Q->value;
q.cells.insert(pk);
} else {
ERR_FAIL_COND(!Q); // TileMapQuadrant should exist...
- if (E->get().source_id == source_id && E->get().get_atlas_coords() == atlas_coords && E->get().alternative_tile == alternative_tile) {
+ if (E->value.source_id == source_id && E->value.get_atlas_coords() == atlas_coords && E->value.alternative_tile == alternative_tile) {
return; // Nothing changed.
}
}
- TileMapCell &c = E->get();
+ TileMapCell &c = E->value;
c.source_id = source_id;
c.set_atlas_coords(atlas_coords);
@@ -1992,61 +1999,65 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c
}
}
+void TileMap::erase_cell(int p_layer, const Vector2i &p_coords) {
+ set_cell(p_layer, p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+}
+
int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSet::INVALID_SOURCE);
// Get a cell source id from position
- const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
+ const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
+ HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
if (!E) {
return TileSet::INVALID_SOURCE;
}
if (p_use_proxies && tile_set.is_valid()) {
- Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
return proxyed[0];
}
- return E->get().source_id;
+ return E->value.source_id;
}
Vector2i TileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_ATLAS_COORDS);
// Get a cell source id from position
- const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
+ const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
+ HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
if (!E) {
return TileSetSource::INVALID_ATLAS_COORDS;
}
if (p_use_proxies && tile_set.is_valid()) {
- Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
return proxyed[1];
}
- return E->get().get_atlas_coords();
+ return E->value.get_atlas_coords();
}
int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_TILE_ALTERNATIVE);
// Get a cell source id from position
- const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
+ const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
+ HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
if (!E) {
return TileSetSource::INVALID_TILE_ALTERNATIVE;
}
if (p_use_proxies && tile_set.is_valid()) {
- Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
return proxyed[2];
}
- return E->get().alternative_tile;
+ return E->value.alternative_tile;
}
Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) {
@@ -2104,6 +2115,7 @@ Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coo
}
Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, Ref<TileMapPattern> p_pattern) {
+ ERR_FAIL_COND_V(p_pattern.is_null(), Vector2i());
ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i());
Vector2i output = p_position_in_tilemap + p_coords_in_pattern;
@@ -2133,17 +2145,17 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat
TypedArray<Vector2i> used_cells = p_pattern->get_used_cells();
for (int i = 0; i < used_cells.size(); i++) {
Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern);
- set_cell(p_layer, coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords));
+ set_cell(p_layer, coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i]));
}
}
-Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints) {
+RBSet<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints) {
if (!tile_set.is_valid()) {
- return Set<TileSet::TerrainsPattern>();
+ return RBSet<TileSet::TerrainsPattern>();
}
// Returns all tiles compatible with the given constraints.
- Set<TileSet::TerrainsPattern> compatible_terrain_tile_patterns;
+ RBSet<TileSet::TerrainsPattern> compatible_terrain_tile_patterns;
for (TileSet::TerrainsPattern &terrain_pattern : tile_set->get_terrains_pattern_set(p_terrain_set)) {
int valid = true;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
@@ -2151,7 +2163,7 @@ Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constrai
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
// Check if the bit is compatible with the constraints.
TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain(bit));
- Set<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
+ RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
valid = false;
break;
@@ -2167,17 +2179,17 @@ Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constrai
return compatible_terrain_tile_patterns;
}
-Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains) const {
+RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_cells_list(int p_layer, const RBSet<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains) const {
if (!tile_set.is_valid()) {
- return Set<TerrainConstraint>();
+ return RBSet<TerrainConstraint>();
}
- ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<TerrainConstraint>());
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Set<TerrainConstraint>());
+ ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), RBSet<TerrainConstraint>());
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), RBSet<TerrainConstraint>());
// Build a set of dummy constraints get the constrained points.
- Set<TerrainConstraint> dummy_constraints;
- for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
+ RBSet<TerrainConstraint> dummy_constraints;
+ for (RBSet<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
@@ -2187,14 +2199,14 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_ce
}
// For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
- Set<TerrainConstraint> constraints;
- for (Set<TerrainConstraint>::Element *E = dummy_constraints.front(); E; E = E->next()) {
+ RBSet<TerrainConstraint> constraints;
+ for (RBSet<TerrainConstraint>::Element *E = dummy_constraints.front(); E; E = E->next()) {
TerrainConstraint c = E->get();
- Map<int, int> terrain_count;
+ HashMap<int, int> terrain_count;
// Count the number of occurrences per terrain.
- Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
+ HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
if (!p_to_replace.has(E_overlapping.key)) {
TileData *neighbor_tile_data = nullptr;
@@ -2203,7 +2215,7 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_ce
Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile);
if (tile_data && tile_data->get_terrain_set() == p_terrain_set) {
neighbor_tile_data = tile_data;
}
@@ -2240,13 +2252,13 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_ce
return constraints;
}
-Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
+RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
if (!tile_set.is_valid()) {
- return Set<TerrainConstraint>();
+ return RBSet<TerrainConstraint>();
}
// Compute the constraints needed from the surrounding tiles.
- Set<TerrainConstraint> output;
+ RBSet<TerrainConstraint> output;
for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
@@ -2258,35 +2270,35 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile
return output;
}
-Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints) {
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(const RBSet<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints) {
if (!tile_set.is_valid()) {
- return Map<Vector2i, TileSet::TerrainsPattern>();
+ return HashMap<Vector2i, TileSet::TerrainsPattern>();
}
// Copy the constraints set.
- Set<TerrainConstraint> constraints = p_constraints;
+ RBSet<TerrainConstraint> constraints = p_constraints;
// Compute all acceptable patterns for each cell.
- Map<Vector2i, Set<TileSet::TerrainsPattern>> per_cell_acceptable_tiles;
+ HashMap<Vector2i, RBSet<TileSet::TerrainsPattern>> per_cell_acceptable_tiles;
for (Vector2i cell : p_to_replace) {
per_cell_acceptable_tiles[cell] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, cell, constraints);
}
// Output map.
- Map<Vector2i, TileSet::TerrainsPattern> output;
+ HashMap<Vector2i, TileSet::TerrainsPattern> output;
// Add all positions to a set.
- Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace);
+ RBSet<Vector2i> to_replace = RBSet<Vector2i>(p_to_replace);
while (!to_replace.is_empty()) {
// Compute the minimum number of tile possibilities for each cell.
int min_nb_possibilities = 100000000;
- for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
+ for (const KeyValue<Vector2i, RBSet<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
min_nb_possibilities = MIN(min_nb_possibilities, E.value.size());
}
// Get the set of possible cells to fill, out of the most constrained ones.
LocalVector<Vector2i> to_choose_from;
- for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
+ for (const KeyValue<Vector2i, RBSet<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
if (E.value.size() == min_nb_possibilities) {
to_choose_from.push_back(E.key);
}
@@ -2295,8 +2307,8 @@ Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(
// Randomly a cell to fill out of the most constrained.
Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)];
- // Get the list of acceptable pattens for the given cell.
- Set<TileSet::TerrainsPattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
+ // Get the list of acceptable patterns for the given cell.
+ RBSet<TileSet::TerrainsPattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
if (valid_tiles.is_empty()) {
break; // No possibilities :/
}
@@ -2307,7 +2319,7 @@ Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(
LocalVector<int> terrains_counts;
int pattern_index = 0;
for (const TileSet::TerrainsPattern &pattern : valid_tiles) {
- Set<int> terrains;
+ RBSet<int> terrains;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
@@ -2335,8 +2347,8 @@ Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(
per_cell_acceptable_tiles.erase(selected_cell_to_replace);
// Add the new constraints from the added tiles.
- Set<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
- for (Set<TerrainConstraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) {
+ RBSet<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
+ for (RBSet<TerrainConstraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) {
constraints.insert(E_constraint->get());
}
@@ -2359,14 +2371,14 @@ void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector
ERR_FAIL_INDEX(p_layer, (int)layers.size());
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
- Set<Vector2i> coords_set;
+ RBSet<Vector2i> coords_set;
for (int i = 0; i < p_coords_array.size(); i++) {
coords_set.insert(p_coords_array[i]);
}
- Set<TileMap::TerrainConstraint> constraints = get_terrain_constraints_from_removed_cells_list(p_layer, coords_set, p_terrain_set, p_ignore_empty_terrains);
+ RBSet<TileMap::TerrainConstraint> constraints = get_terrain_constraints_from_removed_cells_list(p_layer, coords_set, p_terrain_set, p_ignore_empty_terrains);
- Map<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints);
+ HashMap<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints);
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : wfc_output) {
TileMapCell cell = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
set_cell(p_layer, kv.key, cell.source_id, cell.get_atlas_coords(), cell.alternative_tile);
@@ -2375,11 +2387,11 @@ void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector
TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell());
- const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
+ const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
if (!tile_map.has(p_coords)) {
return TileMapCell();
} else {
- TileMapCell c = tile_map.find(p_coords)->get();
+ TileMapCell c = tile_map.find(p_coords)->value;
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile);
c.source_id = proxyed[0];
@@ -2390,7 +2402,7 @@ TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_
}
}
-Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) {
+HashMap<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) {
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr);
return &layers[p_layer].quadrant_map;
@@ -2405,15 +2417,15 @@ void TileMap::fix_invalid_tiles() {
ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
for (unsigned int i = 0; i < layers.size(); i++) {
- const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
- Set<Vector2i> coords;
+ const HashMap<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
+ RBSet<Vector2i> coords;
for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
TileSetSource *source = *tile_set->get_source(E.value.source_id);
if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) {
coords.insert(E.key);
}
}
- for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) {
+ for (RBSet<Vector2i>::Element *E = coords.front(); E; E = E->next()) {
set_cell(i, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
}
}
@@ -2502,10 +2514,10 @@ void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) {
uint32_t v = decode_uint32(&local[4]);
// Extract the transform flags that used to be in the tilemap.
- bool flip_h = v & (1 << 29);
- bool flip_v = v & (1 << 30);
- bool transpose = v & (1 << 31);
- v &= (1 << 29) - 1;
+ bool flip_h = v & (1UL << 29);
+ bool flip_v = v & (1UL << 30);
+ bool transpose = v & (1UL << 31);
+ v &= (1UL << 29) - 1;
// Extract autotile/atlas coords.
int16_t coord_x = 0;
@@ -2536,7 +2548,7 @@ Vector<int> TileMap::_get_tile_data(int p_layer) const {
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Vector<int>());
// Export tile data to raw format
- const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
+ const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
Vector<int> data;
data.resize(tile_map.size() * 3);
int *w = data.ptrw();
@@ -2579,7 +2591,7 @@ void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r
if (atlas_source) {
bool ret = false;
if (GDVIRTUAL_CALL(_use_tile_data_runtime_update, q.layer, E_cell.value, ret) && ret) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
// Create the runtime TileData.
TileData *tile_data_runtime_use = tile_data->duplicate();
@@ -2724,7 +2736,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
}
Vector2 TileMap::map_to_world(const Vector2i &p_pos) const {
- // SHOULD RETURN THE CENTER OF THE TILE
+ // SHOULD RETURN THE CENTER OF THE CELL
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2());
Vector2 ret = p_pos;
@@ -3365,10 +3377,10 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache
used_rect_cache = Rect2i();
for (unsigned int i = 0; i < layers.size(); i++) {
- const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
+ const HashMap<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
if (tile_map.size() > 0) {
if (first) {
- used_rect_cache = Rect2i(tile_map.front()->key().x, tile_map.front()->key().y, 0, 0);
+ used_rect_cache = Rect2i(tile_map.begin()->key.x, tile_map.begin()->key.y, 0, 0);
first = false;
}
@@ -3438,8 +3450,8 @@ void TileMap::set_texture_filter(TextureFilter p_texture_filter) {
// Set a default texture filter for the whole tilemap
CanvasItem::set_texture_filter(p_texture_filter);
for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *F = layers[layer].quadrant_map.front(); F; F = F->next()) {
- TileMapQuadrant &q = F->get();
+ for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) {
+ TileMapQuadrant &q = F->value;
for (const RID &ci : q.canvas_items) {
RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(p_texture_filter));
_make_quadrant_dirty(F);
@@ -3453,8 +3465,8 @@ void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) {
// Set a default texture repeat for the whole tilemap
CanvasItem::set_texture_repeat(p_texture_repeat);
for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *F = layers[layer].quadrant_map.front(); F; F = F->next()) {
- TileMapQuadrant &q = F->get();
+ for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) {
+ TileMapQuadrant &q = F->value;
for (const RID &ci : q.canvas_items) {
RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(p_texture_repeat));
_make_quadrant_dirty(F);
@@ -3502,7 +3514,7 @@ TypedArray<Vector2i> TileMap::get_surrounding_tiles(Vector2i coords) {
return around;
}
-void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform) {
+void TileMap::draw_cells_outline(Control *p_control, RBSet<Vector2i> p_cells, Color p_color, Transform2D p_transform) {
if (!tile_set.is_valid()) {
return;
}
@@ -3512,7 +3524,7 @@ void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Colo
Vector<Vector2> polygon = tile_set->get_tile_shape_polygon();
TileSet::TileShape shape = tile_set->get_tile_shape();
- for (Set<Vector2i>::Element *E = p_cells.front(); E; E = E->next()) {
+ for (RBSet<Vector2i>::Element *E = p_cells.front(); E; E = E->next()) {
Vector2 center = map_to_world(E->get());
#define DRAW_SIDE_IF_NEEDED(side, polygon_index_from, polygon_index_to) \
@@ -3566,7 +3578,7 @@ TypedArray<String> TileMap::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
// Retrieve the set of Z index values with a Y-sorted layer.
- Set<int> y_sorted_z_index;
+ RBSet<int> y_sorted_z_index;
for (int layer = 0; layer < (int)layers.size(); layer++) {
if (layers[layer].y_sort_enabled) {
y_sorted_z_index.insert(layers[layer].z_index);
@@ -3576,7 +3588,7 @@ TypedArray<String> TileMap::get_configuration_warnings() const {
// Check if we have a non-sorted layer in a Z-index with a Y-sorted layer.
for (int layer = 0; layer < (int)layers.size(); layer++) {
if (!layers[layer].y_sort_enabled && y_sorted_z_index.has(layers[layer].z_index)) {
- warnings.push_back(TTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers."));
+ warnings.push_back(RTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers."));
break;
}
}
@@ -3616,7 +3628,8 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "navigation_visibility_mode"), &TileMap::set_navigation_visibility_mode);
ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::get_navigation_visibility_mode);
- ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("erase_cell", "layer", "coords"), &TileMap::erase_cell);
ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id);
ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords);
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile);
@@ -3647,9 +3660,6 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants);
- ClassDB::bind_method(D_METHOD("_set_tile_data", "layer", "data"), &TileMap::_set_tile_data);
- ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data);
-
ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update);
GDVIRTUAL_BIND(_use_tile_data_runtime_update, "layer", "coords");
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index f260422290..02a2b3a1c6 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,11 +57,11 @@ struct TileMapQuadrant {
Vector2i coords;
// TileMapCells
- Set<Vector2i> cells;
+ RBSet<Vector2i> cells;
// We need those two maps to sort by world position for rendering
// This is kind of workaround, it would be better to sort the cells directly in the "cells" set instead.
- Map<Vector2i, Vector2i> map_to_world;
- Map<Vector2i, Vector2i, CoordsWorldComparator> world_to_map;
+ RBMap<Vector2i, Vector2i> map_to_world;
+ RBMap<Vector2i, Vector2i, CoordsWorldComparator> world_to_map;
// Debug.
RID debug_canvas_item;
@@ -74,13 +74,13 @@ struct TileMapQuadrant {
List<RID> bodies;
// Navigation.
- Map<Vector2i, Vector<RID>> navigation_regions;
+ HashMap<Vector2i, Vector<RID>> navigation_regions;
// Scenes.
- Map<Vector2i, String> scenes;
+ HashMap<Vector2i, String> scenes;
// Runtime TileData cache.
- Map<Vector2i, TileData *> runtime_tile_data_cache;
+ HashMap<Vector2i, TileData *> runtime_tile_data_cache;
void operator=(const TileMapQuadrant &q) {
layer = q.layer;
@@ -135,7 +135,7 @@ public:
return base_cell_coords;
}
- Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
+ HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
void set_terrain(int p_terrain) {
terrain = p_terrain;
@@ -193,22 +193,22 @@ private:
int y_sort_origin = 0;
int z_index = 0;
RID canvas_item;
- Map<Vector2i, TileMapCell> tile_map;
- Map<Vector2i, TileMapQuadrant> quadrant_map;
+ HashMap<Vector2i, TileMapCell> tile_map;
+ HashMap<Vector2i, TileMapQuadrant> quadrant_map;
SelfList<TileMapQuadrant>::List dirty_quadrant_list;
};
LocalVector<TileMapLayer> layers;
int selected_layer = -1;
// Mapping for RID to coords.
- Map<RID, Vector2i> bodies_coords;
+ HashMap<RID, Vector2i> bodies_coords;
// Quadrants and internals management.
Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const;
- Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(int p_layer, const Vector2i &p_qk);
+ HashMap<Vector2i, TileMapQuadrant>::Iterator _create_quadrant(int p_layer, const Vector2i &p_qk);
- void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q);
+ void _make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q);
void _make_all_quadrants_dirty();
void _queue_update_dirty_quadrants();
@@ -217,7 +217,7 @@ private:
void _recreate_layer_internals(int p_layer);
void _recreate_internals();
- void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q);
+ void _erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q);
void _clear_layer_internals(int p_layer);
void _clear_internals();
@@ -251,7 +251,7 @@ private:
void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
// Terrains.
- Set<TileSet::TerrainsPattern> _get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints);
+ RBSet<TileSet::TerrainsPattern> _get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints);
// Set and get tiles from data arrays.
void _set_tile_data(int p_layer, const Vector<int> &p_data);
@@ -321,7 +321,8 @@ public:
VisibilityMode get_navigation_visibility_mode();
// Cells accessors.
- void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE);
+ void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
+ void erase_cell(int p_layer, const Vector2i &p_coords);
int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
@@ -332,14 +333,14 @@ public:
void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern);
// Terrains.
- Set<TerrainConstraint> get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains = true) const; // Not exposed.
- Set<TerrainConstraint> get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; // Not exposed.
- Map<Vector2i, TileSet::TerrainsPattern> terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints); // Not exposed.
+ RBSet<TerrainConstraint> get_terrain_constraints_from_removed_cells_list(int p_layer, const RBSet<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains = true) const; // Not exposed.
+ RBSet<TerrainConstraint> get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; // Not exposed.
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_wave_function_collapse(const RBSet<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints); // Not exposed.
void set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains = true);
// Not exposed to users
TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
- Map<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer);
+ HashMap<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer);
int get_effective_quadrant_size(int p_layer) const;
//---
@@ -376,7 +377,7 @@ public:
// Helpers?
TypedArray<Vector2i> get_surrounding_tiles(Vector2i coords);
- void draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D());
+ void draw_cells_outline(Control *p_control, RBSet<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D());
// Virtual function to modify the TileData at runtime
GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);
diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp
index 8bd7b696f2..4a4a2a1da0 100644
--- a/scene/2d/touch_screen_button.cpp
+++ b/scene/2d/touch_screen_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,13 +32,13 @@
#include "scene/main/window.h"
-void TouchScreenButton::set_texture(const Ref<Texture2D> &p_texture) {
- texture = p_texture;
+void TouchScreenButton::set_texture_normal(const Ref<Texture2D> &p_texture) {
+ texture_normal = p_texture;
update();
}
-Ref<Texture2D> TouchScreenButton::get_texture() const {
- return texture;
+Ref<Texture2D> TouchScreenButton::get_texture_normal() const {
+ return texture_normal;
}
void TouchScreenButton::set_texture_pressed(const Ref<Texture2D> &p_texture_pressed) {
@@ -107,13 +107,13 @@ void TouchScreenButton::_notification(int p_what) {
if (finger_pressed != -1) {
if (texture_pressed.is_valid()) {
draw_texture(texture_pressed, Point2());
- } else if (texture.is_valid()) {
- draw_texture(texture, Point2());
+ } else if (texture_normal.is_valid()) {
+ draw_texture(texture_normal, Point2());
}
} else {
- if (texture.is_valid()) {
- draw_texture(texture, Point2());
+ if (texture_normal.is_valid()) {
+ draw_texture(texture_normal, Point2());
}
}
@@ -127,15 +127,15 @@ void TouchScreenButton::_notification(int p_what) {
Color draw_col = get_tree()->get_debug_collisions_color();
Vector2 pos;
- if (shape_centered && texture.is_valid()) {
- pos = texture->get_size() * 0.5;
+ if (shape_centered && texture_normal.is_valid()) {
+ pos = texture_normal->get_size() * 0.5;
}
draw_set_transform_matrix(get_canvas_transform().translated(pos));
shape->draw(get_canvas_item(), draw_col);
}
-
} break;
+
case NOTIFICATION_ENTER_TREE: {
if (!Engine::get_singleton()->is_editor_hint() && !!DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id())) && visibility == VISIBILITY_TOUCHSCREEN_ONLY) {
return;
@@ -145,13 +145,14 @@ void TouchScreenButton::_notification(int p_what) {
if (!Engine::get_singleton()->is_editor_hint()) {
set_process_input(is_visible_in_tree());
}
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (is_pressed()) {
_release(true);
}
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (Engine::get_singleton()->is_editor_hint()) {
break;
@@ -165,6 +166,7 @@ void TouchScreenButton::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_PAUSED: {
if (is_pressed()) {
_release();
@@ -188,7 +190,7 @@ String TouchScreenButton::get_action() const {
void TouchScreenButton::input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (!get_tree()) {
+ if (!is_visible_in_tree()) {
return;
}
@@ -196,8 +198,6 @@ void TouchScreenButton::input(const Ref<InputEvent> &p_event) {
return;
}
- ERR_FAIL_COND(!is_visible_in_tree());
-
const InputEventScreenTouch *st = Object::cast_to<InputEventScreenTouch>(*p_event);
if (passby_press) {
@@ -254,8 +254,8 @@ bool TouchScreenButton::_is_point_inside(const Point2 &p_point) {
check_rect = false;
Vector2 pos;
- if (shape_centered && texture.is_valid()) {
- pos = texture->get_size() * 0.5;
+ if (shape_centered && texture_normal.is_valid()) {
+ pos = texture_normal->get_size() * 0.5;
}
touched = shape->collide(Transform2D().translated(pos), unit_rect, Transform2D(0, coord + Vector2(0.5, 0.5)));
@@ -271,8 +271,8 @@ bool TouchScreenButton::_is_point_inside(const Point2 &p_point) {
}
if (!touched && check_rect) {
- if (texture.is_valid()) {
- touched = Rect2(Size2(), texture->get_size()).has_point(coord);
+ if (texture_normal.is_valid()) {
+ touched = Rect2(Size2(), texture_normal->get_size()).has_point(coord);
}
}
@@ -317,24 +317,24 @@ void TouchScreenButton::_release(bool p_exiting_tree) {
#ifdef TOOLS_ENABLED
Rect2 TouchScreenButton::_edit_get_rect() const {
- if (texture.is_null()) {
+ if (texture_normal.is_null()) {
return CanvasItem::_edit_get_rect();
}
- return Rect2(Size2(), texture->get_size());
+ return Rect2(Size2(), texture_normal->get_size());
}
bool TouchScreenButton::_edit_use_rect() const {
- return !texture.is_null();
+ return !texture_normal.is_null();
}
#endif
Rect2 TouchScreenButton::get_anchorable_rect() const {
- if (texture.is_null()) {
+ if (texture_normal.is_null()) {
return CanvasItem::get_anchorable_rect();
}
- return Rect2(Size2(), texture->get_size());
+ return Rect2(Size2(), texture_normal->get_size());
}
void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
@@ -355,10 +355,10 @@ bool TouchScreenButton::is_passby_press_enabled() const {
}
void TouchScreenButton::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TouchScreenButton::set_texture);
- ClassDB::bind_method(D_METHOD("get_texture"), &TouchScreenButton::get_texture);
+ ClassDB::bind_method(D_METHOD("set_texture_normal", "texture"), &TouchScreenButton::set_texture_normal);
+ ClassDB::bind_method(D_METHOD("get_texture_normal"), &TouchScreenButton::get_texture_normal);
- ClassDB::bind_method(D_METHOD("set_texture_pressed", "texture_pressed"), &TouchScreenButton::set_texture_pressed);
+ ClassDB::bind_method(D_METHOD("set_texture_pressed", "texture"), &TouchScreenButton::set_texture_pressed);
ClassDB::bind_method(D_METHOD("get_texture_pressed"), &TouchScreenButton::get_texture_pressed);
ClassDB::bind_method(D_METHOD("set_bitmask", "bitmask"), &TouchScreenButton::set_bitmask);
@@ -384,8 +384,8 @@ void TouchScreenButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_pressed"), &TouchScreenButton::is_pressed);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_pressed", "get_texture_pressed");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_normal", "get_texture_normal");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_pressed", "get_texture_pressed");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "bitmask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_bitmask", "get_bitmask");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_centered"), "set_shape_centered", "is_shape_centered");
diff --git a/scene/2d/touch_screen_button.h b/scene/2d/touch_screen_button.h
index 1c515149d4..e7f6da9f50 100644
--- a/scene/2d/touch_screen_button.h
+++ b/scene/2d/touch_screen_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,7 +46,7 @@ public:
};
private:
- Ref<Texture2D> texture;
+ Ref<Texture2D> texture_normal;
Ref<Texture2D> texture_pressed;
Ref<BitMap> bitmask;
Ref<Shape2D> shape;
@@ -78,8 +78,8 @@ public:
virtual bool _edit_use_rect() const override;
#endif
- void set_texture(const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_texture() const;
+ void set_texture_normal(const Ref<Texture2D> &p_texture);
+ Ref<Texture2D> get_texture_normal() const;
void set_texture_pressed(const Ref<Texture2D> &p_texture_pressed);
Ref<Texture2D> get_texture_pressed() const;
diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp
index eb4bedb6a3..2aae383cdf 100644
--- a/scene/2d/visible_on_screen_notifier_2d.cpp
+++ b/scene/2d/visible_on_screen_notifier_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -75,15 +75,16 @@ Rect2 VisibleOnScreenNotifier2D::get_rect() const {
void VisibleOnScreenNotifier2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- //get_world_2d()->
on_screen = false;
RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), true, rect, callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_enter), callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_exit));
} break;
+
case NOTIFICATION_DRAW: {
if (Engine::get_singleton()->is_editor_hint()) {
draw_rect(rect, Color(1, 0.5, 1, 0.2));
}
} break;
+
case NOTIFICATION_EXIT_TREE: {
on_screen = false;
RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), false, Rect2(), Callable(), Callable());
@@ -169,21 +170,23 @@ void VisibleOnScreenEnabler2D::_update_enable_mode(bool p_enable) {
}
}
void VisibleOnScreenEnabler2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
- node_id = ObjectID();
- Node *node = get_node(enable_node_path);
- if (node) {
- node_id = node->get_instance_id();
- node->set_process_mode(PROCESS_MODE_DISABLED);
- }
- }
+ node_id = ObjectID();
+ Node *node = get_node(enable_node_path);
+ if (node) {
+ node_id = node->get_instance_id();
+ node->set_process_mode(PROCESS_MODE_DISABLED);
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- node_id = ObjectID();
+ case NOTIFICATION_EXIT_TREE: {
+ node_id = ObjectID();
+ } break;
}
}
@@ -195,7 +198,7 @@ void VisibleOnScreenEnabler2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibleOnScreenEnabler2D::get_enable_node_path);
ADD_GROUP("Enabling", "enable_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,WhenPaused"), "set_enable_mode", "get_enable_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,When Paused"), "set_enable_mode", "get_enable_mode");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "enable_node_path"), "set_enable_node_path", "get_enable_node_path");
BIND_ENUM_CONSTANT(ENABLE_MODE_INHERIT);
diff --git a/scene/2d/visible_on_screen_notifier_2d.h b/scene/2d/visible_on_screen_notifier_2d.h
index 9c236a138f..3165eb92df 100644
--- a/scene/2d/visible_on_screen_notifier_2d.h
+++ b/scene/2d/visible_on_screen_notifier_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,7 +37,7 @@ class Viewport;
class VisibleOnScreenNotifier2D : public Node2D {
GDCLASS(VisibleOnScreenNotifier2D, Node2D);
- Set<Viewport *> viewports;
+ RBSet<Viewport *> viewports;
Rect2 rect;
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index 073543638f..54737e13b3 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -175,7 +175,7 @@ void Area3D::_initialize_wind() {
Node3D *p_wind_source = Object::cast_to<Node3D>(get_node(wind_source_path));
ERR_FAIL_NULL(p_wind_source);
Transform3D global_transform = p_wind_source->get_transform();
- wind_direction = -global_transform.basis.get_axis(Vector3::AXIS_Z).normalized();
+ wind_direction = -global_transform.basis.get_column(Vector3::AXIS_Z).normalized();
wind_source = global_transform.origin;
temp_magnitude = wind_force_magnitude;
}
@@ -192,14 +192,14 @@ void Area3D::_body_enter_tree(ObjectID p_id) {
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
- Map<ObjectID, BodyState>::Element *E = body_map.find(p_id);
+ HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(E->get().in_tree);
+ ERR_FAIL_COND(E->value.in_tree);
- E->get().in_tree = true;
+ E->value.in_tree = true;
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape);
}
}
@@ -207,13 +207,13 @@ void Area3D::_body_exit_tree(ObjectID p_id) {
Object *obj = ObjectDB::get_instance(p_id);
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
- Map<ObjectID, BodyState>::Element *E = body_map.find(p_id);
+ HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(!E->get().in_tree);
- E->get().in_tree = false;
+ ERR_FAIL_COND(!E->value.in_tree);
+ E->value.in_tree = false;
emit_signal(SceneStringNames::get_singleton()->body_exited, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape);
}
}
@@ -224,7 +224,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i
Object *obj = ObjectDB::get_instance(objid);
Node *node = Object::cast_to<Node>(obj);
- Map<ObjectID, BodyState>::Element *E = body_map.find(objid);
+ HashMap<ObjectID, BodyState>::Iterator E = body_map.find(objid);
if (!body_in && !E) {
return; //likely removed from the tree
@@ -235,36 +235,36 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i
if (body_in) {
if (!E) {
E = body_map.insert(objid, BodyState());
- E->get().rid = p_body;
- E->get().rc = 0;
- E->get().in_tree = node && node->is_inside_tree();
+ E->value.rid = p_body;
+ 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));
- if (E->get().in_tree) {
+ if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
}
}
}
- E->get().rc++;
+ E->value.rc++;
if (node) {
- E->get().shapes.insert(ShapePair(p_body_shape, p_area_shape));
+ E->value.shapes.insert(ShapePair(p_body_shape, p_area_shape));
}
- if (E->get().in_tree) {
+ if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape);
}
} else {
- E->get().rc--;
+ E->value.rc--;
if (node) {
- E->get().shapes.erase(ShapePair(p_body_shape, p_area_shape));
+ E->value.shapes.erase(ShapePair(p_body_shape, p_area_shape));
}
- bool in_tree = E->get().in_tree;
- if (E->get().rc == 0) {
- body_map.erase(E);
+ bool in_tree = E->value.in_tree;
+ if (E->value.rc == 0) {
+ body_map.remove(E);
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree));
@@ -285,7 +285,7 @@ void Area3D::_clear_monitoring() {
ERR_FAIL_COND_MSG(locked, "This function can't be used during the in/out signal.");
{
- Map<ObjectID, BodyState> bmcopy = body_map;
+ HashMap<ObjectID, BodyState> bmcopy = body_map;
body_map.clear();
//disconnect all monitored stuff
@@ -314,7 +314,7 @@ void Area3D::_clear_monitoring() {
}
{
- Map<ObjectID, AreaState> bmcopy = area_map;
+ HashMap<ObjectID, AreaState> bmcopy = area_map;
area_map.clear();
//disconnect all monitored stuff
@@ -344,10 +344,14 @@ void Area3D::_clear_monitoring() {
}
void Area3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_EXIT_TREE) {
- _clear_monitoring();
- } else if (p_what == NOTIFICATION_ENTER_TREE) {
- _initialize_wind();
+ switch (p_what) {
+ case NOTIFICATION_EXIT_TREE: {
+ _clear_monitoring();
+ } break;
+
+ case NOTIFICATION_ENTER_TREE: {
+ _initialize_wind();
+ } break;
}
}
@@ -375,14 +379,14 @@ void Area3D::_area_enter_tree(ObjectID p_id) {
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
- Map<ObjectID, AreaState>::Element *E = area_map.find(p_id);
+ HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(E->get().in_tree);
+ ERR_FAIL_COND(E->value.in_tree);
- E->get().in_tree = true;
+ E->value.in_tree = true;
emit_signal(SceneStringNames::get_singleton()->area_entered, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape);
}
}
@@ -390,13 +394,13 @@ void Area3D::_area_exit_tree(ObjectID p_id) {
Object *obj = ObjectDB::get_instance(p_id);
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
- Map<ObjectID, AreaState>::Element *E = area_map.find(p_id);
+ HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(!E->get().in_tree);
- E->get().in_tree = false;
+ ERR_FAIL_COND(!E->value.in_tree);
+ E->value.in_tree = false;
emit_signal(SceneStringNames::get_singleton()->area_exited, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape);
}
}
@@ -407,7 +411,7 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i
Object *obj = ObjectDB::get_instance(objid);
Node *node = Object::cast_to<Node>(obj);
- Map<ObjectID, AreaState>::Element *E = area_map.find(objid);
+ HashMap<ObjectID, AreaState>::Iterator E = area_map.find(objid);
if (!area_in && !E) {
return; //likely removed from the tree
@@ -418,36 +422,36 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i
if (area_in) {
if (!E) {
E = area_map.insert(objid, AreaState());
- E->get().rid = p_area;
- E->get().rc = 0;
- E->get().in_tree = node && node->is_inside_tree();
+ E->value.rid = p_area;
+ 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));
- if (E->get().in_tree) {
+ if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_entered, node);
}
}
}
- E->get().rc++;
+ E->value.rc++;
if (node) {
- E->get().shapes.insert(AreaShapePair(p_area_shape, p_self_shape));
+ E->value.shapes.insert(AreaShapePair(p_area_shape, p_self_shape));
}
- if (!node || E->get().in_tree) {
+ if (!node || E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, node, p_area_shape, p_self_shape);
}
} else {
- E->get().rc--;
+ E->value.rc--;
if (node) {
- E->get().shapes.erase(AreaShapePair(p_area_shape, p_self_shape));
+ E->value.shapes.erase(AreaShapePair(p_area_shape, p_self_shape));
}
- bool in_tree = E->get().in_tree;
- if (E->get().rc == 0) {
- area_map.erase(E);
+ bool in_tree = E->value.in_tree;
+ if (E->value.rc == 0) {
+ area_map.remove(E);
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree));
@@ -520,20 +524,20 @@ TypedArray<Area3D> Area3D::get_overlapping_areas() const {
bool Area3D::overlaps_area(Node *p_area) const {
ERR_FAIL_NULL_V(p_area, false);
- const Map<ObjectID, AreaState>::Element *E = area_map.find(p_area->get_instance_id());
+ HashMap<ObjectID, AreaState>::ConstIterator E = area_map.find(p_area->get_instance_id());
if (!E) {
return false;
}
- return E->get().in_tree;
+ return E->value.in_tree;
}
bool Area3D::overlaps_body(Node *p_body) const {
ERR_FAIL_NULL_V(p_body, false);
- const Map<ObjectID, BodyState>::Element *E = body_map.find(p_body->get_instance_id());
+ HashMap<ObjectID, BodyState>::ConstIterator E = body_map.find(p_body->get_instance_id());
if (!E) {
return false;
}
- return E->get().in_tree;
+ return E->value.in_tree;
}
void Area3D::set_audio_bus_override(bool p_override) {
diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h
index 7f31be2e17..3b892baf57 100644
--- a/scene/3d/area_3d.h
+++ b/scene/3d/area_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,7 +49,7 @@ public:
private:
SpaceOverride gravity_space_override = SPACE_OVERRIDE_DISABLED;
Vector3 gravity_vec;
- real_t gravity;
+ real_t gravity = 0.0;
bool gravity_is_point = false;
real_t gravity_distance_scale = 0.0;
@@ -98,7 +98,7 @@ private:
VSet<ShapePair> shapes;
};
- Map<ObjectID, BodyState> body_map;
+ HashMap<ObjectID, BodyState> body_map;
void _area_inout(int p_status, const RID &p_area, ObjectID p_instance, int p_area_shape, int p_self_shape);
@@ -130,7 +130,7 @@ private:
VSet<AreaShapePair> shapes;
};
- Map<ObjectID, AreaState> area_map;
+ HashMap<ObjectID, AreaState> area_map;
void _clear_monitoring();
bool audio_bus_override = false;
@@ -228,4 +228,4 @@ public:
VARIANT_ENUM_CAST(Area3D::SpaceOverride);
-#endif // AREA__H
+#endif // AREA_3D_H
diff --git a/scene/3d/audio_listener_3d.cpp b/scene/3d/audio_listener_3d.cpp
index b2319e40d7..1ead9bb384 100644
--- a/scene/3d/audio_listener_3d.cpp
+++ b/scene/3d/audio_listener_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -85,9 +85,11 @@ void AudioListener3D::_notification(int p_what) {
make_current();
}
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
_request_listener_update();
} break;
+
case NOTIFICATION_EXIT_WORLD: {
if (!get_tree()->is_node_being_edited(this)) {
if (is_current()) {
@@ -100,7 +102,6 @@ void AudioListener3D::_notification(int p_what) {
}
get_viewport()->_audio_listener_3d_remove(this);
-
} break;
}
}
diff --git a/scene/3d/audio_listener_3d.h b/scene/3d/audio_listener_3d.h
index 31de3b4fb1..ebc37673ed 100644
--- a/scene/3d/audio_listener_3d.h
+++ b/scene/3d/audio_listener_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 34f748b197..4a20a88346 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -239,80 +239,82 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
}
void AudioStreamPlayer3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- velocity_tracker->reset(get_global_transform().origin);
- AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this);
- if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
- play();
- }
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ velocity_tracker->reset(get_global_transform().origin);
+ AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this);
+ if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
+ play();
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- stop();
- AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this);
- }
+ case NOTIFICATION_EXIT_TREE: {
+ stop();
+ AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this);
+ } break;
- if (p_what == NOTIFICATION_PAUSED) {
- if (!can_process()) {
- // Node can't process so we start fading out to silence
- set_stream_paused(true);
- }
- }
+ case NOTIFICATION_PAUSED: {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence.
+ set_stream_paused(true);
+ }
+ } break;
- if (p_what == NOTIFICATION_UNPAUSED) {
- set_stream_paused(false);
- }
+ case NOTIFICATION_UNPAUSED: {
+ set_stream_paused(false);
+ } break;
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
- velocity_tracker->update_position(get_global_transform().origin);
- }
- }
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
+ velocity_tracker->update_position(get_global_transform().origin);
+ }
+ } break;
- if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- //update anything related to position first, if possible of course
- Vector<AudioFrame> volume_vector;
- if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
- volume_vector = _update_panning();
- }
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ // Update anything related to position first, if possible of course.
+ Vector<AudioFrame> volume_vector;
+ if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
+ volume_vector = _update_panning();
+ }
- if (setplay.get() >= 0 && stream.is_valid()) {
- active.set();
- Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
- ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
- Map<StringName, Vector<AudioFrame>> bus_map;
- bus_map[_get_actual_bus()] = volume_vector;
- AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
- stream_playbacks.push_back(new_playback);
- setplay.set(-1);
- }
+ if (setplay.get() >= 0 && stream.is_valid()) {
+ active.set();
+ Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+ ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
+ HashMap<StringName, Vector<AudioFrame>> bus_map;
+ bus_map[_get_actual_bus()] = volume_vector;
+ AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
+ stream_playbacks.push_back(new_playback);
+ setplay.set(-1);
+ }
- if (!stream_playbacks.is_empty() && active.is_set()) {
- // Stop playing if no longer active.
- Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
- for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
- if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
- playbacks_to_remove.push_back(playback);
+ if (!stream_playbacks.is_empty() && active.is_set()) {
+ // Stop playing if no longer active.
+ Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
+ playbacks_to_remove.push_back(playback);
+ }
+ }
+ // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
+ for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
+ stream_playbacks.erase(playback);
+ }
+ if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
+ // This node is no longer actively playing audio.
+ active.clear();
+ set_physics_process_internal(false);
+ }
+ if (!playbacks_to_remove.is_empty()) {
+ emit_signal(SNAME("finished"));
}
}
- // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
- for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
- stream_playbacks.erase(playback);
- }
- if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
- // This node is no longer actively playing audio.
- active.clear();
- set_physics_process_internal(false);
- }
- if (!playbacks_to_remove.is_empty()) {
- emit_signal(SNAME("finished"));
- }
- }
- while (stream_playbacks.size() > max_polyphony) {
- AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
- stream_playbacks.remove(0);
- }
+ while (stream_playbacks.size() > max_polyphony) {
+ AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+ stream_playbacks.remove_at(0);
+ }
+ } break;
}
}
@@ -385,13 +387,19 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
Ref<World3D> world_3d = get_world_3d();
ERR_FAIL_COND_V(world_3d.is_null(), output_volume_vector);
- Set<Camera3D *> cameras = world_3d->get_cameras();
+ RBSet<Camera3D *> cameras = world_3d->get_cameras();
cameras.insert(get_viewport()->get_camera_3d());
PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space());
for (Camera3D *camera : cameras) {
+ if (!camera) {
+ continue;
+ }
Viewport *vp = camera->get_viewport();
+ if (!vp) {
+ continue;
+ }
if (!vp->is_audio_listener_3d()) {
continue;
}
@@ -439,7 +447,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
if (emission_angle_enabled) {
Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin;
- float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative
+ float c = listenertopos.normalized().dot(get_global_transform().basis.get_column(2).normalized()); //it's z negative
float angle = Math::rad2deg(Math::acos(c));
if (angle > emission_angle) {
db_att -= -emission_angle_filter_attenuation_db;
@@ -458,7 +466,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
output_volume_vector.write[k] = multiplier * output_volume_vector[k];
}
- Map<StringName, Vector<AudioFrame>> bus_volumes;
+ HashMap<StringName, Vector<AudioFrame>> bus_volumes;
if (area) {
if (area->is_overriding_audio_bus()) {
//override audio bus
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 697bbe2381..53cdd2e630 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 5dc7382197..d0aeffb166 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -107,17 +107,17 @@ TypedArray<String> BoneAttachment3D::get_configuration_warnings() const {
if (use_external_skeleton) {
if (external_skeleton_node_cache.is_null()) {
- warnings.append(TTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node."));
+ warnings.push_back(RTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node."));
}
} else {
Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent());
if (!parent) {
- warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
+ warnings.push_back(RTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
}
}
if (bone_idx == -1) {
- warnings.append(TTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node."));
+ warnings.push_back(RTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node."));
}
return warnings;
@@ -340,17 +340,20 @@ void BoneAttachment3D::_notification(int p_what) {
}
_check_bind();
} break;
+
case NOTIFICATION_EXIT_TREE: {
_check_unbind();
} break;
+
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
_transform_changed();
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
if (_override_dirty) {
_override_dirty = false;
}
- }
+ } break;
}
}
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index 57b9854e0e..395dfde1d7 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index cc5b7078e3..4c53776bba 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -82,12 +82,6 @@ void Camera3D::_update_camera() {
RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform());
- // here goes listener stuff
- /*
- if (viewport_ptr && is_inside_scene() && is_current())
- get_viewport()->_camera_3d_transform_changed_notify();
- */
-
if (get_tree()->is_node_being_edited(this) || !is_current()) {
return;
}
@@ -108,14 +102,15 @@ void Camera3D::_notification(int p_what) {
if (current || first_camera) {
viewport->_camera_3d_set(this);
}
-
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
_request_camera_update();
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
velocity_tracker->update_position(get_global_transform().origin);
}
} break;
+
case NOTIFICATION_EXIT_WORLD: {
if (!get_tree()->is_node_being_edited(this)) {
if (is_current()) {
@@ -131,13 +126,14 @@ void Camera3D::_notification(int p_what) {
viewport->_camera_3d_remove(this);
viewport = nullptr;
}
-
} break;
+
case NOTIFICATION_BECAME_CURRENT: {
if (viewport) {
viewport->find_world_3d()->_register_camera(this);
}
} break;
+
case NOTIFICATION_LOST_CURRENT: {
if (viewport) {
viewport->find_world_3d()->_remove_camera(this);
@@ -148,8 +144,8 @@ void Camera3D::_notification(int p_what) {
Transform3D Camera3D::get_camera_transform() const {
Transform3D tr = get_global_transform().orthonormalized();
- tr.origin += tr.basis.get_axis(1) * v_offset;
- tr.origin += tr.basis.get_axis(0) * h_offset;
+ tr.origin += tr.basis.get_column(1) * v_offset;
+ tr.origin += tr.basis.get_column(0) * h_offset;
return tr;
}
@@ -221,8 +217,6 @@ void Camera3D::make_current() {
}
get_viewport()->_camera_3d_set(this);
-
- //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this);
}
void Camera3D::clear_current(bool p_enable_next) {
@@ -311,7 +305,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const {
bool Camera3D::is_position_behind(const Vector3 &p_pos) const {
Transform3D t = get_global_transform();
- Vector3 eyedir = -t.basis.get_axis(2).normalized();
+ Vector3 eyedir = -t.basis.get_column(2).normalized();
return eyedir.dot(p_pos - t.origin) < near;
}
@@ -331,11 +325,13 @@ Vector<Vector3> Camera3D::get_near_plane_points() const {
Vector3 endpoints[8];
cm.get_endpoints(Transform3D(), endpoints);
- Vector<Vector3> points;
- points.push_back(Vector3());
- for (int i = 0; i < 4; i++) {
- points.push_back(endpoints[i + 4]);
- }
+ Vector<Vector3> points = {
+ Vector3(),
+ endpoints[4],
+ endpoints[5],
+ endpoints[6],
+ endpoints[7]
+ };
return points;
}
@@ -477,9 +473,9 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_near", "near"), &Camera3D::set_near);
ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection);
ClassDB::bind_method(D_METHOD("set_projection", "mode"), &Camera3D::set_projection);
- ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera3D::set_h_offset);
+ ClassDB::bind_method(D_METHOD("set_h_offset", "offset"), &Camera3D::set_h_offset);
ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera3D::get_h_offset);
- ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera3D::set_v_offset);
+ ClassDB::bind_method(D_METHOD("set_v_offset", "offset"), &Camera3D::set_v_offset);
ClassDB::bind_method(D_METHOD("get_v_offset"), &Camera3D::get_v_offset);
ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &Camera3D::set_cull_mask);
ClassDB::bind_method(D_METHOD("get_cull_mask"), &Camera3D::get_cull_mask);
@@ -511,7 +507,7 @@ void Camera3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.001"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp"), "set_near", "get_near");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp"), "set_far", "get_far");
@@ -559,7 +555,7 @@ void Camera3D::set_fov(real_t p_fov) {
}
void Camera3D::set_size(real_t p_size) {
- ERR_FAIL_COND(p_size < 0.1 || p_size > 16384);
+ ERR_FAIL_COND(p_size < 0.001 || p_size > 16384);
size = p_size;
_update_camera_mode();
}
diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h
index 73126611d5..9f2f8ceed1 100644
--- a/scene/3d/camera_3d.h
+++ b/scene/3d/camera_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,8 @@
#include "scene/3d/node_3d.h"
#include "scene/3d/velocity_tracker_3d.h"
+#include "scene/resources/camera_effects.h"
+#include "scene/resources/environment.h"
class Camera3D : public Node3D {
GDCLASS(Camera3D, Node3D);
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index a166a05c71..70cab77eda 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -345,7 +345,7 @@ void CollisionObject3D::_update_debug_shapes() {
return;
}
- for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) {
+ for (RBSet<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) {
if (shapes.has(shapedata_idx->get())) {
ShapeData &shapedata = shapes[shapedata_idx->get()];
ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw();
@@ -459,7 +459,7 @@ void CollisionObject3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("mouse_entered"));
ADD_SIGNAL(MethodInfo("mouse_exited"));
- ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,Make Static,Keep Active"), "set_disable_mode", "get_disable_mode");
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
@@ -484,7 +484,7 @@ uint32_t CollisionObject3D::create_shape_owner(Object *p_owner) {
id = shapes.back()->key() + 1;
}
- sd.owner = p_owner;
+ sd.owner_id = p_owner ? p_owner->get_instance_id() : ObjectID();
shapes[id] = sd;
@@ -563,7 +563,7 @@ Transform3D CollisionObject3D::shape_owner_get_transform(uint32_t p_owner) const
Object *CollisionObject3D::shape_owner_get_owner(uint32_t p_owner) const {
ERR_FAIL_COND_V(!shapes.has(p_owner), nullptr);
- return shapes[p_owner].owner;
+ return ObjectDB::get_instance(shapes[p_owner].owner_id);
}
void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3D> &p_shape) {
@@ -628,7 +628,7 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape)
--debug_shapes_count;
}
- shapes[p_owner].shapes.remove(p_shape);
+ shapes[p_owner].shapes.remove_at(p_shape);
for (KeyValue<uint32_t, ShapeData> &E : shapes) {
for (int i = 0; i < E.value.shapes.size(); i++) {
@@ -689,7 +689,7 @@ TypedArray<String> CollisionObject3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (shapes.is_empty()) {
- warnings.push_back(TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."));
+ warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."));
}
return warnings;
diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h
index 1c7e205888..84b00de9c9 100644
--- a/scene/3d/collision_object_3d.h
+++ b/scene/3d/collision_object_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,7 +57,7 @@ private:
PhysicsServer3D::BodyMode body_mode = PhysicsServer3D::BODY_MODE_STATIC;
struct ShapeData {
- Object *owner = nullptr;
+ ObjectID owner_id;
Transform3D xform;
struct ShapeBase {
RID debug_shape;
@@ -71,14 +71,14 @@ private:
int total_subshapes = 0;
- Map<uint32_t, ShapeData> shapes;
+ RBMap<uint32_t, ShapeData> shapes;
bool only_update_transform_changes = false; // This is used for sync to physics.
bool capture_input_on_drag = false;
bool ray_pickable = true;
- Set<uint32_t> debug_shapes_to_update;
+ RBSet<uint32_t> debug_shapes_to_update;
int debug_shapes_count = 0;
Transform3D debug_shape_old_transform;
diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp
index 6328d9c67d..5a286d7b55 100644
--- a/scene/3d/collision_polygon_3d.cpp
+++ b/scene/3d/collision_polygon_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -93,18 +93,19 @@ void CollisionPolygon3D::_notification(int p_what) {
_update_in_shape_owner();
}
} break;
+
case NOTIFICATION_ENTER_TREE: {
if (parent) {
_update_in_shape_owner();
}
-
} break;
+
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
if (parent) {
_update_in_shape_owner(true);
}
-
} break;
+
case NOTIFICATION_UNPARENTED: {
if (parent) {
parent->remove_shape_owner(owner_id);
@@ -170,11 +171,11 @@ TypedArray<String> CollisionPolygon3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
- warnings.push_back(TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape."));
+ warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape."));
}
if (polygon.is_empty()) {
- warnings.push_back(TTR("An empty CollisionPolygon3D has no effect on collision."));
+ warnings.push_back(RTR("An empty CollisionPolygon3D has no effect on collision."));
}
return warnings;
diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h
index 73b8a8e0e3..a24d485af2 100644
--- a/scene/3d/collision_polygon_3d.h
+++ b/scene/3d/collision_polygon_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp
index 4e496fba47..759997de7b 100644
--- a/scene/3d/collision_shape_3d.cpp
+++ b/scene/3d/collision_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -87,16 +87,19 @@ void CollisionShape3D::_notification(int p_what) {
_update_in_shape_owner();
}
} break;
+
case NOTIFICATION_ENTER_TREE: {
if (parent) {
_update_in_shape_owner();
}
} break;
+
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
if (parent) {
_update_in_shape_owner(true);
}
} break;
+
case NOTIFICATION_UNPARENTED: {
if (parent) {
parent->remove_shape_owner(owner_id);
@@ -107,7 +110,7 @@ void CollisionShape3D::_notification(int p_what) {
}
}
-void CollisionShape3D::resource_changed(RES res) {
+void CollisionShape3D::resource_changed(Ref<Resource> res) {
update_gizmos();
}
@@ -115,17 +118,17 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
- warnings.push_back(TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape."));
+ warnings.push_back(RTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape."));
}
if (!shape.is_valid()) {
- warnings.push_back(TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it."));
+ warnings.push_back(RTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it."));
}
if (shape.is_valid() &&
Object::cast_to<RigidDynamicBody3D>(get_parent()) &&
Object::cast_to<ConcavePolygonShape3D>(*shape)) {
- warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static."));
+ warnings.push_back(RTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static."));
}
return warnings;
diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h
index cb7fe21eae..5c32230942 100644
--- a/scene/3d/collision_shape_3d.h
+++ b/scene/3d/collision_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,14 +37,13 @@
class CollisionObject3D;
class CollisionShape3D : public Node3D {
GDCLASS(CollisionShape3D, Node3D);
- OBJ_CATEGORY("3D Physics Nodes");
Ref<Shape3D> shape;
uint32_t owner_id = 0;
CollisionObject3D *parent = nullptr;
- void resource_changed(RES res);
+ void resource_changed(Ref<Resource> res);
bool disabled = false;
protected:
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index d347d24c2c..9666994b95 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,10 +39,6 @@ AABB CPUParticles3D::get_aabb() const {
return AABB();
}
-Vector<Face3> CPUParticles3D::get_faces(uint32_t p_usage_particle_flags) const {
- return Vector<Face3>();
-}
-
void CPUParticles3D::set_emitting(bool p_emitting) {
if (emitting == p_emitting) {
return;
@@ -210,11 +206,11 @@ TypedArray<String> CPUParticles3D::get_configuration_warnings() const {
anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES);
if (!mesh_found) {
- warnings.push_back(TTR("Nothing is visible because no mesh has been assigned."));
+ warnings.push_back(RTR("Nothing is visible because no mesh has been assigned."));
}
if (!anim_material_found && (get_param_max(PARAM_ANIM_SPEED) != 0.0 || get_param_max(PARAM_ANIM_OFFSET) != 0.0 || get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) {
- warnings.push_back(TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."));
+ warnings.push_back(RTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."));
}
return warnings;
@@ -368,6 +364,14 @@ Ref<Gradient> CPUParticles3D::get_color_ramp() const {
return color_ramp;
}
+void CPUParticles3D::set_color_initial_ramp(const Ref<Gradient> &p_ramp) {
+ color_initial_ramp = p_ramp;
+}
+
+Ref<Gradient> CPUParticles3D::get_color_initial_ramp() const {
+ return color_initial_ramp;
+}
+
void CPUParticles3D::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) {
ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX);
particle_flags[p_particle_flag] = p_enable;
@@ -504,7 +508,7 @@ bool CPUParticles3D::get_split_scale() {
}
void CPUParticles3D::_validate_property(PropertyInfo &property) const {
- if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) {
+ if (property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) {
property.usage = PROPERTY_USAGE_NONE;
}
@@ -748,10 +752,16 @@ void CPUParticles3D::_particles_process(double p_delta) {
p.hue_rot_rand = Math::randf();
p.anim_offset_rand = Math::randf();
+ if (color_initial_ramp.is_valid()) {
+ p.start_color_rand = color_initial_ramp->get_color_at_offset(Math::randf());
+ } else {
+ p.start_color_rand = Color(1, 1, 1, 1);
+ }
+
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread);
Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0);
- p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], Math::randf());
+ p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf());
} else {
//initiate velocity spread in 3D
real_t angle1_rad = Math::deg2rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * spread);
@@ -775,7 +785,7 @@ void CPUParticles3D::_particles_process(double p_delta) {
binormal.normalize();
Vector3 normal = binormal.cross(direction_nrm);
spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;
- p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], float(Math::randf()));
+ p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf());
}
real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand);
@@ -794,6 +804,13 @@ void CPUParticles3D::_particles_process(double p_delta) {
case EMISSION_SHAPE_SPHERE: {
real_t s = 2.0 * Math::randf() - 1.0;
real_t t = Math_TAU * Math::randf();
+ real_t x = Math::randf();
+ real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s);
+ p.transform.origin = Vector3(0, 0, 0).lerp(Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s), x);
+ } break;
+ case EMISSION_SHAPE_SPHERE_SURFACE: {
+ real_t s = 2.0 * Math::randf() - 1.0;
+ real_t t = Math_TAU * Math::randf();
real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s);
p.transform.origin = Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s);
} break;
@@ -816,8 +833,8 @@ void CPUParticles3D::_particles_process(double p_delta) {
Vector3 normal = emission_normals.get(random_idx);
Vector2 normal_2d(normal.x, normal.y);
Transform2D m2;
- m2.set_axis(0, normal_2d);
- m2.set_axis(1, normal_2d.orthogonal());
+ m2.columns[0] = normal_2d;
+ m2.columns[1] = normal_2d.orthogonal();
Vector2 velocity_2d(p.velocity.x, p.velocity.y);
velocity_2d = m2.basis_xform(velocity_2d);
p.velocity.x = velocity_2d.x;
@@ -828,9 +845,9 @@ void CPUParticles3D::_particles_process(double p_delta) {
Vector3 tangent = v0.cross(normal).normalized();
Vector3 bitangent = tangent.cross(normal).normalized();
Basis m3;
- m3.set_axis(0, tangent);
- m3.set_axis(1, bitangent);
- m3.set_axis(2, normal);
+ m3.set_column(0, tangent);
+ m3.set_column(1, bitangent);
+ m3.set_column(2, normal);
p.velocity = m3.xform(p.velocity);
}
}
@@ -881,53 +898,53 @@ void CPUParticles3D::_particles_process(double p_delta) {
p.custom[1] = p.time / lifetime;
tv = p.time / p.lifetime;
- real_t tex_linear_velocity = 0.0;
+ real_t tex_linear_velocity = 1.0;
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv);
}
- real_t tex_orbit_velocity = 0.0;
+ real_t tex_orbit_velocity = 1.0;
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv);
}
}
- real_t tex_angular_velocity = 0.0;
+ real_t tex_angular_velocity = 1.0;
if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv);
}
- real_t tex_linear_accel = 0.0;
+ real_t tex_linear_accel = 1.0;
if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv);
}
- real_t tex_tangential_accel = 0.0;
+ real_t tex_tangential_accel = 1.0;
if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv);
}
- real_t tex_radial_accel = 0.0;
+ real_t tex_radial_accel = 1.0;
if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv);
}
- real_t tex_damping = 0.0;
+ real_t tex_damping = 1.0;
if (curve_parameters[PARAM_DAMPING].is_valid()) {
tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv);
}
- real_t tex_angle = 0.0;
+ real_t tex_angle = 1.0;
if (curve_parameters[PARAM_ANGLE].is_valid()) {
tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv);
}
- real_t tex_anim_speed = 0.0;
+ real_t tex_anim_speed = 1.0;
if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) {
tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv);
}
- real_t tex_anim_offset = 0.0;
+ real_t tex_anim_offset = 1.0;
if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) {
tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv);
}
@@ -984,7 +1001,7 @@ void CPUParticles3D::_particles_process(double p_delta) {
real_t base_angle = (tex_angle)*Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand);
base_angle += p.custom[1] * lifetime * tex_angular_velocity * Math::lerp(parameters_min[PARAM_ANGULAR_VELOCITY], parameters_max[PARAM_ANGULAR_VELOCITY], rand_from_seed(alt_seed));
p.custom[0] = Math::deg2rad(base_angle); //angle
- p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + p.custom[1] * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed)); //angle
+ p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + tv * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed)); //angle
}
//apply color
//apply hue rotation
@@ -1046,38 +1063,38 @@ void CPUParticles3D::_particles_process(double p_delta) {
p.color.g = color_rgb.y;
p.color.b = color_rgb.z;
- p.color *= p.base_color;
+ p.color *= p.base_color * p.start_color_rand;
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) {
if (p.velocity.length() > 0.0) {
- p.transform.basis.set_axis(1, p.velocity.normalized());
+ p.transform.basis.set_column(1, p.velocity.normalized());
} else {
- p.transform.basis.set_axis(1, p.transform.basis.get_axis(1));
+ p.transform.basis.set_column(1, p.transform.basis.get_column(1));
}
- p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
- p.transform.basis.set_axis(2, Vector3(0, 0, 1));
+ p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized());
+ p.transform.basis.set_column(2, Vector3(0, 0, 1));
} else {
- p.transform.basis.set_axis(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0));
- p.transform.basis.set_axis(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0));
- p.transform.basis.set_axis(2, Vector3(0, 0, 1));
+ p.transform.basis.set_column(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0));
+ p.transform.basis.set_column(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0));
+ p.transform.basis.set_column(2, Vector3(0, 0, 1));
}
} else {
//orient particle Y towards velocity
if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) {
if (p.velocity.length() > 0.0) {
- p.transform.basis.set_axis(1, p.velocity.normalized());
+ p.transform.basis.set_column(1, p.velocity.normalized());
} else {
- p.transform.basis.set_axis(1, p.transform.basis.get_axis(1).normalized());
+ p.transform.basis.set_column(1, p.transform.basis.get_column(1).normalized());
}
- if (p.transform.basis.get_axis(1) == p.transform.basis.get_axis(0)) {
- p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
- p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized());
+ if (p.transform.basis.get_column(1) == p.transform.basis.get_column(0)) {
+ p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized());
+ p.transform.basis.set_column(2, p.transform.basis.get_column(0).cross(p.transform.basis.get_column(1)).normalized());
} else {
- p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized());
- p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized());
+ p.transform.basis.set_column(2, p.transform.basis.get_column(0).cross(p.transform.basis.get_column(1)).normalized());
+ p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized());
}
} else {
p.transform.basis.orthonormalize();
@@ -1142,7 +1159,7 @@ void CPUParticles3D::_update_particle_data_buffer() {
ERR_FAIL_NULL(get_viewport());
Camera3D *c = get_viewport()->get_camera_3d();
if (c) {
- Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close
+ Vector3 dir = c->get_global_transform().basis.get_column(2); //far away to close
if (local_coords) {
// will look different from Particles in editor as this is based on the camera in the scenetree
@@ -1170,17 +1187,17 @@ void CPUParticles3D::_update_particle_data_buffer() {
}
if (r[idx].active) {
- ptr[0] = t.basis.elements[0][0];
- ptr[1] = t.basis.elements[0][1];
- ptr[2] = t.basis.elements[0][2];
+ ptr[0] = t.basis.rows[0][0];
+ ptr[1] = t.basis.rows[0][1];
+ ptr[2] = t.basis.rows[0][2];
ptr[3] = t.origin.x;
- ptr[4] = t.basis.elements[1][0];
- ptr[5] = t.basis.elements[1][1];
- ptr[6] = t.basis.elements[1][2];
+ ptr[4] = t.basis.rows[1][0];
+ ptr[5] = t.basis.rows[1][1];
+ ptr[6] = t.basis.rows[1][2];
ptr[7] = t.origin.y;
- ptr[8] = t.basis.elements[2][0];
- ptr[9] = t.basis.elements[2][1];
- ptr[10] = t.basis.elements[2][2];
+ ptr[8] = t.basis.rows[2][0];
+ ptr[9] = t.basis.rows[2][1];
+ ptr[10] = t.basis.rows[2][2];
ptr[11] = t.origin.z;
} else {
memset(ptr, 0, sizeof(Transform3D));
@@ -1237,65 +1254,67 @@ void CPUParticles3D::_update_render_thread() {
}
void CPUParticles3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- set_process_internal(emitting);
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ set_process_internal(emitting);
- // first update before rendering to avoid one frame delay after emitting starts
- if (emitting && (time == 0)) {
- _update_internal();
- }
- }
+ // first update before rendering to avoid one frame delay after emitting starts
+ if (emitting && (time == 0)) {
+ _update_internal();
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- _set_redraw(false);
- }
+ case NOTIFICATION_EXIT_TREE: {
+ _set_redraw(false);
+ } break;
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- // first update before rendering to avoid one frame delay after emitting starts
- if (emitting && (time == 0)) {
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ // first update before rendering to avoid one frame delay after emitting starts
+ if (emitting && (time == 0)) {
+ _update_internal();
+ }
+ } break;
+
+ case NOTIFICATION_INTERNAL_PROCESS: {
_update_internal();
- }
- }
+ } break;
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- _update_internal();
- }
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ inv_emission_transform = get_global_transform().affine_inverse();
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- inv_emission_transform = get_global_transform().affine_inverse();
+ if (!local_coords) {
+ int pc = particles.size();
+
+ float *w = particle_data.ptrw();
+ const Particle *r = particles.ptr();
+ float *ptr = w;
+
+ for (int i = 0; i < pc; i++) {
+ Transform3D t = inv_emission_transform * r[i].transform;
+
+ if (r[i].active) {
+ ptr[0] = t.basis.rows[0][0];
+ ptr[1] = t.basis.rows[0][1];
+ ptr[2] = t.basis.rows[0][2];
+ ptr[3] = t.origin.x;
+ ptr[4] = t.basis.rows[1][0];
+ ptr[5] = t.basis.rows[1][1];
+ ptr[6] = t.basis.rows[1][2];
+ ptr[7] = t.origin.y;
+ ptr[8] = t.basis.rows[2][0];
+ ptr[9] = t.basis.rows[2][1];
+ ptr[10] = t.basis.rows[2][2];
+ ptr[11] = t.origin.z;
+ } else {
+ memset(ptr, 0, sizeof(float) * 12);
+ }
- if (!local_coords) {
- int pc = particles.size();
-
- float *w = particle_data.ptrw();
- const Particle *r = particles.ptr();
- float *ptr = w;
-
- for (int i = 0; i < pc; i++) {
- Transform3D t = inv_emission_transform * r[i].transform;
-
- if (r[i].active) {
- ptr[0] = t.basis.elements[0][0];
- ptr[1] = t.basis.elements[0][1];
- ptr[2] = t.basis.elements[0][2];
- ptr[3] = t.origin.x;
- ptr[4] = t.basis.elements[1][0];
- ptr[5] = t.basis.elements[1][1];
- ptr[6] = t.basis.elements[1][2];
- ptr[7] = t.origin.y;
- ptr[8] = t.basis.elements[2][0];
- ptr[9] = t.basis.elements[2][1];
- ptr[10] = t.basis.elements[2][2];
- ptr[11] = t.origin.z;
- } else {
- memset(ptr, 0, sizeof(float) * 12);
+ ptr += 20;
}
- ptr += 20;
+ can_update.set();
}
-
- can_update.set();
- }
+ } break;
}
}
@@ -1333,6 +1352,11 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) {
set_color_ramp(gt->get_gradient());
}
+ Ref<GradientTexture1D> gti = material->get_color_initial_ramp();
+ if (gti.is_valid()) {
+ set_color_initial_ramp(gti->get_gradient());
+ }
+
set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY));
set_particle_flag(PARTICLE_FLAG_ROTATE_Y, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ROTATE_Y));
set_particle_flag(PARTICLE_FLAG_DISABLE_Z, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z));
@@ -1459,6 +1483,9 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles3D::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles3D::get_color_ramp);
+ ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &CPUParticles3D::set_color_initial_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &CPUParticles3D::get_color_initial_ramp);
+
ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &CPUParticles3D::set_particle_flag);
ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &CPUParticles3D::get_particle_flag);
@@ -1510,7 +1537,7 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles3D::convert_from_particles);
ADD_GROUP("Emission Shape", "emission_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Sphere Surface,Box,Points,Directed Points,Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points");
@@ -1572,6 +1599,7 @@ void CPUParticles3D::_bind_methods() {
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_initial_ramp", "get_color_initial_ramp");
ADD_GROUP("Hue Variation", "hue_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION);
@@ -1606,6 +1634,7 @@ void CPUParticles3D::_bind_methods() {
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE_SURFACE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index aca7328a27..7f225ee98d 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -71,6 +71,7 @@ public:
enum EmissionShape {
EMISSION_SHAPE_POINT,
EMISSION_SHAPE_SPHERE,
+ EMISSION_SHAPE_SPHERE_SURFACE,
EMISSION_SHAPE_BOX,
EMISSION_SHAPE_POINTS,
EMISSION_SHAPE_DIRECTED_POINTS,
@@ -91,6 +92,7 @@ private:
real_t scale_rand = 0.0;
real_t hue_rot_rand = 0.0;
real_t anim_offset_rand = 0.0;
+ Color start_color_rand;
double time = 0.0;
double lifetime = 0.0;
Color base_color;
@@ -160,6 +162,7 @@ private:
Ref<Curve> curve_parameters[PARAM_MAX];
Color color = Color(1, 1, 1, 1);
Ref<Gradient> color_ramp;
+ Ref<Gradient> color_initial_ramp;
bool particle_flags[PARTICLE_FLAG_MAX] = {};
@@ -171,9 +174,9 @@ private:
Vector<Color> emission_colors;
int emission_point_count = 0;
Vector3 emission_ring_axis;
- real_t emission_ring_height;
- real_t emission_ring_radius;
- real_t emission_ring_inner_radius;
+ real_t emission_ring_height = 0.0;
+ real_t emission_ring_radius = 0.0;
+ real_t emission_ring_inner_radius = 0.0;
Ref<Curve> scale_curve_x;
Ref<Curve> scale_curve_y;
@@ -199,7 +202,6 @@ protected:
public:
AABB get_aabb() const override;
- Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
void set_emitting(bool p_emitting);
void set_amount(int p_amount);
@@ -261,6 +263,9 @@ public:
void set_color_ramp(const Ref<Gradient> &p_ramp);
Ref<Gradient> get_color_ramp() const;
+ void set_color_initial_ramp(const Ref<Gradient> &p_ramp);
+ Ref<Gradient> get_color_initial_ramp() const;
+
void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable);
bool get_particle_flag(ParticleFlags p_particle_flag) const;
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index 500bf4d8f5..a50f75f127 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -152,10 +152,6 @@ AABB Decal::get_aabb() const {
return aabb;
}
-Vector<Face3> Decal::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
void Decal::_validate_property(PropertyInfo &property) const {
if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) {
property.usage = PROPERTY_USAGE_NO_EDITOR;
@@ -167,15 +163,15 @@ TypedArray<String> Decal::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (textures[TEXTURE_ALBEDO].is_null() && textures[TEXTURE_NORMAL].is_null() && textures[TEXTURE_ORM].is_null() && textures[TEXTURE_EMISSION].is_null()) {
- warnings.push_back(TTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible."));
+ warnings.push_back(RTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible."));
}
if ((textures[TEXTURE_NORMAL].is_valid() || textures[TEXTURE_ORM].is_valid()) && textures[TEXTURE_ALBEDO].is_null()) {
- warnings.push_back(TTR("The decal has a Normal and/or ORM texture, but no Albedo texture is set.\nAn Albedo texture with an alpha channel is required to blend the normal/ORM maps onto the underlying surface.\nIf you don't want the Albedo texture to be visible, set Albedo Mix to 0."));
+ warnings.push_back(RTR("The decal has a Normal and/or ORM texture, but no Albedo texture is set.\nAn Albedo texture with an alpha channel is required to blend the normal/ORM maps onto the underlying surface.\nIf you don't want the Albedo texture to be visible, set Albedo Mix to 0."));
}
if (cull_mask == 0) {
- warnings.push_back(TTR("The decal's Cull Mask has no bits enabled, which means the decal will not paint objects on any layer.\nTo resolve this, enable at least one bit in the Cull Mask property."));
+ warnings.push_back(RTR("The decal's Cull Mask has no bits enabled, which means the decal will not paint objects on any layer.\nTo resolve this, enable at least one bit in the Cull Mask property."));
}
return warnings;
diff --git a/scene/3d/decal.h b/scene/3d/decal.h
index e9bda3276d..d5990272c6 100644
--- a/scene/3d/decal.h
+++ b/scene/3d/decal.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -104,7 +104,6 @@ public:
uint32_t get_cull_mask() const;
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
Decal();
~Decal();
diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp
index 694defd7dc..8d05254a25 100644
--- a/scene/3d/fog_volume.cpp
+++ b/scene/3d/fog_volume.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/fog_volume.h b/scene/3d/fog_volume.h
index 0807fb22e6..556a92ad3f 100644
--- a/scene/3d/fog_volume.h
+++ b/scene/3d/fog_volume.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -62,7 +62,6 @@ public:
Ref<Material> get_material() const;
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); }
TypedArray<String> get_configuration_warnings() const override;
FogVolume();
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index b35a45576f..33ce9fc6fe 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,10 +36,6 @@ AABB GPUParticles3D::get_aabb() const {
return AABB();
}
-Vector<Face3> GPUParticles3D::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
void GPUParticles3D::set_emitting(bool p_emitting) {
RS::get_singleton()->particles_set_emitting(particles, p_emitting);
@@ -277,7 +273,7 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."));
+ warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."));
}
bool meshes_found = false;
@@ -304,17 +300,17 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const {
}
if (!meshes_found) {
- warnings.push_back(TTR("Nothing is visible because meshes have not been assigned to draw passes."));
+ warnings.push_back(RTR("Nothing is visible because meshes have not been assigned to draw passes."));
}
if (process_material.is_null()) {
- warnings.push_back(TTR("A material to process the particles is not assigned, so no behavior is imprinted."));
+ warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted."));
} else {
const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr());
if (!anim_material_found && process &&
(process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 ||
process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) {
- warnings.push_back(TTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."));
+ warnings.push_back(RTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."));
}
}
@@ -356,15 +352,15 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const {
}
if (dp_count && skin.is_valid()) {
- warnings.push_back(TTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin."));
+ warnings.push_back(RTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin."));
} else if (dp_count == 0 && skin.is_null()) {
- warnings.push_back(TTR("Trails active, but neither Trail meshes or a Skin were found."));
+ warnings.push_back(RTR("Trails active, but neither Trail meshes or a Skin were found."));
} else if (dp_count > 1) {
- warnings.push_back(TTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation)."));
+ warnings.push_back(RTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation)."));
}
if ((dp_count || !skin.is_null()) && (missing_trails || no_materials)) {
- warnings.push_back(TTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering."));
+ warnings.push_back(RTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering."));
}
}
@@ -423,38 +419,41 @@ NodePath GPUParticles3D::get_sub_emitter() const {
}
void GPUParticles3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
- if (can_process()) {
- RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
- } else {
- RS::get_singleton()->particles_set_speed_scale(particles, 0);
- }
- }
-
- // Use internal process when emitting and one_shot is on so that when
- // the shot ends the editor can properly update
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- if (one_shot && !is_emitting()) {
- notify_property_list_changed();
- set_process_internal(false);
- }
- }
+ switch (p_what) {
+ case NOTIFICATION_PAUSED:
+ case NOTIFICATION_UNPAUSED: {
+ if (can_process()) {
+ RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
+ } else {
+ RS::get_singleton()->particles_set_speed_scale(particles, 0);
+ }
+ } break;
+
+ // Use internal process when emitting and one_shot is on so that when
+ // the shot ends the editor can properly update.
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (one_shot && !is_emitting()) {
+ notify_property_list_changed();
+ set_process_internal(false);
+ }
+ } break;
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (sub_emitter != NodePath()) {
- _attach_sub_emitter();
- }
- }
+ case NOTIFICATION_ENTER_TREE: {
+ if (sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- RS::get_singleton()->particles_set_subemitter(particles, RID());
- }
+ case NOTIFICATION_EXIT_TREE: {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ } break;
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- // make sure particles are updated before rendering occurs if they were active before
- if (is_visible_in_tree() && !RS::get_singleton()->particles_is_inactive(particles)) {
- RS::get_singleton()->particles_request_process(particles);
- }
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ // Make sure particles are updated before rendering occurs if they were active before.
+ if (is_visible_in_tree() && !RS::get_singleton()->particles_is_inactive(particles)) {
+ RS::get_singleton()->particles_request_process(particles);
+ }
+ } break;
}
}
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index 5e96f660da..adce45a0a9 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -61,16 +61,16 @@ private:
RID particles;
bool one_shot;
- int amount;
- double lifetime;
- double pre_process_time;
- real_t explosiveness_ratio;
- real_t randomness_ratio;
- double speed_scale;
+ int amount = 0;
+ double lifetime = 0.0;
+ double pre_process_time = 0.0;
+ real_t explosiveness_ratio = 0.0;
+ real_t randomness_ratio = 0.0;
+ double speed_scale = 0.0;
AABB visibility_aabb;
- bool local_coords;
- int fixed_fps;
- bool fractional_delta;
+ bool local_coords = false;
+ int fixed_fps = 0;
+ bool fractional_delta = false;
bool interpolate = true;
NodePath sub_emitter;
real_t collision_base_size = 0.01;
@@ -98,7 +98,6 @@ protected:
public:
AABB get_aabb() const override;
- Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
void set_emitting(bool p_emitting);
void set_amount(int p_amount);
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
index 2235de1599..4552ed9537 100644
--- a/scene/3d/gpu_particles_collision_3d.cpp
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -62,68 +62,68 @@ GPUParticlesCollision3D::~GPUParticlesCollision3D() {
/////////////////////////////////
-void GPUParticlesCollisionSphere::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesCollisionSphere::set_radius);
- ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesCollisionSphere::get_radius);
+void GPUParticlesCollisionSphere3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesCollisionSphere3D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesCollisionSphere3D::get_radius);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius");
}
-void GPUParticlesCollisionSphere::set_radius(real_t p_radius) {
+void GPUParticlesCollisionSphere3D::set_radius(real_t p_radius) {
radius = p_radius;
RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius);
update_gizmos();
}
-real_t GPUParticlesCollisionSphere::get_radius() const {
+real_t GPUParticlesCollisionSphere3D::get_radius() const {
return radius;
}
-AABB GPUParticlesCollisionSphere::get_aabb() const {
+AABB GPUParticlesCollisionSphere3D::get_aabb() const {
return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2));
}
-GPUParticlesCollisionSphere::GPUParticlesCollisionSphere() :
+GPUParticlesCollisionSphere3D::GPUParticlesCollisionSphere3D() :
GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE) {
}
-GPUParticlesCollisionSphere::~GPUParticlesCollisionSphere() {
+GPUParticlesCollisionSphere3D::~GPUParticlesCollisionSphere3D() {
}
///////////////////////////
-void GPUParticlesCollisionBox::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionBox::set_extents);
- ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionBox::get_extents);
+void GPUParticlesCollisionBox3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionBox3D::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionBox3D::get_extents);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
}
-void GPUParticlesCollisionBox::set_extents(const Vector3 &p_extents) {
+void GPUParticlesCollisionBox3D::set_extents(const Vector3 &p_extents) {
extents = p_extents;
RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
update_gizmos();
}
-Vector3 GPUParticlesCollisionBox::get_extents() const {
+Vector3 GPUParticlesCollisionBox3D::get_extents() const {
return extents;
}
-AABB GPUParticlesCollisionBox::get_aabb() const {
+AABB GPUParticlesCollisionBox3D::get_aabb() const {
return AABB(-extents, extents * 2);
}
-GPUParticlesCollisionBox::GPUParticlesCollisionBox() :
+GPUParticlesCollisionBox3D::GPUParticlesCollisionBox3D() :
GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE) {
}
-GPUParticlesCollisionBox::~GPUParticlesCollisionBox() {
+GPUParticlesCollisionBox3D::~GPUParticlesCollisionBox3D() {
}
///////////////////////////////
///////////////////////////
-void GPUParticlesCollisionSDF::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) {
+void GPUParticlesCollisionSDF3D::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
if (mi && mi->is_visible_in_tree()) {
Ref<Mesh> mesh = mi->get_mesh();
@@ -172,7 +172,7 @@ void GPUParticlesCollisionSDF::_find_meshes(const AABB &p_aabb, Node *p_at_node,
}
}
-uint32_t GPUParticlesCollisionSDF::_create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness) {
+uint32_t GPUParticlesCollisionSDF3D::_create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness) {
if (p_face_count == 1) {
return BVH::LEAF_BIT | p_faces[0].index;
}
@@ -220,7 +220,7 @@ static _FORCE_INLINE_ real_t Vector3_dot2(const Vector3 &p_vec3) {
return p_vec3.dot(p_vec3);
}
-void GPUParticlesCollisionSDF::_find_closest_distance(const Vector3 &p_pos, const BVH *bvh, uint32_t p_bvh_cell, const Face3 *triangles, float thickness, float &closest_distance) {
+void GPUParticlesCollisionSDF3D::_find_closest_distance(const Vector3 &p_pos, const BVH *bvh, uint32_t p_bvh_cell, const Face3 *triangles, float thickness, float &closest_distance) {
if (p_bvh_cell & BVH::LEAF_BIT) {
p_bvh_cell &= BVH::LEAF_MASK; //remove bit
@@ -321,7 +321,7 @@ void GPUParticlesCollisionSDF::_find_closest_distance(const Vector3 &p_pos, cons
}
}
-void GPUParticlesCollisionSDF::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *params) {
+void GPUParticlesCollisionSDF3D::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *params) {
int32_t z_ofs = p_z * params->size.y * params->size.x;
for (int32_t y = 0; y < params->size.y; y++) {
int32_t y_ofs = z_ofs + y * params->size.x;
@@ -338,10 +338,10 @@ void GPUParticlesCollisionSDF::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *pa
}
}
-void GPUParticlesCollisionSDF::_compute_sdf(ComputeSDFParams *params) {
+void GPUParticlesCollisionSDF3D::_compute_sdf(ComputeSDFParams *params) {
ThreadWorkPool work_pool;
work_pool.init();
- work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF::_compute_sdf_z, params);
+ work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params);
while (!work_pool.is_done_dispatching()) {
OS::get_singleton()->delay_usec(10000);
bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF");
@@ -350,7 +350,7 @@ void GPUParticlesCollisionSDF::_compute_sdf(ComputeSDFParams *params) {
work_pool.finish();
}
-Vector3i GPUParticlesCollisionSDF::get_estimated_cell_size() const {
+Vector3i GPUParticlesCollisionSDF3D::get_estimated_cell_size() const {
static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
int subdiv = subdivs[get_resolution()];
@@ -365,7 +365,7 @@ Vector3i GPUParticlesCollisionSDF::get_estimated_cell_size() const {
return sdf_size;
}
-Ref<Image> GPUParticlesCollisionSDF::bake() {
+Ref<Image> GPUParticlesCollisionSDF3D::bake() {
static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
int subdiv = subdivs[get_resolution()];
@@ -501,18 +501,18 @@ Ref<Image> GPUParticlesCollisionSDF::bake() {
return ret;
}
-void GPUParticlesCollisionSDF::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionSDF::set_extents);
- ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionSDF::get_extents);
+void GPUParticlesCollisionSDF3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionSDF3D::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionSDF3D::get_extents);
- ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionSDF::set_resolution);
- ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionSDF::get_resolution);
+ ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionSDF3D::set_resolution);
+ ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionSDF3D::get_resolution);
- ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesCollisionSDF::set_texture);
- ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesCollisionSDF::get_texture);
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesCollisionSDF3D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesCollisionSDF3D::get_texture);
- ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF::set_thickness);
- ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF::get_thickness);
+ ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF3D::set_thickness);
+ ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF3D::get_thickness);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512"), "set_resolution", "get_resolution");
@@ -528,128 +528,125 @@ void GPUParticlesCollisionSDF::_bind_methods() {
BIND_ENUM_CONSTANT(RESOLUTION_MAX);
}
-void GPUParticlesCollisionSDF::set_thickness(float p_thickness) {
+void GPUParticlesCollisionSDF3D::set_thickness(float p_thickness) {
thickness = p_thickness;
}
-float GPUParticlesCollisionSDF::get_thickness() const {
+float GPUParticlesCollisionSDF3D::get_thickness() const {
return thickness;
}
-void GPUParticlesCollisionSDF::set_extents(const Vector3 &p_extents) {
+void GPUParticlesCollisionSDF3D::set_extents(const Vector3 &p_extents) {
extents = p_extents;
RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
update_gizmos();
}
-Vector3 GPUParticlesCollisionSDF::get_extents() const {
+Vector3 GPUParticlesCollisionSDF3D::get_extents() const {
return extents;
}
-void GPUParticlesCollisionSDF::set_resolution(Resolution p_resolution) {
+void GPUParticlesCollisionSDF3D::set_resolution(Resolution p_resolution) {
resolution = p_resolution;
update_gizmos();
}
-GPUParticlesCollisionSDF::Resolution GPUParticlesCollisionSDF::get_resolution() const {
+GPUParticlesCollisionSDF3D::Resolution GPUParticlesCollisionSDF3D::get_resolution() const {
return resolution;
}
-void GPUParticlesCollisionSDF::set_texture(const Ref<Texture3D> &p_texture) {
+void GPUParticlesCollisionSDF3D::set_texture(const Ref<Texture3D> &p_texture) {
texture = p_texture;
RID tex = texture.is_valid() ? texture->get_rid() : RID();
RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex);
}
-Ref<Texture3D> GPUParticlesCollisionSDF::get_texture() const {
+Ref<Texture3D> GPUParticlesCollisionSDF3D::get_texture() const {
return texture;
}
-AABB GPUParticlesCollisionSDF::get_aabb() const {
+AABB GPUParticlesCollisionSDF3D::get_aabb() const {
return AABB(-extents, extents * 2);
}
-GPUParticlesCollisionSDF::BakeBeginFunc GPUParticlesCollisionSDF::bake_begin_function = nullptr;
-GPUParticlesCollisionSDF::BakeStepFunc GPUParticlesCollisionSDF::bake_step_function = nullptr;
-GPUParticlesCollisionSDF::BakeEndFunc GPUParticlesCollisionSDF::bake_end_function = nullptr;
+GPUParticlesCollisionSDF3D::BakeBeginFunc GPUParticlesCollisionSDF3D::bake_begin_function = nullptr;
+GPUParticlesCollisionSDF3D::BakeStepFunc GPUParticlesCollisionSDF3D::bake_step_function = nullptr;
+GPUParticlesCollisionSDF3D::BakeEndFunc GPUParticlesCollisionSDF3D::bake_end_function = nullptr;
-GPUParticlesCollisionSDF::GPUParticlesCollisionSDF() :
+GPUParticlesCollisionSDF3D::GPUParticlesCollisionSDF3D() :
GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE) {
}
-GPUParticlesCollisionSDF::~GPUParticlesCollisionSDF() {
+GPUParticlesCollisionSDF3D::~GPUParticlesCollisionSDF3D() {
}
////////////////////////////
////////////////////////////
-void GPUParticlesCollisionHeightField::_notification(int p_what) {
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- if (update_mode == UPDATE_MODE_ALWAYS) {
- RS::get_singleton()->particles_collision_height_field_update(_get_collision());
- }
+void GPUParticlesCollisionHeightField3D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (update_mode == UPDATE_MODE_ALWAYS) {
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ }
- if (follow_camera_mode && get_viewport()) {
- Camera3D *cam = get_viewport()->get_camera_3d();
- if (cam) {
- Transform3D xform = get_global_transform();
- Vector3 x_axis = xform.basis.get_axis(Vector3::AXIS_X).normalized();
- Vector3 z_axis = xform.basis.get_axis(Vector3::AXIS_Z).normalized();
- float x_len = xform.basis.get_scale().x;
- float z_len = xform.basis.get_scale().z;
+ if (follow_camera_mode && get_viewport()) {
+ Camera3D *cam = get_viewport()->get_camera_3d();
+ if (cam) {
+ Transform3D xform = get_global_transform();
+ Vector3 x_axis = xform.basis.get_column(Vector3::AXIS_X).normalized();
+ Vector3 z_axis = xform.basis.get_column(Vector3::AXIS_Z).normalized();
+ float x_len = xform.basis.get_scale().x;
+ float z_len = xform.basis.get_scale().z;
- Vector3 cam_pos = cam->get_global_transform().origin;
- Transform3D new_xform = xform;
+ Vector3 cam_pos = cam->get_global_transform().origin;
+ Transform3D new_xform = xform;
- while (x_axis.dot(cam_pos - new_xform.origin) > x_len) {
- new_xform.origin += x_axis * x_len;
- }
- while (x_axis.dot(cam_pos - new_xform.origin) < -x_len) {
- new_xform.origin -= x_axis * x_len;
- }
+ while (x_axis.dot(cam_pos - new_xform.origin) > x_len) {
+ new_xform.origin += x_axis * x_len;
+ }
+ while (x_axis.dot(cam_pos - new_xform.origin) < -x_len) {
+ new_xform.origin -= x_axis * x_len;
+ }
- while (z_axis.dot(cam_pos - new_xform.origin) > z_len) {
- new_xform.origin += z_axis * z_len;
- }
- while (z_axis.dot(cam_pos - new_xform.origin) < -z_len) {
- new_xform.origin -= z_axis * z_len;
- }
+ while (z_axis.dot(cam_pos - new_xform.origin) > z_len) {
+ new_xform.origin += z_axis * z_len;
+ }
+ while (z_axis.dot(cam_pos - new_xform.origin) < -z_len) {
+ new_xform.origin -= z_axis * z_len;
+ }
- if (new_xform != xform) {
- set_global_transform(new_xform);
- RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ if (new_xform != xform) {
+ set_global_transform(new_xform);
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ }
}
}
- }
- }
+ } break;
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ } break;
}
}
-void GPUParticlesCollisionHeightField::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionHeightField::set_extents);
- ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionHeightField::get_extents);
+void GPUParticlesCollisionHeightField3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionHeightField3D::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionHeightField3D::get_extents);
- ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionHeightField::set_resolution);
- ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionHeightField::get_resolution);
+ ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionHeightField3D::set_resolution);
+ ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionHeightField3D::get_resolution);
- ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField::set_update_mode);
- ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField::get_update_mode);
+ ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField3D::set_update_mode);
+ ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField3D::get_update_mode);
- ClassDB::bind_method(D_METHOD("set_follow_camera_mode", "enabled"), &GPUParticlesCollisionHeightField::set_follow_camera_mode);
- ClassDB::bind_method(D_METHOD("is_follow_camera_mode_enabled"), &GPUParticlesCollisionHeightField::is_follow_camera_mode_enabled);
-
- ClassDB::bind_method(D_METHOD("set_follow_camera_push_ratio", "ratio"), &GPUParticlesCollisionHeightField::set_follow_camera_push_ratio);
- ClassDB::bind_method(D_METHOD("get_follow_camera_push_ratio"), &GPUParticlesCollisionHeightField::get_follow_camera_push_ratio);
+ ClassDB::bind_method(D_METHOD("set_follow_camera_enabled", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_enabled);
+ ClassDB::bind_method(D_METHOD("is_follow_camera_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_enabled);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096,8192"), "set_resolution", "get_resolution");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "WhenMoved,Always"), "set_update_mode", "get_update_mode");
- ADD_GROUP("Follow Camera", "follow_camera_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_mode", "is_follow_camera_mode_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_camera_push_ratio", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_follow_camera_push_ratio", "get_follow_camera_push_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256 (Fastest),512 (Fast),1024 (Average),2048 (Slow),4096 (Slower),8192 (Slowest)"), "set_resolution", "get_resolution");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "When Moved (Fast),Always (Slow)"), "set_update_mode", "get_update_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_enabled", "is_follow_camera_enabled");
BIND_ENUM_CONSTANT(RESOLUTION_256);
BIND_ENUM_CONSTANT(RESOLUTION_512);
@@ -663,63 +660,55 @@ void GPUParticlesCollisionHeightField::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS);
}
-void GPUParticlesCollisionHeightField::set_follow_camera_push_ratio(float p_follow_camera_push_ratio) {
- follow_camera_push_ratio = p_follow_camera_push_ratio;
-}
-
-float GPUParticlesCollisionHeightField::get_follow_camera_push_ratio() const {
- return follow_camera_push_ratio;
-}
-
-void GPUParticlesCollisionHeightField::set_extents(const Vector3 &p_extents) {
+void GPUParticlesCollisionHeightField3D::set_extents(const Vector3 &p_extents) {
extents = p_extents;
RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
update_gizmos();
RS::get_singleton()->particles_collision_height_field_update(_get_collision());
}
-Vector3 GPUParticlesCollisionHeightField::get_extents() const {
+Vector3 GPUParticlesCollisionHeightField3D::get_extents() const {
return extents;
}
-void GPUParticlesCollisionHeightField::set_resolution(Resolution p_resolution) {
+void GPUParticlesCollisionHeightField3D::set_resolution(Resolution p_resolution) {
resolution = p_resolution;
RS::get_singleton()->particles_collision_set_height_field_resolution(_get_collision(), RS::ParticlesCollisionHeightfieldResolution(resolution));
update_gizmos();
RS::get_singleton()->particles_collision_height_field_update(_get_collision());
}
-GPUParticlesCollisionHeightField::Resolution GPUParticlesCollisionHeightField::get_resolution() const {
+GPUParticlesCollisionHeightField3D::Resolution GPUParticlesCollisionHeightField3D::get_resolution() const {
return resolution;
}
-void GPUParticlesCollisionHeightField::set_update_mode(UpdateMode p_update_mode) {
+void GPUParticlesCollisionHeightField3D::set_update_mode(UpdateMode p_update_mode) {
update_mode = p_update_mode;
set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);
}
-GPUParticlesCollisionHeightField::UpdateMode GPUParticlesCollisionHeightField::get_update_mode() const {
+GPUParticlesCollisionHeightField3D::UpdateMode GPUParticlesCollisionHeightField3D::get_update_mode() const {
return update_mode;
}
-void GPUParticlesCollisionHeightField::set_follow_camera_mode(bool p_enabled) {
+void GPUParticlesCollisionHeightField3D::set_follow_camera_enabled(bool p_enabled) {
follow_camera_mode = p_enabled;
set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);
}
-bool GPUParticlesCollisionHeightField::is_follow_camera_mode_enabled() const {
+bool GPUParticlesCollisionHeightField3D::is_follow_camera_enabled() const {
return follow_camera_mode;
}
-AABB GPUParticlesCollisionHeightField::get_aabb() const {
+AABB GPUParticlesCollisionHeightField3D::get_aabb() const {
return AABB(-extents, extents * 2);
}
-GPUParticlesCollisionHeightField::GPUParticlesCollisionHeightField() :
+GPUParticlesCollisionHeightField3D::GPUParticlesCollisionHeightField3D() :
GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE) {
}
-GPUParticlesCollisionHeightField::~GPUParticlesCollisionHeightField() {
+GPUParticlesCollisionHeightField3D::~GPUParticlesCollisionHeightField3D() {
}
////////////////////////////
@@ -792,104 +781,104 @@ GPUParticlesAttractor3D::~GPUParticlesAttractor3D() {
/////////////////////////////////
-void GPUParticlesAttractorSphere::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesAttractorSphere::set_radius);
- ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesAttractorSphere::get_radius);
+void GPUParticlesAttractorSphere3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesAttractorSphere3D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesAttractorSphere3D::get_radius);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius");
}
-void GPUParticlesAttractorSphere::set_radius(real_t p_radius) {
+void GPUParticlesAttractorSphere3D::set_radius(real_t p_radius) {
radius = p_radius;
RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius);
update_gizmos();
}
-real_t GPUParticlesAttractorSphere::get_radius() const {
+real_t GPUParticlesAttractorSphere3D::get_radius() const {
return radius;
}
-AABB GPUParticlesAttractorSphere::get_aabb() const {
+AABB GPUParticlesAttractorSphere3D::get_aabb() const {
return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2));
}
-GPUParticlesAttractorSphere::GPUParticlesAttractorSphere() :
+GPUParticlesAttractorSphere3D::GPUParticlesAttractorSphere3D() :
GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT) {
}
-GPUParticlesAttractorSphere::~GPUParticlesAttractorSphere() {
+GPUParticlesAttractorSphere3D::~GPUParticlesAttractorSphere3D() {
}
///////////////////////////
-void GPUParticlesAttractorBox::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorBox::set_extents);
- ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorBox::get_extents);
+void GPUParticlesAttractorBox3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorBox3D::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorBox3D::get_extents);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
}
-void GPUParticlesAttractorBox::set_extents(const Vector3 &p_extents) {
+void GPUParticlesAttractorBox3D::set_extents(const Vector3 &p_extents) {
extents = p_extents;
RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
update_gizmos();
}
-Vector3 GPUParticlesAttractorBox::get_extents() const {
+Vector3 GPUParticlesAttractorBox3D::get_extents() const {
return extents;
}
-AABB GPUParticlesAttractorBox::get_aabb() const {
+AABB GPUParticlesAttractorBox3D::get_aabb() const {
return AABB(-extents, extents * 2);
}
-GPUParticlesAttractorBox::GPUParticlesAttractorBox() :
+GPUParticlesAttractorBox3D::GPUParticlesAttractorBox3D() :
GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT) {
}
-GPUParticlesAttractorBox::~GPUParticlesAttractorBox() {
+GPUParticlesAttractorBox3D::~GPUParticlesAttractorBox3D() {
}
///////////////////////////
-void GPUParticlesAttractorVectorField::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorVectorField::set_extents);
- ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorVectorField::get_extents);
+void GPUParticlesAttractorVectorField3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorVectorField3D::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorVectorField3D::get_extents);
- ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesAttractorVectorField::set_texture);
- ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesAttractorVectorField::get_texture);
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesAttractorVectorField3D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesAttractorVectorField3D::get_texture);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture");
}
-void GPUParticlesAttractorVectorField::set_extents(const Vector3 &p_extents) {
+void GPUParticlesAttractorVectorField3D::set_extents(const Vector3 &p_extents) {
extents = p_extents;
RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
update_gizmos();
}
-Vector3 GPUParticlesAttractorVectorField::get_extents() const {
+Vector3 GPUParticlesAttractorVectorField3D::get_extents() const {
return extents;
}
-void GPUParticlesAttractorVectorField::set_texture(const Ref<Texture3D> &p_texture) {
+void GPUParticlesAttractorVectorField3D::set_texture(const Ref<Texture3D> &p_texture) {
texture = p_texture;
RID tex = texture.is_valid() ? texture->get_rid() : RID();
RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex);
}
-Ref<Texture3D> GPUParticlesAttractorVectorField::get_texture() const {
+Ref<Texture3D> GPUParticlesAttractorVectorField3D::get_texture() const {
return texture;
}
-AABB GPUParticlesAttractorVectorField::get_aabb() const {
+AABB GPUParticlesAttractorVectorField3D::get_aabb() const {
return AABB(-extents, extents * 2);
}
-GPUParticlesAttractorVectorField::GPUParticlesAttractorVectorField() :
+GPUParticlesAttractorVectorField3D::GPUParticlesAttractorVectorField3D() :
GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) {
}
-GPUParticlesAttractorVectorField::~GPUParticlesAttractorVectorField() {
+GPUParticlesAttractorVectorField3D::~GPUParticlesAttractorVectorField3D() {
}
diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h
index fbf68ed6df..4b2cb930fa 100644
--- a/scene/3d/gpu_particles_collision_3d.h
+++ b/scene/3d/gpu_particles_collision_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -50,13 +50,11 @@ public:
void set_cull_mask(uint32_t p_cull_mask);
uint32_t get_cull_mask() const;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); }
-
~GPUParticlesCollision3D();
};
-class GPUParticlesCollisionSphere : public GPUParticlesCollision3D {
- GDCLASS(GPUParticlesCollisionSphere, GPUParticlesCollision3D);
+class GPUParticlesCollisionSphere3D : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionSphere3D, GPUParticlesCollision3D);
real_t radius = 1.0;
@@ -69,12 +67,12 @@ public:
virtual AABB get_aabb() const override;
- GPUParticlesCollisionSphere();
- ~GPUParticlesCollisionSphere();
+ GPUParticlesCollisionSphere3D();
+ ~GPUParticlesCollisionSphere3D();
};
-class GPUParticlesCollisionBox : public GPUParticlesCollision3D {
- GDCLASS(GPUParticlesCollisionBox, GPUParticlesCollision3D);
+class GPUParticlesCollisionBox3D : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionBox3D, GPUParticlesCollision3D);
Vector3 extents = Vector3(1, 1, 1);
@@ -87,12 +85,12 @@ public:
virtual AABB get_aabb() const override;
- GPUParticlesCollisionBox();
- ~GPUParticlesCollisionBox();
+ GPUParticlesCollisionBox3D();
+ ~GPUParticlesCollisionBox3D();
};
-class GPUParticlesCollisionSDF : public GPUParticlesCollision3D {
- GDCLASS(GPUParticlesCollisionSDF, GPUParticlesCollision3D);
+class GPUParticlesCollisionSDF3D : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionSDF3D, GPUParticlesCollision3D);
public:
enum Resolution {
@@ -184,14 +182,14 @@ public:
static BakeStepFunc bake_step_function;
static BakeEndFunc bake_end_function;
- GPUParticlesCollisionSDF();
- ~GPUParticlesCollisionSDF();
+ GPUParticlesCollisionSDF3D();
+ ~GPUParticlesCollisionSDF3D();
};
-VARIANT_ENUM_CAST(GPUParticlesCollisionSDF::Resolution)
+VARIANT_ENUM_CAST(GPUParticlesCollisionSDF3D::Resolution)
-class GPUParticlesCollisionHeightField : public GPUParticlesCollision3D {
- GDCLASS(GPUParticlesCollisionHeightField, GPUParticlesCollision3D);
+class GPUParticlesCollisionHeightField3D : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionHeightField3D, GPUParticlesCollision3D);
public:
enum Resolution {
@@ -213,7 +211,6 @@ private:
Vector3 extents = Vector3(1, 1, 1);
Resolution resolution = RESOLUTION_1024;
bool follow_camera_mode = false;
- float follow_camera_push_ratio = 0.1;
UpdateMode update_mode = UPDATE_MODE_WHEN_MOVED;
@@ -231,20 +228,17 @@ public:
void set_update_mode(UpdateMode p_update_mode);
UpdateMode get_update_mode() const;
- void set_follow_camera_mode(bool p_enabled);
- bool is_follow_camera_mode_enabled() const;
-
- void set_follow_camera_push_ratio(float p_ratio);
- float get_follow_camera_push_ratio() const;
+ void set_follow_camera_enabled(bool p_enabled);
+ bool is_follow_camera_enabled() const;
virtual AABB get_aabb() const override;
- GPUParticlesCollisionHeightField();
- ~GPUParticlesCollisionHeightField();
+ GPUParticlesCollisionHeightField3D();
+ ~GPUParticlesCollisionHeightField3D();
};
-VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField::Resolution)
-VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField::UpdateMode)
+VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField3D::Resolution)
+VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField3D::UpdateMode)
class GPUParticlesAttractor3D : public VisualInstance3D {
GDCLASS(GPUParticlesAttractor3D, VisualInstance3D);
@@ -274,13 +268,11 @@ public:
void set_directionality(real_t p_directionality);
real_t get_directionality() const;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); }
-
~GPUParticlesAttractor3D();
};
-class GPUParticlesAttractorSphere : public GPUParticlesAttractor3D {
- GDCLASS(GPUParticlesAttractorSphere, GPUParticlesAttractor3D);
+class GPUParticlesAttractorSphere3D : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorSphere3D, GPUParticlesAttractor3D);
real_t radius = 1.0;
@@ -293,12 +285,12 @@ public:
virtual AABB get_aabb() const override;
- GPUParticlesAttractorSphere();
- ~GPUParticlesAttractorSphere();
+ GPUParticlesAttractorSphere3D();
+ ~GPUParticlesAttractorSphere3D();
};
-class GPUParticlesAttractorBox : public GPUParticlesAttractor3D {
- GDCLASS(GPUParticlesAttractorBox, GPUParticlesAttractor3D);
+class GPUParticlesAttractorBox3D : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorBox3D, GPUParticlesAttractor3D);
Vector3 extents = Vector3(1, 1, 1);
@@ -311,12 +303,12 @@ public:
virtual AABB get_aabb() const override;
- GPUParticlesAttractorBox();
- ~GPUParticlesAttractorBox();
+ GPUParticlesAttractorBox3D();
+ ~GPUParticlesAttractorBox3D();
};
-class GPUParticlesAttractorVectorField : public GPUParticlesAttractor3D {
- GDCLASS(GPUParticlesAttractorVectorField, GPUParticlesAttractor3D);
+class GPUParticlesAttractorVectorField3D : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorVectorField3D, GPUParticlesAttractor3D);
Vector3 extents = Vector3(1, 1, 1);
Ref<Texture3D> texture;
@@ -333,8 +325,8 @@ public:
virtual AABB get_aabb() const override;
- GPUParticlesAttractorVectorField();
- ~GPUParticlesAttractorVectorField();
+ GPUParticlesAttractorVectorField3D();
+ ~GPUParticlesAttractorVectorField3D();
};
#endif // GPU_PARTICLES_COLLISION_3D_H
diff --git a/scene/3d/importer_mesh_instance_3d.cpp b/scene/3d/importer_mesh_instance_3d.cpp
index 748a2e5092..7cd6a81532 100644
--- a/scene/3d/importer_mesh_instance_3d.cpp
+++ b/scene/3d/importer_mesh_instance_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/importer_mesh_instance_3d.h b/scene/3d/importer_mesh_instance_3d.h
index 0cf7dbe86b..3daf06771d 100644
--- a/scene/3d/importer_mesh_instance_3d.h
+++ b/scene/3d/importer_mesh_instance_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp
index aa5ca85bdf..c22e3f6d91 100644
--- a/scene/3d/joint_3d.cpp
+++ b/scene/3d/joint_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -76,15 +76,15 @@ void Joint3D::_update_joint(bool p_only_free) {
PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b);
if (node_a && !body_a && node_b && !body_b) {
- warning = TTR("Node A and Node B must be PhysicsBody3Ds");
+ warning = RTR("Node A and Node B must be PhysicsBody3Ds");
} else if (node_a && !body_a) {
- warning = TTR("Node A must be a PhysicsBody3D");
+ warning = RTR("Node A must be a PhysicsBody3D");
} else if (node_b && !body_b) {
- warning = TTR("Node B must be a PhysicsBody3D");
+ warning = RTR("Node B must be a PhysicsBody3D");
} else if (!body_a && !body_b) {
- warning = TTR("Joint is not connected to any PhysicsBody3Ds");
+ warning = RTR("Joint is not connected to any PhysicsBody3Ds");
} else if (body_a == body_b) {
- warning = TTR("Node A and Node B must be different PhysicsBody3Ds");
+ warning = RTR("Node A and Node B must be different PhysicsBody3Ds");
} else {
warning = String();
}
@@ -124,7 +124,7 @@ void Joint3D::set_node_a(const NodePath &p_node_a) {
return;
}
- if (joint.is_valid()) {
+ if (is_configured()) {
_disconnect_signals();
}
@@ -141,7 +141,7 @@ void Joint3D::set_node_b(const NodePath &p_node_b) {
return;
}
- if (joint.is_valid()) {
+ if (is_configured()) {
_disconnect_signals();
}
@@ -166,14 +166,18 @@ int Joint3D::get_solver_priority() const {
void Joint3D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
+ case NOTIFICATION_POST_ENTER_TREE: {
+ if (is_configured()) {
+ _disconnect_signals();
+ }
_update_joint();
} break;
+
case NOTIFICATION_EXIT_TREE: {
- if (joint.is_valid()) {
+ if (is_configured()) {
_disconnect_signals();
- _update_joint(true);
}
+ _update_joint(true);
} break;
}
}
@@ -182,6 +186,10 @@ void Joint3D::set_exclude_nodes_from_collision(bool p_enable) {
if (exclude_from_collision == p_enable) {
return;
}
+ if (is_configured()) {
+ _disconnect_signals();
+ }
+ _update_joint(true);
exclude_from_collision = p_enable;
_update_joint();
}
diff --git a/scene/3d/joint_3d.h b/scene/3d/joint_3d.h
index 211cf8e071..ea356ef3b7 100644
--- a/scene/3d/joint_3d.h
+++ b/scene/3d/joint_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
new file mode 100644
index 0000000000..2d7da48ab1
--- /dev/null
+++ b/scene/3d/label_3d.cpp
@@ -0,0 +1,1010 @@
+/*************************************************************************/
+/* label_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 "label_3d.h"
+
+#include "core/core_string_names.h"
+#include "scene/resources/theme.h"
+#include "scene/scene_string_names.h"
+
+void Label3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label3D::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label3D::get_horizontal_alignment);
+
+ ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &Label3D::set_vertical_alignment);
+ ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label3D::get_vertical_alignment);
+
+ ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &Label3D::set_modulate);
+ ClassDB::bind_method(D_METHOD("get_modulate"), &Label3D::get_modulate);
+
+ ClassDB::bind_method(D_METHOD("set_outline_modulate", "modulate"), &Label3D::set_outline_modulate);
+ ClassDB::bind_method(D_METHOD("get_outline_modulate"), &Label3D::get_outline_modulate);
+
+ ClassDB::bind_method(D_METHOD("set_text", "text"), &Label3D::set_text);
+ ClassDB::bind_method(D_METHOD("get_text"), &Label3D::get_text);
+
+ ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label3D::set_text_direction);
+ ClassDB::bind_method(D_METHOD("get_text_direction"), &Label3D::get_text_direction);
+
+ ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Label3D::set_opentype_feature);
+ ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Label3D::get_opentype_feature);
+ ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Label3D::clear_opentype_features);
+
+ ClassDB::bind_method(D_METHOD("set_language", "language"), &Label3D::set_language);
+ ClassDB::bind_method(D_METHOD("get_language"), &Label3D::get_language);
+
+ ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &Label3D::set_structured_text_bidi_override);
+ ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &Label3D::get_structured_text_bidi_override);
+
+ ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label3D::set_structured_text_bidi_override_options);
+ ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label3D::get_structured_text_bidi_override_options);
+
+ ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &Label3D::set_uppercase);
+ ClassDB::bind_method(D_METHOD("is_uppercase"), &Label3D::is_uppercase);
+
+ ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &Label3D::set_render_priority);
+ ClassDB::bind_method(D_METHOD("get_render_priority"), &Label3D::get_render_priority);
+
+ ClassDB::bind_method(D_METHOD("set_outline_render_priority", "priority"), &Label3D::set_outline_render_priority);
+ ClassDB::bind_method(D_METHOD("get_outline_render_priority"), &Label3D::get_outline_render_priority);
+
+ ClassDB::bind_method(D_METHOD("set_font", "font"), &Label3D::set_font);
+ ClassDB::bind_method(D_METHOD("get_font"), &Label3D::get_font);
+
+ ClassDB::bind_method(D_METHOD("set_font_size", "size"), &Label3D::set_font_size);
+ ClassDB::bind_method(D_METHOD("get_font_size"), &Label3D::get_font_size);
+
+ ClassDB::bind_method(D_METHOD("set_outline_size", "outline_size"), &Label3D::set_outline_size);
+ ClassDB::bind_method(D_METHOD("get_outline_size"), &Label3D::get_outline_size);
+
+ ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &Label3D::set_line_spacing);
+ ClassDB::bind_method(D_METHOD("get_line_spacing"), &Label3D::get_line_spacing);
+
+ ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label3D::set_autowrap_mode);
+ ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Label3D::get_autowrap_mode);
+
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &Label3D::set_width);
+ ClassDB::bind_method(D_METHOD("get_width"), &Label3D::get_width);
+
+ ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &Label3D::set_pixel_size);
+ ClassDB::bind_method(D_METHOD("get_pixel_size"), &Label3D::get_pixel_size);
+
+ ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Label3D::set_offset);
+ ClassDB::bind_method(D_METHOD("get_offset"), &Label3D::get_offset);
+
+ ClassDB::bind_method(D_METHOD("set_draw_flag", "flag", "enabled"), &Label3D::set_draw_flag);
+ ClassDB::bind_method(D_METHOD("get_draw_flag", "flag"), &Label3D::get_draw_flag);
+
+ ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &Label3D::set_billboard_mode);
+ ClassDB::bind_method(D_METHOD("get_billboard_mode"), &Label3D::get_billboard_mode);
+
+ ClassDB::bind_method(D_METHOD("set_alpha_cut_mode", "mode"), &Label3D::set_alpha_cut_mode);
+ ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &Label3D::get_alpha_cut_mode);
+
+ ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold", "threshold"), &Label3D::set_alpha_scissor_threshold);
+ ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold"), &Label3D::get_alpha_scissor_threshold);
+
+ ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &Label3D::set_texture_filter);
+ ClassDB::bind_method(D_METHOD("get_texture_filter"), &Label3D::get_texture_filter);
+
+ ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &Label3D::generate_triangle_mesh);
+
+ ClassDB::bind_method(D_METHOD("_queue_update"), &Label3D::_queue_update);
+ ClassDB::bind_method(D_METHOD("_font_changed"), &Label3D::_font_changed);
+ ClassDB::bind_method(D_METHOD("_im_update"), &Label3D::_im_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001"), "set_pixel_size", "get_pixel_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
+
+ ADD_GROUP("Flags", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard"), "set_billboard_mode", "get_billboard_mode");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_outline_render_priority", "get_outline_render_priority");
+
+ ADD_GROUP("Text", "");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_modulate"), "set_outline_modulate", "get_outline_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,127,1"), "set_font_size", "get_font_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1"), "set_outline_size", "get_outline_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing"), "set_line_spacing", "get_line_spacing");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
+
+ ADD_GROUP("Locale", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
+
+ BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
+ BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART);
+
+ BIND_ENUM_CONSTANT(FLAG_SHADED);
+ BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED);
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST);
+ BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE);
+ BIND_ENUM_CONSTANT(FLAG_MAX);
+
+ BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED);
+ BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD);
+ BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS);
+}
+
+bool Label3D::_set(const StringName &p_name, const Variant &p_value) {
+ String str = p_name;
+ if (str.begins_with("opentype_features/")) {
+ String name = str.get_slicec('/', 1);
+ int32_t tag = TS->name_to_tag(name);
+ int value = p_value;
+ if (value == -1) {
+ if (opentype_features.has(tag)) {
+ opentype_features.erase(tag);
+ dirty_font = true;
+ _queue_update();
+ }
+ } else {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
+ opentype_features[tag] = value;
+ dirty_font = true;
+ _queue_update();
+ }
+ }
+ notify_property_list_changed();
+ return true;
+ }
+
+ return false;
+}
+
+bool Label3D::_get(const StringName &p_name, Variant &r_ret) const {
+ String str = p_name;
+ if (str.begins_with("opentype_features/")) {
+ String name = str.get_slicec('/', 1);
+ int32_t tag = TS->name_to_tag(name);
+ if (opentype_features.has(tag)) {
+ r_ret = opentype_features[tag];
+ return true;
+ } else {
+ r_ret = -1;
+ return true;
+ }
+ }
+ return false;
+}
+
+void Label3D::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
+ String name = TS->tag_to_name(*ftr);
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
+ }
+ p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+}
+
+void Label3D::_validate_property(PropertyInfo &property) const {
+ if (property.name == "material_override" || property.name == "material_overlay") {
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+}
+
+void Label3D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (!pending_update) {
+ _im_update();
+ }
+ } break;
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ String new_text = tr(text);
+ if (new_text == xl_text) {
+ return; // Nothing new.
+ }
+ xl_text = new_text;
+ dirty_text = true;
+ _queue_update();
+ } break;
+ }
+}
+
+void Label3D::_im_update() {
+ _shape();
+
+ triangle_mesh.unref();
+ update_gizmos();
+
+ pending_update = false;
+}
+
+void Label3D::_queue_update() {
+ if (pending_update) {
+ return;
+ }
+
+ pending_update = true;
+ call_deferred(SceneStringNames::get_singleton()->_im_update);
+}
+
+AABB Label3D::get_aabb() const {
+ return aabb;
+}
+
+Ref<TriangleMesh> Label3D::generate_triangle_mesh() const {
+ if (triangle_mesh.is_valid()) {
+ return triangle_mesh;
+ }
+
+ Ref<Font> font = _get_font_or_default();
+ if (font.is_null()) {
+ return Ref<TriangleMesh>();
+ }
+
+ Vector<Vector3> faces;
+ faces.resize(6);
+ Vector3 *facesw = faces.ptrw();
+
+ float total_h = 0.0;
+ float max_line_w = 0.0;
+ for (int i = 0; i < lines_rid.size(); i++) {
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ max_line_w = MAX(max_line_w, TS->shaped_text_get_width(lines_rid[i]));
+ }
+
+ float vbegin = 0;
+ switch (vertical_alignment) {
+ case VERTICAL_ALIGNMENT_FILL:
+ case VERTICAL_ALIGNMENT_TOP: {
+ // Nothing.
+ } break;
+ case VERTICAL_ALIGNMENT_CENTER: {
+ vbegin = (total_h - line_spacing) / 2.0;
+ } break;
+ case VERTICAL_ALIGNMENT_BOTTOM: {
+ vbegin = (total_h - line_spacing);
+ } break;
+ }
+
+ Vector2 offset = Vector2(0, vbegin);
+ switch (horizontal_alignment) {
+ case HORIZONTAL_ALIGNMENT_LEFT:
+ break;
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ offset.x = -max_line_w / 2.0;
+ } break;
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
+ offset.x = -max_line_w;
+ } break;
+ }
+
+ Rect2 final_rect = Rect2(offset + lbl_offset, Size2(max_line_w, total_h));
+
+ if (final_rect.size.x == 0 || final_rect.size.y == 0) {
+ return Ref<TriangleMesh>();
+ }
+
+ real_t pixel_size = get_pixel_size();
+
+ Vector2 vertices[4] = {
+
+ (final_rect.position + Vector2(0, -final_rect.size.y)) * pixel_size,
+ (final_rect.position + Vector2(final_rect.size.x, -final_rect.size.y)) * pixel_size,
+ (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
+ final_rect.position * pixel_size,
+
+ };
+
+ static const int indices[6] = {
+ 0, 1, 2,
+ 0, 2, 3
+ };
+
+ for (int j = 0; j < 6; j++) {
+ int i = indices[j];
+ Vector3 vtx;
+ vtx[0] = vertices[i][0];
+ vtx[1] = vertices[i][1];
+ facesw[j] = vtx;
+ }
+
+ triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
+ triangle_mesh->create(faces);
+
+ return triangle_mesh;
+}
+
+void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority, int p_outline_size) {
+ for (int j = 0; j < p_glyph.repeat; j++) {
+ Vector2 gl_of;
+ Vector2 gl_sz;
+ Rect2 gl_uv;
+ Size2 texs;
+ RID tex;
+
+ if (p_glyph.font_rid != RID()) {
+ tex = TS->font_get_glyph_texture_rid(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index);
+ if (tex != RID()) {
+ gl_of = (TS->font_get_glyph_offset(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index) + Vector2(p_glyph.x_off, p_glyph.y_off)) * pixel_size;
+ gl_sz = TS->font_get_glyph_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index) * pixel_size;
+ gl_uv = TS->font_get_glyph_uv_rect(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index);
+ texs = TS->font_get_glyph_texture_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index);
+ }
+ } else {
+ gl_sz = TS->get_hex_code_box_size(p_glyph.font_size, p_glyph.index) * pixel_size;
+ gl_of = Vector2(0, -gl_sz.y);
+ }
+
+ bool msdf = TS->font_is_multichannel_signed_distance_field(p_glyph.font_rid);
+
+ uint64_t mat_hash;
+ if (tex != RID()) {
+ mat_hash = hash_one_uint64(tex.get_id());
+ } else {
+ mat_hash = hash_one_uint64(0);
+ }
+ mat_hash = hash_djb2_one_64(p_priority | (p_outline_size << 31), mat_hash);
+
+ if (!surfaces.has(mat_hash)) {
+ SurfaceData surf;
+ surf.material = RenderingServer::get_singleton()->material_create();
+ // Set defaults for material, names need to match up those in StandardMaterial3D
+ RS::get_singleton()->material_set_param(surf.material, "albedo", Color(1, 1, 1, 1));
+ RS::get_singleton()->material_set_param(surf.material, "specular", 0.5);
+ RS::get_singleton()->material_set_param(surf.material, "metallic", 0.0);
+ RS::get_singleton()->material_set_param(surf.material, "roughness", 1.0);
+ RS::get_singleton()->material_set_param(surf.material, "uv1_offset", Vector3(0, 0, 0));
+ RS::get_singleton()->material_set_param(surf.material, "uv1_scale", Vector3(1, 1, 1));
+ RS::get_singleton()->material_set_param(surf.material, "uv2_offset", Vector3(0, 0, 0));
+ RS::get_singleton()->material_set_param(surf.material, "uv2_scale", Vector3(1, 1, 1));
+ RS::get_singleton()->material_set_param(surf.material, "alpha_scissor_threshold", alpha_scissor_threshold);
+ if (msdf) {
+ RS::get_singleton()->material_set_param(surf.material, "msdf_pixel_range", TS->font_get_msdf_pixel_range(p_glyph.font_rid));
+ RS::get_singleton()->material_set_param(surf.material, "msdf_outline_size", p_outline_size);
+ }
+
+ RID shader_rid;
+ StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), true, get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, msdf, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), texture_filter, &shader_rid);
+
+ RS::get_singleton()->material_set_shader(surf.material, shader_rid);
+ RS::get_singleton()->material_set_param(surf.material, "texture_albedo", tex);
+ if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) {
+ RS::get_singleton()->material_set_render_priority(surf.material, p_priority);
+ } else {
+ surf.z_shift = p_priority * pixel_size;
+ }
+
+ surfaces[mat_hash] = surf;
+ }
+ SurfaceData &s = surfaces[mat_hash];
+
+ s.mesh_vertices.resize((s.offset + 1) * 4);
+ s.mesh_normals.resize((s.offset + 1) * 4);
+ s.mesh_tangents.resize((s.offset + 1) * 16);
+ s.mesh_colors.resize((s.offset + 1) * 4);
+ s.mesh_uvs.resize((s.offset + 1) * 4);
+
+ s.mesh_vertices.write[(s.offset * 4) + 3] = Vector3(r_offset.x + gl_of.x, r_offset.y - gl_of.y - gl_sz.y, s.z_shift);
+ s.mesh_vertices.write[(s.offset * 4) + 2] = Vector3(r_offset.x + gl_of.x + gl_sz.x, r_offset.y - gl_of.y - gl_sz.y, s.z_shift);
+ s.mesh_vertices.write[(s.offset * 4) + 1] = Vector3(r_offset.x + gl_of.x + gl_sz.x, r_offset.y - gl_of.y, s.z_shift);
+ s.mesh_vertices.write[(s.offset * 4) + 0] = Vector3(r_offset.x + gl_of.x, r_offset.y - gl_of.y, s.z_shift);
+
+ for (int i = 0; i < 4; i++) {
+ s.mesh_normals.write[(s.offset * 4) + i] = Vector3(0.0, 0.0, 1.0);
+ s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 0] = 1.0;
+ s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 1] = 0.0;
+ s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 2] = 0.0;
+ s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 3] = 1.0;
+ s.mesh_colors.write[(s.offset * 4) + i] = p_modulate;
+ s.mesh_uvs.write[(s.offset * 4) + i] = Vector2();
+
+ if (aabb == AABB()) {
+ aabb.position = s.mesh_vertices[(s.offset * 4) + i];
+ } else {
+ aabb.expand_to(s.mesh_vertices[(s.offset * 4) + i]);
+ }
+ }
+
+ if (tex != RID()) {
+ s.mesh_uvs.write[(s.offset * 4) + 3] = Vector2(gl_uv.position.x / texs.x, (gl_uv.position.y + gl_uv.size.y) / texs.y);
+ s.mesh_uvs.write[(s.offset * 4) + 2] = Vector2((gl_uv.position.x + gl_uv.size.x) / texs.x, (gl_uv.position.y + gl_uv.size.y) / texs.y);
+ s.mesh_uvs.write[(s.offset * 4) + 1] = Vector2((gl_uv.position.x + gl_uv.size.x) / texs.x, gl_uv.position.y / texs.y);
+ s.mesh_uvs.write[(s.offset * 4) + 0] = Vector2(gl_uv.position.x / texs.x, gl_uv.position.y / texs.y);
+ }
+
+ s.indices.resize((s.offset + 1) * 6);
+ s.indices.write[(s.offset * 6) + 0] = (s.offset * 4) + 0;
+ s.indices.write[(s.offset * 6) + 1] = (s.offset * 4) + 1;
+ s.indices.write[(s.offset * 6) + 2] = (s.offset * 4) + 2;
+ s.indices.write[(s.offset * 6) + 3] = (s.offset * 4) + 0;
+ s.indices.write[(s.offset * 6) + 4] = (s.offset * 4) + 2;
+ s.indices.write[(s.offset * 6) + 5] = (s.offset * 4) + 3;
+
+ s.offset++;
+ r_offset.x += p_glyph.advance * pixel_size;
+ }
+}
+
+void Label3D::_shape() {
+ // Clear mesh.
+ RS::get_singleton()->mesh_clear(mesh);
+ aabb = AABB();
+
+ // Clear materials.
+ for (const KeyValue<uint64_t, SurfaceData> &E : surfaces) {
+ RenderingServer::get_singleton()->free(E.value.material);
+ }
+ surfaces.clear();
+
+ Ref<Font> font = _get_font_or_default();
+ ERR_FAIL_COND(font.is_null());
+
+ // Update text buffer.
+ if (dirty_text) {
+ TS->shaped_text_clear(text_rid);
+ TS->shaped_text_set_direction(text_rid, text_direction);
+
+ String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
+ TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, language);
+
+ Array stt;
+ if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
+ GDVIRTUAL_CALL(_structured_text_parser, st_args, text, stt);
+ } else {
+ stt = TS->parse_structured_text(st_parser, st_args, text);
+ }
+ TS->shaped_text_set_bidi_override(text_rid, stt);
+
+ dirty_text = false;
+ dirty_font = false;
+ dirty_lines = true;
+ } else if (dirty_font) {
+ int spans = TS->shaped_get_span_count(text_rid);
+ for (int i = 0; i < spans; i++) {
+ TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features);
+ }
+
+ dirty_font = false;
+ dirty_lines = true;
+ }
+
+ if (dirty_lines) {
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free_rid(lines_rid[i]);
+ }
+ lines_rid.clear();
+
+ uint16_t autowrap_flags = TextServer::BREAK_MANDATORY;
+ switch (autowrap_mode) {
+ case AUTOWRAP_WORD_SMART:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_WORD:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_ARBITRARY:
+ autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_OFF:
+ break;
+ }
+ PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);
+
+ float max_line_w = 0.0;
+ for (int i = 0; i < line_breaks.size(); i = i + 2) {
+ RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
+ max_line_w = MAX(max_line_w, TS->shaped_text_get_width(line));
+ lines_rid.push_back(line);
+ }
+
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
+ for (int i = 0; i < lines_rid.size() - 1; i++) {
+ TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
+ }
+ }
+ dirty_lines = false;
+ }
+
+ // Generate surfaces and materials.
+ float total_h = 0.0;
+ for (int i = 0; i < lines_rid.size(); i++) {
+ total_h += (TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing) * pixel_size;
+ }
+
+ float vbegin = 0.0;
+ switch (vertical_alignment) {
+ case VERTICAL_ALIGNMENT_FILL:
+ case VERTICAL_ALIGNMENT_TOP: {
+ // Nothing.
+ } break;
+ case VERTICAL_ALIGNMENT_CENTER: {
+ vbegin = (total_h - line_spacing * pixel_size) / 2.0;
+ } break;
+ case VERTICAL_ALIGNMENT_BOTTOM: {
+ vbegin = (total_h - line_spacing * pixel_size);
+ } break;
+ }
+
+ Vector2 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size);
+ for (int i = 0; i < lines_rid.size(); i++) {
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
+ int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
+ float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size;
+
+ switch (horizontal_alignment) {
+ case HORIZONTAL_ALIGNMENT_LEFT:
+ offset.x = 0.0;
+ break;
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ offset.x = -line_width / 2.0;
+ } break;
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
+ offset.x = -line_width;
+ } break;
+ }
+ offset.x += lbl_offset.x * pixel_size;
+ offset.y -= (TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP)) * pixel_size;
+
+ if (outline_modulate.a != 0.0 && outline_size > 0) {
+ // Outline surfaces.
+ Vector2 ol_offset = offset;
+ for (int j = 0; j < gl_size; j++) {
+ _generate_glyph_surfaces(glyphs[j], ol_offset, outline_modulate, outline_render_priority, outline_size);
+ }
+ }
+
+ // Main text surfaces.
+ for (int j = 0; j < gl_size; j++) {
+ _generate_glyph_surfaces(glyphs[j], offset, modulate, render_priority);
+ }
+ offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM)) * pixel_size;
+ }
+
+ for (const KeyValue<uint64_t, SurfaceData> &E : surfaces) {
+ Array mesh_array;
+ mesh_array.resize(RS::ARRAY_MAX);
+ mesh_array[RS::ARRAY_VERTEX] = E.value.mesh_vertices;
+ mesh_array[RS::ARRAY_NORMAL] = E.value.mesh_normals;
+ mesh_array[RS::ARRAY_TANGENT] = E.value.mesh_tangents;
+ mesh_array[RS::ARRAY_COLOR] = E.value.mesh_colors;
+ mesh_array[RS::ARRAY_TEX_UV] = E.value.mesh_uvs;
+ mesh_array[RS::ARRAY_INDEX] = E.value.indices;
+
+ RS::SurfaceData sd;
+ RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, mesh_array);
+
+ sd.material = E.value.material;
+
+ RS::get_singleton()->mesh_add_surface(mesh, sd);
+ }
+}
+
+void Label3D::set_text(const String &p_string) {
+ text = p_string;
+ xl_text = tr(p_string);
+ dirty_text = true;
+ _queue_update();
+}
+
+String Label3D::get_text() const {
+ return text;
+}
+
+void Label3D::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ if (horizontal_alignment != p_alignment) {
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
+ dirty_lines = true; // Reshape lines.
+ }
+ horizontal_alignment = p_alignment;
+ _queue_update();
+ }
+}
+
+HorizontalAlignment Label3D::get_horizontal_alignment() const {
+ return horizontal_alignment;
+}
+
+void Label3D::set_vertical_alignment(VerticalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ if (vertical_alignment != p_alignment) {
+ vertical_alignment = p_alignment;
+ _queue_update();
+ }
+}
+
+VerticalAlignment Label3D::get_vertical_alignment() const {
+ return vertical_alignment;
+}
+
+void Label3D::set_text_direction(TextServer::Direction p_text_direction) {
+ ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+ if (text_direction != p_text_direction) {
+ text_direction = p_text_direction;
+ dirty_text = true;
+ _queue_update();
+ }
+}
+
+TextServer::Direction Label3D::get_text_direction() const {
+ return text_direction;
+}
+
+void Label3D::clear_opentype_features() {
+ opentype_features.clear();
+ dirty_font = true;
+ _queue_update();
+}
+
+void Label3D::set_opentype_feature(const String &p_name, int p_value) {
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
+ opentype_features[tag] = p_value;
+ dirty_font = true;
+ _queue_update();
+ }
+}
+
+int Label3D::get_opentype_feature(const String &p_name) const {
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!opentype_features.has(tag)) {
+ return -1;
+ }
+ return opentype_features[tag];
+}
+
+void Label3D::set_language(const String &p_language) {
+ if (language != p_language) {
+ language = p_language;
+ dirty_text = true;
+ _queue_update();
+ }
+}
+
+String Label3D::get_language() const {
+ return language;
+}
+
+void Label3D::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
+ if (st_parser != p_parser) {
+ st_parser = p_parser;
+ dirty_text = true;
+ _queue_update();
+ }
+}
+
+TextServer::StructuredTextParser Label3D::get_structured_text_bidi_override() const {
+ return st_parser;
+}
+
+void Label3D::set_structured_text_bidi_override_options(Array p_args) {
+ if (st_args != p_args) {
+ st_args = p_args;
+ dirty_text = true;
+ _queue_update();
+ }
+}
+
+Array Label3D::get_structured_text_bidi_override_options() const {
+ return st_args;
+}
+
+void Label3D::set_uppercase(bool p_uppercase) {
+ if (uppercase != p_uppercase) {
+ uppercase = p_uppercase;
+ dirty_text = true;
+ _queue_update();
+ }
+}
+
+bool Label3D::is_uppercase() const {
+ return uppercase;
+}
+
+void Label3D::set_render_priority(int p_priority) {
+ ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX);
+ if (render_priority != p_priority) {
+ render_priority = p_priority;
+ _queue_update();
+ }
+}
+
+int Label3D::get_render_priority() const {
+ return render_priority;
+}
+
+void Label3D::set_outline_render_priority(int p_priority) {
+ ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX);
+ if (outline_render_priority != p_priority) {
+ outline_render_priority = p_priority;
+ _queue_update();
+ }
+}
+
+int Label3D::get_outline_render_priority() const {
+ return outline_render_priority;
+}
+
+void Label3D::_font_changed() {
+ dirty_font = true;
+ _queue_update();
+}
+
+void Label3D::set_font(const Ref<Font> &p_font) {
+ if (font_override != p_font) {
+ if (font_override.is_valid()) {
+ font_override->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed"));
+ }
+ font_override = p_font;
+ dirty_font = true;
+ if (font_override.is_valid()) {
+ font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed"));
+ }
+ _queue_update();
+ }
+}
+
+Ref<Font> Label3D::get_font() const {
+ return font_override;
+}
+
+Ref<Font> Label3D::_get_font_or_default() const {
+ if (font_override.is_valid() && font_override->get_data_count() > 0) {
+ return font_override;
+ }
+
+ // 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)) {
+ return Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+ }
+ }
+ }
+
+ // Lastly, fall back on the items defined in the default Theme, if they exist.
+ {
+ 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)) {
+ return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+ }
+ }
+ }
+
+ // If they don't exist, use any type to return the default/empty value.
+ return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
+}
+
+void Label3D::set_font_size(int p_size) {
+ if (font_size != p_size) {
+ font_size = p_size;
+ dirty_font = true;
+ _queue_update();
+ }
+}
+
+int Label3D::get_font_size() const {
+ return font_size;
+}
+
+void Label3D::set_outline_size(int p_size) {
+ if (outline_size != p_size) {
+ outline_size = p_size;
+ _queue_update();
+ }
+}
+
+int Label3D::get_outline_size() const {
+ return outline_size;
+}
+
+void Label3D::set_modulate(const Color &p_color) {
+ if (modulate != p_color) {
+ modulate = p_color;
+ _queue_update();
+ }
+}
+
+Color Label3D::get_modulate() const {
+ return modulate;
+}
+
+void Label3D::set_outline_modulate(const Color &p_color) {
+ if (outline_modulate != p_color) {
+ outline_modulate = p_color;
+ _queue_update();
+ }
+}
+
+Color Label3D::get_outline_modulate() const {
+ return outline_modulate;
+}
+
+void Label3D::set_autowrap_mode(Label3D::AutowrapMode p_mode) {
+ if (autowrap_mode != p_mode) {
+ autowrap_mode = p_mode;
+ dirty_lines = true;
+ _queue_update();
+ }
+}
+
+Label3D::AutowrapMode Label3D::get_autowrap_mode() const {
+ return autowrap_mode;
+}
+
+void Label3D::set_width(float p_width) {
+ if (width != p_width) {
+ width = p_width;
+ dirty_lines = true;
+ _queue_update();
+ }
+}
+
+float Label3D::get_width() const {
+ return width;
+}
+
+void Label3D::set_pixel_size(real_t p_amount) {
+ if (pixel_size != p_amount) {
+ pixel_size = p_amount;
+ _queue_update();
+ }
+}
+
+real_t Label3D::get_pixel_size() const {
+ return pixel_size;
+}
+
+void Label3D::set_offset(const Point2 &p_offset) {
+ if (lbl_offset != p_offset) {
+ lbl_offset = p_offset;
+ _queue_update();
+ }
+}
+
+Point2 Label3D::get_offset() const {
+ return lbl_offset;
+}
+
+void Label3D::set_line_spacing(float p_line_spacing) {
+ if (line_spacing != p_line_spacing) {
+ line_spacing = p_line_spacing;
+ _queue_update();
+ }
+}
+
+float Label3D::get_line_spacing() const {
+ return line_spacing;
+}
+
+void Label3D::set_draw_flag(DrawFlags p_flag, bool p_enable) {
+ ERR_FAIL_INDEX(p_flag, FLAG_MAX);
+ if (flags[p_flag] != p_enable) {
+ flags[p_flag] = p_enable;
+ _queue_update();
+ }
+}
+
+bool Label3D::get_draw_flag(DrawFlags p_flag) const {
+ ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false);
+ return flags[p_flag];
+}
+
+void Label3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) {
+ ERR_FAIL_INDEX(p_mode, 3);
+ if (billboard_mode != p_mode) {
+ billboard_mode = p_mode;
+ _queue_update();
+ }
+}
+
+StandardMaterial3D::BillboardMode Label3D::get_billboard_mode() const {
+ return billboard_mode;
+}
+
+void Label3D::set_alpha_cut_mode(AlphaCutMode p_mode) {
+ ERR_FAIL_INDEX(p_mode, 3);
+ if (alpha_cut != p_mode) {
+ alpha_cut = p_mode;
+ _queue_update();
+ }
+}
+
+void Label3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) {
+ if (texture_filter != p_filter) {
+ texture_filter = p_filter;
+ _queue_update();
+ }
+}
+
+StandardMaterial3D::TextureFilter Label3D::get_texture_filter() const {
+ return texture_filter;
+}
+
+Label3D::AlphaCutMode Label3D::get_alpha_cut_mode() const {
+ return alpha_cut;
+}
+
+void Label3D::set_alpha_scissor_threshold(float p_threshold) {
+ if (alpha_scissor_threshold != p_threshold) {
+ alpha_scissor_threshold = p_threshold;
+ _queue_update();
+ }
+}
+
+float Label3D::get_alpha_scissor_threshold() const {
+ return alpha_scissor_threshold;
+}
+
+Label3D::Label3D() {
+ for (int i = 0; i < FLAG_MAX; i++) {
+ flags[i] = (i == FLAG_DOUBLE_SIDED);
+ }
+
+ text_rid = TS->create_shaped_text();
+
+ mesh = RenderingServer::get_singleton()->mesh_create();
+
+ set_cast_shadows_setting(SHADOW_CASTING_SETTING_OFF);
+ set_base(mesh);
+}
+
+Label3D::~Label3D() {
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free_rid(lines_rid[i]);
+ }
+ lines_rid.clear();
+
+ TS->free_rid(text_rid);
+
+ RenderingServer::get_singleton()->free(mesh);
+ for (KeyValue<uint64_t, SurfaceData> E : surfaces) {
+ RenderingServer::get_singleton()->free(E.value.material);
+ }
+ surfaces.clear();
+}
diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h
new file mode 100644
index 0000000000..f57797a247
--- /dev/null
+++ b/scene/3d/label_3d.h
@@ -0,0 +1,240 @@
+/*************************************************************************/
+/* label_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 LABEL_3D_H
+#define LABEL_3D_H
+
+#include "scene/3d/visual_instance_3d.h"
+#include "scene/resources/font.h"
+
+#include "servers/text_server.h"
+
+class Label3D : public GeometryInstance3D {
+ GDCLASS(Label3D, GeometryInstance3D);
+
+public:
+ enum DrawFlags {
+ FLAG_SHADED,
+ FLAG_DOUBLE_SIDED,
+ FLAG_DISABLE_DEPTH_TEST,
+ FLAG_FIXED_SIZE,
+ FLAG_MAX
+ };
+
+ enum AlphaCutMode {
+ ALPHA_CUT_DISABLED,
+ ALPHA_CUT_DISCARD,
+ ALPHA_CUT_OPAQUE_PREPASS
+ };
+
+ enum AutowrapMode {
+ AUTOWRAP_OFF,
+ AUTOWRAP_ARBITRARY,
+ AUTOWRAP_WORD,
+ AUTOWRAP_WORD_SMART
+ };
+
+private:
+ real_t pixel_size = 0.01;
+ bool flags[FLAG_MAX] = {};
+ AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED;
+ float alpha_scissor_threshold = 0.5;
+
+ AABB aabb;
+
+ mutable Ref<TriangleMesh> triangle_mesh;
+ RID mesh;
+ struct SurfaceData {
+ PackedVector3Array mesh_vertices;
+ PackedVector3Array mesh_normals;
+ PackedFloat32Array mesh_tangents;
+ PackedColorArray mesh_colors;
+ PackedVector2Array mesh_uvs;
+ PackedInt32Array indices;
+ int offset = 0;
+ float z_shift = 0.0;
+ RID material;
+ };
+
+ HashMap<uint64_t, SurfaceData> surfaces;
+
+ HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER;
+ VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_CENTER;
+ String text;
+ String xl_text;
+ bool uppercase = false;
+
+ AutowrapMode autowrap_mode = AUTOWRAP_OFF;
+ float width = 500.0;
+
+ int font_size = 16;
+ Ref<Font> font_override;
+ Color modulate = Color(1, 1, 1, 1);
+ Point2 lbl_offset;
+ int outline_render_priority = -1;
+ int render_priority = 0;
+
+ int outline_size = 0;
+ Color outline_modulate = Color(0, 0, 0, 1);
+
+ float line_spacing = 0.f;
+
+ Dictionary opentype_features;
+ String language;
+ TextServer::Direction text_direction = TextServer::DIRECTION_AUTO;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
+ Array st_args;
+
+ RID text_rid;
+ Vector<RID> lines_rid;
+
+ RID base_material;
+ StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED;
+ StandardMaterial3D::TextureFilter texture_filter = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS;
+
+ bool pending_update = false;
+
+ bool dirty_lines = true;
+ bool dirty_font = true;
+ bool dirty_text = true;
+
+ void _generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority = 0, int p_outline_size = 0);
+
+protected:
+ GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
+
+ void _notification(int p_what);
+
+ 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;
+ void _validate_property(PropertyInfo &property) const override;
+
+ void _im_update();
+ void _font_changed();
+ void _queue_update();
+
+ void _shape();
+
+public:
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
+
+ void set_vertical_alignment(VerticalAlignment p_alignment);
+ VerticalAlignment get_vertical_alignment() const;
+
+ void set_render_priority(int p_priority);
+ int get_render_priority() const;
+
+ void set_outline_render_priority(int p_priority);
+ int get_outline_render_priority() const;
+
+ void set_text(const String &p_string);
+ String get_text() const;
+
+ void set_text_direction(TextServer::Direction p_text_direction);
+ TextServer::Direction get_text_direction() const;
+
+ void set_opentype_feature(const String &p_name, int p_value);
+ int get_opentype_feature(const String &p_name) const;
+ void clear_opentype_features();
+
+ void set_language(const String &p_language);
+ String get_language() const;
+
+ void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
+ TextServer::StructuredTextParser get_structured_text_bidi_override() const;
+
+ void set_structured_text_bidi_override_options(Array p_args);
+ Array get_structured_text_bidi_override_options() const;
+
+ void set_uppercase(bool p_uppercase);
+ bool is_uppercase() const;
+
+ void set_font(const Ref<Font> &p_font);
+ Ref<Font> get_font() const;
+ Ref<Font> _get_font_or_default() const;
+
+ void set_font_size(int p_size);
+ int get_font_size() const;
+
+ void set_outline_size(int p_size);
+ int get_outline_size() const;
+
+ void set_line_spacing(float p_size);
+ float get_line_spacing() const;
+
+ void set_modulate(const Color &p_color);
+ Color get_modulate() const;
+
+ void set_outline_modulate(const Color &p_color);
+ Color get_outline_modulate() const;
+
+ void set_autowrap_mode(AutowrapMode p_mode);
+ AutowrapMode get_autowrap_mode() const;
+
+ void set_width(float p_width);
+ float get_width() const;
+
+ void set_pixel_size(real_t p_amount);
+ real_t get_pixel_size() const;
+
+ void set_offset(const Point2 &p_offset);
+ Point2 get_offset() const;
+
+ void set_draw_flag(DrawFlags p_flag, bool p_enable);
+ bool get_draw_flag(DrawFlags p_flag) const;
+
+ void set_alpha_cut_mode(AlphaCutMode p_mode);
+ AlphaCutMode get_alpha_cut_mode() const;
+
+ void set_alpha_scissor_threshold(float p_threshold);
+ float get_alpha_scissor_threshold() const;
+
+ void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode);
+ StandardMaterial3D::BillboardMode get_billboard_mode() const;
+
+ void set_texture_filter(StandardMaterial3D::TextureFilter p_filter);
+ StandardMaterial3D::TextureFilter get_texture_filter() const;
+
+ virtual AABB get_aabb() const override;
+ Ref<TriangleMesh> generate_triangle_mesh() const;
+
+ Label3D();
+ ~Label3D();
+};
+
+VARIANT_ENUM_CAST(Label3D::AutowrapMode);
+VARIANT_ENUM_CAST(Label3D::DrawFlags);
+VARIANT_ENUM_CAST(Label3D::AlphaCutMode);
+
+#endif // LABEL_3D_H
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 5d9ae019c2..c95806b2d0 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -74,6 +74,43 @@ bool Light3D::is_negative() const {
return negative;
}
+void Light3D::set_enable_distance_fade(bool p_enable) {
+ distance_fade_enabled = p_enable;
+ RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length);
+ notify_property_list_changed();
+}
+
+bool Light3D::is_distance_fade_enabled() const {
+ return distance_fade_enabled;
+}
+
+void Light3D::set_distance_fade_begin(real_t p_distance) {
+ distance_fade_begin = p_distance;
+ RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length);
+}
+
+real_t Light3D::get_distance_fade_begin() const {
+ return distance_fade_begin;
+}
+
+void Light3D::set_distance_fade_shadow(real_t p_distance) {
+ distance_fade_shadow = p_distance;
+ RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length);
+}
+
+real_t Light3D::get_distance_fade_shadow() const {
+ return distance_fade_shadow;
+}
+
+void Light3D::set_distance_fade_length(real_t p_length) {
+ distance_fade_length = p_length;
+ RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length);
+}
+
+real_t Light3D::get_distance_fade_length() const {
+ return distance_fade_length;
+}
+
void Light3D::set_cull_mask(uint32_t p_cull_mask) {
cull_mask = p_cull_mask;
RS::get_singleton()->light_set_cull_mask(light, p_cull_mask);
@@ -94,15 +131,6 @@ Color Light3D::get_color() const {
return color;
}
-void Light3D::set_shadow_color(const Color &p_shadow_color) {
- shadow_color = p_shadow_color;
- RS::get_singleton()->light_set_shadow_color(light, p_shadow_color);
-}
-
-Color Light3D::get_shadow_color() const {
- return shadow_color;
-}
-
void Light3D::set_shadow_reverse_cull_face(bool p_enable) {
reverse_cull = p_enable;
RS::get_singleton()->light_set_reverse_cull_face_mode(light, reverse_cull);
@@ -128,10 +156,6 @@ AABB Light3D::get_aabb() const {
return AABB();
}
-Vector<Face3> Light3D::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
void Light3D::set_bake_mode(BakeMode p_mode) {
bake_mode = p_mode;
RS::get_singleton()->light_set_bake_mode(light, RS::LightBakeMode(p_mode));
@@ -177,12 +201,11 @@ void Light3D::_update_visibility() {
}
void Light3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- _update_visibility();
- }
-
- if (p_what == NOTIFICATION_ENTER_TREE) {
- _update_visibility();
+ switch (p_what) {
+ case NOTIFICATION_VISIBILITY_CHANGED:
+ case NOTIFICATION_ENTER_TREE: {
+ _update_visibility();
+ } break;
}
}
@@ -196,7 +219,7 @@ bool Light3D::is_editor_only() const {
}
void Light3D::_validate_property(PropertyInfo &property) const {
- if (!shadow && (property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur")) {
+ if (!shadow && (property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur" || property.name == "distance_fade_shadow")) {
property.usage = PROPERTY_USAGE_NO_EDITOR;
}
@@ -204,6 +227,11 @@ void Light3D::_validate_property(PropertyInfo &property) const {
// Angular distance is only used in DirectionalLight3D.
property.usage = PROPERTY_USAGE_NONE;
}
+
+ if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_shadow" || property.name == "distance_fade_length")) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+
VisualInstance3D::_validate_property(property);
}
@@ -223,15 +251,24 @@ void Light3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cull_mask", "cull_mask"), &Light3D::set_cull_mask);
ClassDB::bind_method(D_METHOD("get_cull_mask"), &Light3D::get_cull_mask);
+ ClassDB::bind_method(D_METHOD("set_enable_distance_fade", "enable"), &Light3D::set_enable_distance_fade);
+ ClassDB::bind_method(D_METHOD("is_distance_fade_enabled"), &Light3D::is_distance_fade_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_distance_fade_begin", "distance"), &Light3D::set_distance_fade_begin);
+ ClassDB::bind_method(D_METHOD("get_distance_fade_begin"), &Light3D::get_distance_fade_begin);
+
+ ClassDB::bind_method(D_METHOD("set_distance_fade_shadow", "distance"), &Light3D::set_distance_fade_shadow);
+ ClassDB::bind_method(D_METHOD("get_distance_fade_shadow"), &Light3D::get_distance_fade_shadow);
+
+ ClassDB::bind_method(D_METHOD("set_distance_fade_length", "distance"), &Light3D::set_distance_fade_length);
+ ClassDB::bind_method(D_METHOD("get_distance_fade_length"), &Light3D::get_distance_fade_length);
+
ClassDB::bind_method(D_METHOD("set_color", "color"), &Light3D::set_color);
ClassDB::bind_method(D_METHOD("get_color"), &Light3D::get_color);
ClassDB::bind_method(D_METHOD("set_shadow_reverse_cull_face", "enable"), &Light3D::set_shadow_reverse_cull_face);
ClassDB::bind_method(D_METHOD("get_shadow_reverse_cull_face"), &Light3D::get_shadow_reverse_cull_face);
- ClassDB::bind_method(D_METHOD("set_shadow_color", "shadow_color"), &Light3D::set_shadow_color);
- ClassDB::bind_method(D_METHOD("get_shadow_color"), &Light3D::get_shadow_color);
-
ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode);
ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode);
@@ -247,17 +284,21 @@ void Light3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Dynamic,Static"), "set_bake_mode", "get_bake_mode");
+ 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_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_shadow_color", "get_shadow_color");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_normal_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0.1,8,0.01"), "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"), "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"), "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"), "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("", "");
@@ -284,8 +325,8 @@ void Light3D::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_MAX);
BIND_ENUM_CONSTANT(BAKE_DISABLED);
- BIND_ENUM_CONSTANT(BAKE_DYNAMIC);
BIND_ENUM_CONSTANT(BAKE_STATIC);
+ BIND_ENUM_CONSTANT(BAKE_DYNAMIC);
}
Light3D::Light3D(RenderingServer::LightType p_type) {
@@ -367,13 +408,13 @@ bool DirectionalLight3D::is_blend_splits_enabled() const {
return blend_splits;
}
-void DirectionalLight3D::set_sky_only(bool p_sky_only) {
- sky_only = p_sky_only;
- RS::get_singleton()->light_directional_set_sky_only(light, p_sky_only);
+void DirectionalLight3D::set_sky_mode(SkyMode p_mode) {
+ sky_mode = p_mode;
+ RS::get_singleton()->light_directional_set_sky_mode(light, RS::LightDirectionalSkyMode(p_mode));
}
-bool DirectionalLight3D::is_sky_only() const {
- return sky_only;
+DirectionalLight3D::SkyMode DirectionalLight3D::get_sky_mode() const {
+ return sky_mode;
}
void DirectionalLight3D::_validate_property(PropertyInfo &property) const {
@@ -392,6 +433,12 @@ void DirectionalLight3D::_validate_property(PropertyInfo &property) const {
property.usage = PROPERTY_USAGE_NONE;
}
+ if (property.name == "distance_fade_enabled" || property.name == "distance_fade_begin" || property.name == "distance_fade_shadow" || property.name == "distance_fade_length") {
+ // Not relevant for DirectionalLight3D, as the light LOD system only pertains to point lights.
+ // For DirectionalLight3D, `directional_shadow_max_distance` can be used instead.
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+
Light3D::_validate_property(property);
}
@@ -402,8 +449,8 @@ void DirectionalLight3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blend_splits", "enabled"), &DirectionalLight3D::set_blend_splits);
ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight3D::is_blend_splits_enabled);
- ClassDB::bind_method(D_METHOD("set_sky_only", "priority"), &DirectionalLight3D::set_sky_only);
- ClassDB::bind_method(D_METHOD("is_sky_only"), &DirectionalLight3D::is_sky_only);
+ ClassDB::bind_method(D_METHOD("set_sky_mode", "mode"), &DirectionalLight3D::set_sky_mode);
+ ClassDB::bind_method(D_METHOD("get_sky_mode"), &DirectionalLight3D::get_sky_mode);
ADD_GROUP("Directional Shadow", "directional_shadow_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 4 Splits (Slow)"), "set_shadow_mode", "get_shadow_mode");
@@ -415,11 +462,15 @@ void DirectionalLight3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,8192,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_RANGE, "0,1024,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_in_sky_only"), "set_sky_only", "is_sky_only");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sky_mode", PROPERTY_HINT_ENUM, "Light and Sky,Light Only,Sky Only"), "set_sky_mode", "get_sky_mode");
BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL);
BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS);
BIND_ENUM_CONSTANT(SHADOW_PARALLEL_4_SPLITS);
+
+ BIND_ENUM_CONSTANT(SKY_MODE_LIGHT_AND_SKY);
+ BIND_ENUM_CONSTANT(SKY_MODE_LIGHT_ONLY);
+ BIND_ENUM_CONSTANT(SKY_MODE_SKY_ONLY);
}
DirectionalLight3D::DirectionalLight3D() :
@@ -430,6 +481,7 @@ DirectionalLight3D::DirectionalLight3D() :
set_param(PARAM_SHADOW_BIAS, 0.1);
set_shadow_mode(SHADOW_PARALLEL_4_SPLITS);
blend_splits = false;
+ set_sky_mode(SKY_MODE_LIGHT_AND_SKY);
}
void OmniLight3D::set_shadow_mode(ShadowMode p_mode) {
@@ -445,7 +497,7 @@ TypedArray<String> OmniLight3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!has_shadow() && get_projector().is_valid()) {
- warnings.push_back(TTR("Projector texture only works with shadows active."));
+ warnings.push_back(RTR("Projector texture only works with shadows active."));
}
return warnings;
@@ -475,11 +527,11 @@ TypedArray<String> SpotLight3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) {
- warnings.push_back(TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."));
+ warnings.push_back(RTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."));
}
if (!has_shadow() && get_projector().is_valid()) {
- warnings.push_back(TTR("Projector texture only works with shadows active."));
+ warnings.push_back(RTR("Projector texture only works with shadows active."));
}
return warnings;
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index a9f5ce27b4..383fa644e5 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,6 @@
class Light3D : public VisualInstance3D {
GDCLASS(Light3D, VisualInstance3D);
- OBJ_CATEGORY("3D Light Nodes");
public:
enum Param {
@@ -63,18 +62,21 @@ public:
enum BakeMode {
BAKE_DISABLED,
+ BAKE_STATIC,
BAKE_DYNAMIC,
- BAKE_STATIC
};
private:
Color color;
real_t param[PARAM_MAX] = {};
- Color shadow_color;
bool shadow = false;
bool negative = false;
bool reverse_cull = false;
uint32_t cull_mask = 0;
+ bool distance_fade_enabled = false;
+ real_t distance_fade_begin = 40.0;
+ real_t distance_fade_shadow = 50.0;
+ real_t distance_fade_length = 10.0;
RS::LightType type = RenderingServer::LIGHT_DIRECTIONAL;
bool editor_only = false;
void _update_visibility();
@@ -107,15 +109,24 @@ public:
void set_negative(bool p_enable);
bool is_negative() const;
+ void set_enable_distance_fade(bool p_enable);
+ bool is_distance_fade_enabled() const;
+
+ void set_distance_fade_begin(real_t p_distance);
+ real_t get_distance_fade_begin() const;
+
+ void set_distance_fade_shadow(real_t p_distance);
+ real_t get_distance_fade_shadow() const;
+
+ void set_distance_fade_length(real_t p_length);
+ real_t get_distance_fade_length() const;
+
void set_cull_mask(uint32_t p_cull_mask);
uint32_t get_cull_mask() const;
void set_color(const Color &p_color);
Color get_color() const;
- void set_shadow_color(const Color &p_shadow_color);
- Color get_shadow_color() const;
-
void set_shadow_reverse_cull_face(bool p_enable);
bool get_shadow_reverse_cull_face() const;
@@ -126,7 +137,6 @@ public:
Ref<Texture2D> get_projector() const;
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
Light3D();
~Light3D();
@@ -145,10 +155,16 @@ public:
SHADOW_PARALLEL_4_SPLITS,
};
+ enum SkyMode {
+ SKY_MODE_LIGHT_AND_SKY,
+ SKY_MODE_LIGHT_ONLY,
+ SKY_MODE_SKY_ONLY,
+ };
+
private:
bool blend_splits;
ShadowMode shadow_mode;
- bool sky_only = false;
+ SkyMode sky_mode = SKY_MODE_LIGHT_AND_SKY;
protected:
static void _bind_methods();
@@ -161,13 +177,14 @@ public:
void set_blend_splits(bool p_enable);
bool is_blend_splits_enabled() const;
- void set_sky_only(bool p_sky_only);
- bool is_sky_only() const;
+ void set_sky_mode(SkyMode p_mode);
+ SkyMode get_sky_mode() const;
DirectionalLight3D();
};
VARIANT_ENUM_CAST(DirectionalLight3D::ShadowMode)
+VARIANT_ENUM_CAST(DirectionalLight3D::SkyMode)
class OmniLight3D : public Light3D {
GDCLASS(OmniLight3D, Light3D);
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 1b5d4ad243..5c63bdcf1d 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -217,7 +217,7 @@ LightmapGIData::~LightmapGIData() {
void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &meshes, Vector<LightsFound> &lights, Vector<Vector3> &probes) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
- if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) {
+ if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) {
Ref<Mesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
bool all_have_uv2_and_normal = true;
@@ -318,12 +318,11 @@ void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &m
int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const {
int over = 0;
int under = 0;
- int coplanar = 0;
const BSPSimplex &s = p_simplices[p_simplex];
for (int i = 0; i < 4; i++) {
const Vector3 v = p_points[s.vertices[i]];
- if (p_plane.has_point(v)) { //coplanar
- coplanar++;
+ if (p_plane.has_point(v)) {
+ // Coplanar.
} else if (p_plane.is_point_over(v)) {
over++;
} else {
@@ -614,7 +613,7 @@ void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, f
}
LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_path, Lightmapper::BakeStepFunc p_bake_step, void *p_bake_userdata) {
- if (p_image_data_path == "") {
+ if (p_image_data_path.is_empty()) {
if (get_light_data().is_null()) {
return BAKE_ERROR_NO_SAVE_PATH;
}
@@ -635,7 +634,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
bsud.to_percent = 0.8;
if (p_bake_step) {
- p_bake_step(0.0, TTR("Finding meshes, lights and probes"), p_bake_userdata, true);
+ p_bake_step(0.0, RTR("Finding meshes, lights and probes"), p_bake_userdata, true);
}
/* STEP 1, FIND MESHES, LIGHTS AND PROBES */
Vector<Lightmapper::MeshData> mesh_data;
@@ -655,7 +654,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
for (int m_i = 0; m_i < meshes_found.size(); m_i++) {
if (p_bake_step) {
float p = (float)(m_i) / meshes_found.size();
- p_bake_step(p * 0.1, vformat(TTR("Preparing geometry %d/%d"), m_i, meshes_found.size()), p_bake_userdata, false);
+ p_bake_step(p * 0.1, vformat(RTR("Preparing geometry %d/%d"), m_i, meshes_found.size()), p_bake_userdata, false);
}
MeshesFound &mf = meshes_found.write[m_i];
@@ -790,7 +789,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
/* STEP 2, CREATE PROBES */
if (p_bake_step) {
- p_bake_step(0.3, TTR("Creating probes"), p_bake_userdata, true);
+ p_bake_step(0.3, RTR("Creating probes"), p_bake_userdata, true);
}
//bounds need to include the user probes
@@ -832,7 +831,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
for (int i = 0; i < mesh_data.size(); i++) {
if (p_bake_step) {
float p = (float)(i) / mesh_data.size();
- p_bake_step(0.3 + p * 0.1, vformat(TTR("Creating probes from mesh %d/%d"), i, mesh_data.size()), p_bake_userdata, false);
+ p_bake_step(0.3 + p * 0.1, vformat(RTR("Creating probes from mesh %d/%d"), i, mesh_data.size()), p_bake_userdata, false);
}
for (int j = 0; j < mesh_data[i].points.size(); j += 3) {
@@ -873,7 +872,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
// Add everything to lightmapper
if (p_bake_step) {
- p_bake_step(0.4, TTR("Preparing Lightmapper"), p_bake_userdata, true);
+ p_bake_step(0.4, RTR("Preparing Lightmapper"), p_bake_userdata, true);
}
{
@@ -884,15 +883,16 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
Light3D *light = lights_found[i].light;
Transform3D xf = lights_found[i].xform;
+ Color linear_color = light->get_color().srgb_to_linear();
if (Object::cast_to<DirectionalLight3D>(light)) {
DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light);
- lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE));
+ lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE));
} else if (Object::cast_to<OmniLight3D>(light)) {
OmniLight3D *l = Object::cast_to<OmniLight3D>(light);
- lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE));
+ lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE));
} else if (Object::cast_to<SpotLight3D>(light)) {
SpotLight3D *l = Object::cast_to<SpotLight3D>(light);
- lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE));
+ lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE));
}
}
for (int i = 0; i < probes_found.size(); i++) {
@@ -906,7 +906,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
// Add everything to lightmapper
if (environment_mode != ENVIRONMENT_MODE_DISABLED) {
if (p_bake_step) {
- p_bake_step(4.1, TTR("Preparing Environment"), p_bake_userdata, true);
+ p_bake_step(4.1, RTR("Preparing Environment"), p_bake_userdata, true);
}
environment_transform = get_global_transform().basis;
@@ -941,11 +941,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
c.r *= environment_custom_energy;
c.g *= environment_custom_energy;
c.b *= environment_custom_energy;
- for (int i = 0; i < 128; i++) {
- for (int j = 0; j < 64; j++) {
- environment_image->set_pixel(i, j, c);
- }
- }
+ environment_image->fill(c);
} break;
}
@@ -985,7 +981,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
}
config->set_value("remap", "importer", "2d_array_texture");
- config->set_value("remap", "type", "StreamTexture2DArray");
+ config->set_value("remap", "type", "CompressedTexture2DArray");
if (!config->has_section_key("params", "compress/mode")) {
config->set_value("params", "compress/mode", 2); //user may want another compression, so leave it be
}
@@ -1049,7 +1045,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
//Obtain solved simplices
if (p_bake_step) {
- p_bake_step(0.8, TTR("Generating Probe Volumes"), p_bake_userdata, true);
+ p_bake_step(0.8, RTR("Generating Probe Volumes"), p_bake_userdata, true);
}
Vector<Delaunay3D::OutputSimplex> solved_simplices = Delaunay3D::tetrahedralize(points);
@@ -1104,7 +1100,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
//#define DEBUG_SIMPLICES_AS_OBJ_FILE
#ifdef DEBUG_SIMPLICES_AS_OBJ_FILE
{
- FileAccessRef f = FileAccess::open("res://bsp.obj", FileAccess::WRITE);
+ Ref<FileAccess> f = FileAccess::open("res://bsp.obj", FileAccess::WRITE);
for (uint32_t i = 0; i < bsp_simplices.size(); i++) {
f->store_line("o Simplex" + itos(i));
for (int j = 0; j < 4; j++) {
@@ -1121,7 +1117,6 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
f->store_line(vformat("f %d %d %d", 4 * i + face_order[j][0], 4 * i + face_order[j][1], 4 * i + face_order[j][2]));
}
}
- f->close();
}
#endif
@@ -1133,7 +1128,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
}
if (p_bake_step) {
- p_bake_step(0.9, TTR("Generating Probe Acceleration Structures"), p_bake_userdata, true);
+ p_bake_step(0.9, RTR("Generating Probe Acceleration Structures"), p_bake_userdata, true);
}
_compute_bsp_tree(points, bsp_planes, planes_tested, bsp_simplices, bsp_simplex_indices, bsp_nodes);
@@ -1153,7 +1148,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
}
//#define DEBUG_BSP_TREE
#ifdef DEBUG_BSP_TREE
- FileAccessRef f = FileAccess::open("res://bsp.txt", FileAccess::WRITE);
+ Ref<FileAccess> f = FileAccess::open("res://bsp.txt", FileAccess::WRITE);
for (uint32_t i = 0; i < bsp_nodes.size(); i++) {
f->store_line(itos(i) + " - plane: " + bsp_nodes[i].plane + " over: " + itos(bsp_nodes[i].over) + " under: " + itos(bsp_nodes[i].under));
}
@@ -1179,16 +1174,18 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
}
void LightmapGI::_notification(int p_what) {
- if (p_what == NOTIFICATION_POST_ENTER_TREE) {
- if (light_data.is_valid()) {
- _assign_lightmaps();
- }
- }
+ switch (p_what) {
+ case NOTIFICATION_POST_ENTER_TREE: {
+ if (light_data.is_valid()) {
+ _assign_lightmaps();
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- if (light_data.is_valid()) {
- _clear_lightmaps();
- }
+ case NOTIFICATION_EXIT_TREE: {
+ if (light_data.is_valid()) {
+ _clear_lightmaps();
+ }
+ } break;
}
}
@@ -1264,10 +1261,6 @@ AABB LightmapGI::get_aabb() const {
return AABB();
}
-Vector<Face3> LightmapGI::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
void LightmapGI::set_use_denoiser(bool p_enable) {
use_denoiser = p_enable;
}
diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h
index e73350fd64..d29d7a7c28 100644
--- a/scene/3d/lightmap_gi.h
+++ b/scene/3d/lightmap_gi.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -267,7 +267,6 @@ public:
GenerateProbes get_generate_probes() const;
AABB get_aabb() const override;
- Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr);
LightmapGI();
diff --git a/scene/3d/lightmap_probe.cpp b/scene/3d/lightmap_probe.cpp
index 830b97ffab..641b0a4c48 100644
--- a/scene/3d/lightmap_probe.cpp
+++ b/scene/3d/lightmap_probe.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/lightmap_probe.h b/scene/3d/lightmap_probe.h
index df87ed49dd..11b35451a1 100644
--- a/scene/3d/lightmap_probe.h
+++ b/scene/3d/lightmap_probe.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/lightmapper.cpp b/scene/3d/lightmapper.cpp
index 9e5078ba95..8ab710d98f 100644
--- a/scene/3d/lightmapper.cpp
+++ b/scene/3d/lightmapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h
index d028628901..55d9a52a28 100644
--- a/scene/3d/lightmapper.h
+++ b/scene/3d/lightmapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -88,7 +88,9 @@ public:
instID(INVALID_GEOMETRY_ID) {}
/*! Tests if we hit something. */
- _FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; }
+ _FORCE_INLINE_ explicit operator bool() const {
+ return geomID != INVALID_GEOMETRY_ID;
+ }
public:
Vector3 org; //!< Ray origin + tnear
@@ -116,7 +118,7 @@ public:
virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) = 0;
virtual void commit() = 0;
- virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0;
+ virtual void set_mesh_filter(const RBSet<int> &p_mesh_ids) = 0;
virtual void clear_mesh_filter() = 0;
static Ref<LightmapRaycaster> create();
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index ae686143e4..189ab7fc3b 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,9 +42,9 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) {
return false;
}
- Map<StringName, int>::Element *E = blend_shape_properties.find(p_name);
+ HashMap<StringName, int>::Iterator E = blend_shape_properties.find(p_name);
if (E) {
- set_blend_shape_value(E->get(), p_value);
+ set_blend_shape_value(E->value, p_value);
return true;
}
@@ -66,9 +66,9 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const {
return false;
}
- const Map<StringName, int>::Element *E = blend_shape_properties.find(p_name);
+ HashMap<StringName, int>::ConstIterator E = blend_shape_properties.find(p_name);
if (E) {
- r_ret = get_blend_shape_value(E->get());
+ r_ret = get_blend_shape_value(E->value);
return true;
}
@@ -219,18 +219,6 @@ AABB MeshInstance3D::get_aabb() const {
return AABB();
}
-Vector<Face3> MeshInstance3D::get_faces(uint32_t p_usage_flags) const {
- if (!(p_usage_flags & (FACES_SOLID | FACES_ENCLOSING))) {
- return Vector<Face3>();
- }
-
- if (mesh.is_null()) {
- return Vector<Face3>();
- }
-
- return mesh->get_faces();
-}
-
Node *MeshInstance3D::create_trimesh_collision_node() {
if (mesh.is_null()) {
return nullptr;
@@ -328,8 +316,10 @@ void MeshInstance3D::create_multiple_convex_collisions() {
}
void MeshInstance3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- _resolve_skeleton_path();
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ _resolve_skeleton_path();
+ } break;
}
}
diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h
index 8f21726601..dc9c64fa41 100644
--- a/scene/3d/mesh_instance_3d.h
+++ b/scene/3d/mesh_instance_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,7 +47,7 @@ protected:
NodePath skeleton_path = NodePath("..");
LocalVector<float> blend_shape_tracks;
- Map<StringName, int> blend_shape_properties;
+ HashMap<StringName, int> blend_shape_properties;
Vector<Ref<Material>> surface_override_materials;
void _mesh_changed();
@@ -93,7 +93,6 @@ public:
void create_debug_tangents();
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
MeshInstance3D();
~MeshInstance3D();
diff --git a/scene/3d/multimesh_instance_3d.cpp b/scene/3d/multimesh_instance_3d.cpp
index 2adef115cf..d197388fad 100644
--- a/scene/3d/multimesh_instance_3d.cpp
+++ b/scene/3d/multimesh_instance_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,10 +49,6 @@ Ref<MultiMesh> MultiMeshInstance3D::get_multimesh() const {
return multimesh;
}
-Vector<Face3> MultiMeshInstance3D::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
AABB MultiMeshInstance3D::get_aabb() const {
if (multimesh.is_null()) {
return AABB();
diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h
index 63735fd3a6..111f1e9c09 100644
--- a/scene/3d/multimesh_instance_3d.h
+++ b/scene/3d/multimesh_instance_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,8 +44,6 @@ protected:
// bind helpers
public:
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
-
void set_multimesh(const Ref<MultiMesh> &p_multimesh);
Ref<MultiMesh> get_multimesh() const;
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index 1bc7d20c19..86c11b3789 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -62,6 +62,9 @@ void NavigationAgent3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent3D::set_path_max_distance);
ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent3D::get_path_max_distance);
+ ClassDB::bind_method(D_METHOD("set_navigable_layers", "navigable_layers"), &NavigationAgent3D::set_navigable_layers);
+ ClassDB::bind_method(D_METHOD("get_navigable_layers"), &NavigationAgent3D::get_navigable_layers);
+
ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent3D::set_target_location);
ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent3D::get_target_location);
ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent3D::get_next_location);
@@ -85,6 +88,7 @@ void NavigationAgent3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "navigable_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigable_layers", "get_navigable_layers");
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
@@ -103,10 +107,12 @@ void NavigationAgent3D::_notification(int p_what) {
}
set_physics_process_internal(true);
} break;
+
case NOTIFICATION_EXIT_TREE: {
agent_parent = nullptr;
set_physics_process_internal(false);
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent) {
NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
@@ -131,6 +137,18 @@ NavigationAgent3D::~NavigationAgent3D() {
agent = RID(); // Pointless
}
+void NavigationAgent3D::set_navigable_layers(uint32_t p_layers) {
+ bool layers_changed = navigable_layers != p_layers;
+ navigable_layers = p_layers;
+ if (layers_changed) {
+ _request_repath();
+ }
+}
+
+uint32_t NavigationAgent3D::get_navigable_layers() const {
+ return navigable_layers;
+}
+
void NavigationAgent3D::set_target_desired_distance(real_t p_dd) {
target_desired_distance = p_dd;
}
@@ -179,10 +197,7 @@ real_t NavigationAgent3D::get_path_max_distance() {
void NavigationAgent3D::set_target_location(Vector3 p_location) {
target_location = p_location;
- navigation_path.clear();
- target_reached = false;
- navigation_finished = false;
- update_frame_id = 0;
+ _request_repath();
}
Vector3 NavigationAgent3D::get_target_location() const {
@@ -248,7 +263,7 @@ TypedArray<String> NavigationAgent3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node3D>(get_parent())) {
- warnings.push_back(TTR("The NavigationAgent3D can be used only under a spatial node."));
+ warnings.push_back(RTR("The NavigationAgent3D can be used only under a spatial node."));
}
return warnings;
@@ -292,7 +307,7 @@ void NavigationAgent3D::update_navigation() {
}
if (reload_path) {
- navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true);
+ navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true, navigable_layers);
navigation_finished = false;
nav_path_index = 0;
emit_signal(SNAME("path_changed"));
@@ -318,6 +333,13 @@ void NavigationAgent3D::update_navigation() {
}
}
+void NavigationAgent3D::_request_repath() {
+ navigation_path.clear();
+ target_reached = false;
+ navigation_finished = false;
+ update_frame_id = 0;
+}
+
void NavigationAgent3D::_check_distance_to_target() {
if (!target_reached) {
if (distance_to_target() < target_desired_distance) {
diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h
index bebfdc5f7e..283b99a24f 100644
--- a/scene/3d/navigation_agent_3d.h
+++ b/scene/3d/navigation_agent_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,20 +42,22 @@ class NavigationAgent3D : public Node {
RID agent;
+ uint32_t navigable_layers = 1;
+
real_t target_desired_distance = 1.0;
- real_t radius;
+ real_t radius = 0.0;
real_t navigation_height_offset = 0.0;
- bool ignore_y;
- real_t neighbor_dist;
- int max_neighbors;
- real_t time_horizon;
- real_t max_speed;
+ bool ignore_y = false;
+ real_t neighbor_dist = 0.0;
+ int max_neighbors = 0;
+ real_t time_horizon = 0.0;
+ real_t max_speed = 0.0;
real_t path_max_distance = 3.0;
Vector3 target_location;
Vector<Vector3> navigation_path;
- int nav_path_index;
+ int nav_path_index = 0;
bool velocity_submitted = false;
Vector3 prev_safe_velocity;
/// The submitted target velocity
@@ -63,7 +65,7 @@ class NavigationAgent3D : public Node {
bool target_reached = false;
bool navigation_finished = true;
// No initialized on purpose
- uint32_t update_frame_id;
+ uint32_t update_frame_id = 0;
protected:
static void _bind_methods();
@@ -77,6 +79,9 @@ public:
return agent;
}
+ void set_navigable_layers(uint32_t p_layers);
+ uint32_t get_navigable_layers() const;
+
void set_target_desired_distance(real_t p_dd);
real_t get_target_desired_distance() const {
return target_desired_distance;
@@ -146,6 +151,7 @@ public:
private:
void update_navigation();
+ void _request_repath();
void _check_distance_to_target();
};
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index f9fff802e0..fa6a633dee 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,6 +35,8 @@
#include "servers/navigation_server_3d.h"
void NavigationObstacle3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle3D::get_rid);
+
ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius);
ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius);
@@ -54,28 +56,32 @@ void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const {
void NavigationObstacle3D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
- initialize_agent();
+ case NOTIFICATION_ENTER_TREE: {
parent_node3d = Object::cast_to<Node3D>(get_parent());
+ reevaluate_agent_radius();
if (parent_node3d != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map());
}
set_physics_process_internal(true);
} break;
+
case NOTIFICATION_EXIT_TREE: {
parent_node3d = nullptr;
set_physics_process_internal(false);
} break;
+
case NOTIFICATION_PARENTED: {
parent_node3d = Object::cast_to<Node3D>(get_parent());
reevaluate_agent_radius();
} break;
+
case NOTIFICATION_UNPARENTED: {
parent_node3d = nullptr;
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (parent_node3d) {
+ if (parent_node3d && parent_node3d->is_inside_tree()) {
NavigationServer3D::get_singleton()->agent_set_position(agent, parent_node3d->get_global_transform().origin);
PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent());
@@ -91,6 +97,7 @@ void NavigationObstacle3D::_notification(int p_what) {
NavigationObstacle3D::NavigationObstacle3D() {
agent = NavigationServer3D::get_singleton()->agent_create();
+ initialize_agent();
}
NavigationObstacle3D::~NavigationObstacle3D() {
@@ -102,7 +109,12 @@ TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node3D>(get_parent())) {
- warnings.push_back(TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object."));
+ warnings.push_back(RTR("The NavigationObstacle3D only serves to provide collision avoidance to a Node3D inheriting parent object."));
+ }
+
+ if (Object::cast_to<StaticBody3D>(get_parent())) {
+ warnings.push_back(RTR("The NavigationObstacle3D is intended for constantly moving bodies like CharacterBody3D or RigidDynamicBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
+ "\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail"));
}
return warnings;
@@ -118,19 +130,19 @@ void NavigationObstacle3D::initialize_agent() {
void NavigationObstacle3D::reevaluate_agent_radius() {
if (!estimate_radius) {
NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
- } else if (parent_node3d) {
+ } else if (parent_node3d && parent_node3d->is_inside_tree()) {
NavigationServer3D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
}
}
real_t NavigationObstacle3D::estimate_agent_radius() const {
- if (parent_node3d) {
+ if (parent_node3d && parent_node3d->is_inside_tree()) {
// Estimate the radius of this physics body
real_t radius = 0.0;
for (int i(0); i < parent_node3d->get_child_count(); i++) {
// For each collision shape
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(parent_node3d->get_child(i));
- if (cs) {
+ if (cs && cs->is_inside_tree()) {
// Take the distance between the Body center to the shape center
real_t r = cs->get_transform().origin.length();
if (cs->get_shape().is_valid()) {
@@ -141,6 +153,9 @@ real_t NavigationObstacle3D::estimate_agent_radius() const {
r *= MAX(s.x, MAX(s.y, s.z));
// Takes the biggest radius
radius = MAX(radius, r);
+ } else if (cs && !cs->is_inside_tree()) {
+ WARN_PRINT("A CollisionShape3D of the NavigationObstacle3D parent node was not inside the SceneTree when estimating the obstacle radius."
+ "\nMove the NavigationObstacle3D to a child position below any CollisionShape3D node of the parent node so the CollisionShape3D is already inside the SceneTree.");
}
}
diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h
index 12c813ab08..542d603a0a 100644
--- a/scene/3d/navigation_obstacle_3d.h
+++ b/scene/3d/navigation_obstacle_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 473368cf69..6404432631 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -73,6 +73,10 @@ uint32_t NavigationRegion3D::get_layers() const {
return NavigationServer3D::get_singleton()->region_get_layers(region);
}
+RID NavigationRegion3D::get_region_rid() const {
+ return region;
+}
+
/////////////////////////////
void NavigationRegion3D::_notification(int p_what) {
@@ -93,12 +97,12 @@ void NavigationRegion3D::_notification(int p_what) {
add_child(dm);
debug_view = dm;
}
-
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform());
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
NavigationServer3D::get_singleton()->region_set_map(region, RID());
@@ -161,13 +165,22 @@ void _bake_navigation_mesh(void *p_user_data) {
}
}
-void NavigationRegion3D::bake_navigation_mesh() {
+void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh.");
BakeThreadsArgs *args = memnew(BakeThreadsArgs);
args->nav_region = this;
- bake_thread.start(_bake_navigation_mesh, args);
+ if (p_on_thread && !OS::get_singleton()->can_use_threads()) {
+ WARN_PRINT("NavigationMesh bake 'on_thread' will be disabled as the current OS does not support multiple threads."
+ "\nAs a fallback the navigation mesh will bake on the main thread which can cause framerate issues.");
+ }
+
+ if (p_on_thread && OS::get_singleton()->can_use_threads()) {
+ bake_thread.start(_bake_navigation_mesh, args);
+ } else {
+ _bake_navigation_mesh(args);
+ }
}
void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
@@ -181,7 +194,7 @@ TypedArray<String> NavigationRegion3D::get_configuration_warnings() const {
if (is_visible_in_tree() && is_inside_tree()) {
if (!navmesh.is_valid()) {
- warnings.push_back(TTR("A NavigationMesh resource must be set or created for this node to work."));
+ warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work."));
}
}
@@ -198,7 +211,9 @@ void NavigationRegion3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layers", "layers"), &NavigationRegion3D::set_layers);
ClassDB::bind_method(D_METHOD("get_layers"), &NavigationRegion3D::get_layers);
- ClassDB::bind_method(D_METHOD("bake_navigation_mesh"), &NavigationRegion3D::bake_navigation_mesh);
+ ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion3D::get_region_rid);
+
+ ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationRegion3D::bake_navigation_mesh, DEFVAL(true));
ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationRegion3D::_bake_finished);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h
index ec7761ef93..140dfebf6a 100644
--- a/scene/3d/navigation_region_3d.h
+++ b/scene/3d/navigation_region_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,12 +57,14 @@ public:
void set_layers(uint32_t p_layers);
uint32_t get_layers() const;
+ RID get_region_rid() const;
+
void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh);
Ref<NavigationMesh> get_navigation_mesh() const;
- /// Bakes the navigation mesh in a dedicated thread; once done, automatically
+ /// Bakes the navigation mesh; once done, automatically
/// sets the new navigation mesh and emits a signal
- void bake_navigation_mesh();
+ void bake_navigation_mesh(bool p_on_thread);
void _bake_finished(Ref<NavigationMesh> p_nav_mesh);
TypedArray<String> get_configuration_warnings() const override;
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index b7b88c7135..bbc977647e 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,7 @@
#include "core/object/message_queue.h"
#include "scene/3d/visual_instance_3d.h"
#include "scene/main/viewport.h"
+#include "scene/property_utils.h"
#include "scene/scene_string_names.h"
/*
@@ -84,6 +85,9 @@ void Node3D::_notify_dirty() {
}
void Node3D::_update_local_transform() const {
+ if (this->get_rotation_edit_mode() != ROTATION_EDIT_MODE_BASIS) {
+ data.local_transform = data.local_transform.orthogonalized();
+ }
data.local_transform.basis.set_euler_scale(data.rotation, data.scale);
data.dirty &= ~DIRTY_LOCAL;
@@ -143,8 +147,8 @@ void Node3D::_notification(int p_what) {
notification(NOTIFICATION_ENTER_WORLD);
_update_visibility_parent(true);
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
notification(NOTIFICATION_EXIT_WORLD, true);
if (xform_change.in_list()) {
@@ -158,6 +162,7 @@ void Node3D::_notification(int p_what) {
data.top_level_active = false;
_update_visibility_parent(true);
} break;
+
case NOTIFICATION_ENTER_WORLD: {
data.inside_world = true;
data.viewport = nullptr;
@@ -172,22 +177,14 @@ void Node3D::_notification(int p_what) {
if (get_script_instance()) {
get_script_instance()->call(SceneStringNames::get_singleton()->_enter_world);
}
+
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) {
get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this);
- if (!data.gizmos_disabled) {
- for (int i = 0; i < data.gizmos.size(); i++) {
- data.gizmos.write[i]->create();
- if (is_visible_in_tree()) {
- data.gizmos.write[i]->redraw();
- }
- data.gizmos.write[i]->transform();
- }
- }
}
#endif
-
} break;
+
case NOTIFICATION_EXIT_WORLD: {
#ifdef TOOLS_ENABLED
clear_gizmos();
@@ -199,7 +196,6 @@ void Node3D::_notification(int p_what) {
data.viewport = nullptr;
data.inside_world = false;
-
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -209,9 +205,6 @@ void Node3D::_notification(int p_what) {
}
#endif
} break;
-
- default: {
- }
}
}
@@ -296,8 +289,9 @@ Node3D *Node3D::get_parent_node_3d() const {
}
Transform3D Node3D::get_relative_transform(const Node *p_parent) const {
- if (p_parent == this)
+ if (p_parent == this) {
return Transform3D();
+ }
ERR_FAIL_COND_V(!data.parent, Transform3D());
@@ -321,6 +315,14 @@ void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) {
return;
}
data.rotation_edit_mode = p_mode;
+
+ // Shearing is not allowed except in ROTATION_EDIT_MODE_BASIS.
+ data.dirty |= DIRTY_LOCAL;
+ _propagate_transform_changed(this);
+ if (data.notify_local_transform) {
+ notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
+ }
+
notify_property_list_changed();
}
@@ -416,6 +418,7 @@ void Node3D::update_gizmos() {
}
if (data.gizmos.is_empty()) {
+ get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this);
return;
}
if (data.gizmos_dirty) {
@@ -456,7 +459,6 @@ void Node3D::clear_subgizmo_selection() {
void Node3D::add_gizmo(Ref<Node3DGizmo> p_gizmo) {
#ifdef TOOLS_ENABLED
-
if (data.gizmos_disabled || p_gizmo.is_null()) {
return;
}
@@ -474,11 +476,10 @@ void Node3D::add_gizmo(Ref<Node3DGizmo> p_gizmo) {
void Node3D::remove_gizmo(Ref<Node3DGizmo> p_gizmo) {
#ifdef TOOLS_ENABLED
-
int idx = data.gizmos.find(p_gizmo);
if (idx != -1) {
p_gizmo->free();
- data.gizmos.remove(idx);
+ data.gizmos.remove_at(idx);
}
#endif
}
@@ -506,10 +507,8 @@ Array Node3D::get_gizmos_bind() const {
Vector<Ref<Node3DGizmo>> Node3D::get_gizmos() const {
#ifdef TOOLS_ENABLED
-
return data.gizmos;
#else
-
return Vector<Ref<Node3DGizmo>>();
#endif
}
@@ -561,7 +560,6 @@ void Node3D::set_as_top_level(bool p_enabled) {
data.top_level = p_enabled;
data.top_level_active = p_enabled;
-
} else {
data.top_level = p_enabled;
}
@@ -581,6 +579,7 @@ Ref<World3D> Node3D::get_world_3d() const {
void Node3D::_propagate_visibility_changed() {
notification(NOTIFICATION_VISIBILITY_CHANGED);
emit_signal(SceneStringNames::get_singleton()->visibility_changed);
+
#ifdef TOOLS_ENABLED
if (!data.gizmos.is_empty()) {
data.gizmos_dirty = true;
@@ -597,33 +596,30 @@ void Node3D::_propagate_visibility_changed() {
}
void Node3D::show() {
- if (data.visible) {
- return;
- }
-
- data.visible = true;
-
- if (!is_inside_tree()) {
- return;
- }
-
- _propagate_visibility_changed();
+ set_visible(true);
}
void Node3D::hide() {
- if (!data.visible) {
+ set_visible(false);
+}
+
+void Node3D::set_visible(bool p_visible) {
+ if (data.visible == p_visible) {
return;
}
- data.visible = false;
+ data.visible = p_visible;
if (!is_inside_tree()) {
return;
}
-
_propagate_visibility_changed();
}
+bool Node3D::is_visible() const {
+ return data.visible;
+}
+
bool Node3D::is_visible_in_tree() const {
const Node3D *s = this;
@@ -637,18 +633,6 @@ bool Node3D::is_visible_in_tree() const {
return true;
}
-void Node3D::set_visible(bool p_visible) {
- if (p_visible) {
- show();
- } else {
- hide();
- }
-}
-
-bool Node3D::is_visible() const {
- return data.visible;
-}
-
void Node3D::rotate_object_local(const Vector3 &p_axis, real_t p_angle) {
Transform3D t = get_transform();
t.basis.rotate_local(p_axis, p_angle);
@@ -757,16 +741,16 @@ Vector3 Node3D::to_global(Vector3 p_local) const {
return get_global_transform().xform(p_local);
}
-void Node3D::set_notify_transform(bool p_enable) {
- data.notify_transform = p_enable;
+void Node3D::set_notify_transform(bool p_enabled) {
+ data.notify_transform = p_enabled;
}
bool Node3D::is_transform_notification_enabled() const {
return data.notify_transform;
}
-void Node3D::set_notify_local_transform(bool p_enable) {
- data.notify_local_transform = p_enable;
+void Node3D::set_notify_local_transform(bool p_enabled) {
+ data.notify_local_transform = p_enabled;
}
bool Node3D::is_local_transform_notification_enabled() const {
@@ -845,6 +829,64 @@ void Node3D::_validate_property(PropertyInfo &property) const {
}
}
+bool Node3D::property_can_revert(const String &p_name) {
+ if (p_name == "basis") {
+ return true;
+ } else if (p_name == "scale") {
+ return true;
+ } else if (p_name == "quaternion") {
+ return true;
+ } else if (p_name == "rotation") {
+ return true;
+ } else if (p_name == "position") {
+ return true;
+ }
+ return false;
+}
+
+Variant Node3D::property_get_revert(const String &p_name) {
+ Variant r_ret;
+ bool valid = false;
+
+ if (p_name == "basis") {
+ Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
+ if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
+ r_ret = Transform3D(variant).get_basis();
+ } else {
+ r_ret = Basis();
+ }
+ } else if (p_name == "scale") {
+ Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
+ if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
+ r_ret = Transform3D(variant).get_basis().get_scale();
+ } else {
+ return Vector3(1.0, 1.0, 1.0);
+ }
+ } else if (p_name == "quaternion") {
+ Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
+ if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
+ r_ret = Quaternion(Transform3D(variant).get_basis());
+ } else {
+ return Quaternion();
+ }
+ } else if (p_name == "rotation") {
+ Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
+ if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
+ r_ret = Transform3D(variant).get_basis().get_euler_normalized(data.rotation_order);
+ } else {
+ return Vector3();
+ }
+ } else if (p_name == "position") {
+ Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
+ if (valid) {
+ r_ret = Transform3D(variant).get_origin();
+ } else {
+ return Vector3();
+ }
+ }
+ return r_ret;
+}
+
void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform);
ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform);
@@ -916,6 +958,9 @@ void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("to_local", "global_point"), &Node3D::to_local);
ClassDB::bind_method(D_METHOD("to_global", "local_point"), &Node3D::to_global);
+ ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &Node3D::property_can_revert);
+ ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &Node3D::property_get_revert);
+
BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED);
BIND_CONSTANT(NOTIFICATION_ENTER_WORLD);
BIND_CONSTANT(NOTIFICATION_EXIT_WORLD);
@@ -934,8 +979,9 @@ void Node3D::_bind_methods() {
//ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM3D,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ;
ADD_GROUP("Transform", "");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "quaternion", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion");
ADD_PROPERTY(PropertyInfo(Variant::BASIS, "basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_basis", "get_basis");
@@ -943,7 +989,6 @@ void Node3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_edit_mode", PROPERTY_HINT_ENUM, "Euler,Quaternion,Basis"), "set_rotation_edit_mode", "get_rotation_edit_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_order", PROPERTY_HINT_ENUM, "XYZ,XZY,YXZ,YZX,ZXY,ZYX"), "set_rotation_order", "get_rotation_order");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
- ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform");
ADD_GROUP("Visibility", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent");
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 3e21eb12be..6d857a83ea 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,6 +32,7 @@
#define NODE_3D_H
#include "scene/main/node.h"
+#include "scene/resources/world_3d.h"
class Node3DGizmo : public RefCounted {
GDCLASS(Node3DGizmo, RefCounted);
@@ -49,7 +50,6 @@ public:
class Node3D : public Node {
GDCLASS(Node3D, Node);
- OBJ_CATEGORY("3D");
public:
enum RotationEditMode {
@@ -137,6 +137,9 @@ protected:
virtual void _validate_property(PropertyInfo &property) const override;
+ bool property_can_revert(const String &p_name);
+ Variant property_get_revert(const String &p_name);
+
public:
enum {
NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED,
@@ -182,12 +185,6 @@ public:
virtual bool is_transform_gizmo_visible() const { return data.transform_gizmo_visible; };
#endif
- void set_as_top_level(bool p_enabled);
- bool is_set_as_top_level() const;
-
- void set_disable_scale(bool p_enabled);
- bool is_scale_disabled() const;
-
void set_disable_gizmos(bool p_enabled);
void update_gizmos();
void set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D());
@@ -198,6 +195,12 @@ public:
void remove_gizmo(Ref<Node3DGizmo> p_gizmo);
void clear_gizmos();
+ void set_as_top_level(bool p_enabled);
+ bool is_set_as_top_level() const;
+
+ void set_disable_scale(bool p_enabled);
+ bool is_scale_disabled() const;
+
_FORCE_INLINE_ bool is_inside_world() const { return data.inside_world; }
Transform3D get_relative_transform(const Node *p_parent) const;
@@ -223,19 +226,19 @@ public:
Vector3 to_local(Vector3 p_global) const;
Vector3 to_global(Vector3 p_local) const;
- void set_notify_transform(bool p_enable);
+ void set_notify_transform(bool p_enabled);
bool is_transform_notification_enabled() const;
- void set_notify_local_transform(bool p_enable);
+ void set_notify_local_transform(bool p_enabled);
bool is_local_transform_notification_enabled() const;
void orthonormalize();
void set_identity();
void set_visible(bool p_visible);
- bool is_visible() const;
void show();
void hide();
+ bool is_visible() const;
bool is_visible_in_tree() const;
void force_update_transform();
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index aeac430cd9..f848eaab2e 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,42 +29,27 @@
/*************************************************************************/
#include "occluder_instance_3d.h"
+
+#include "core/config/project_settings.h"
#include "core/core_string_names.h"
+#include "core/math/geometry_2d.h"
+#include "core/math/triangulate.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/mesh_instance_3d.h"
+#include "scene/resources/importer_mesh.h"
+#include "scene/resources/surface_tool.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
+#endif
RID Occluder3D::get_rid() const {
- if (!occluder.is_valid()) {
- occluder = RS::get_singleton()->occluder_create();
- RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
- }
return occluder;
}
-void Occluder3D::set_vertices(PackedVector3Array p_vertices) {
- vertices = p_vertices;
- if (occluder.is_valid()) {
- RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
- }
- _update_changes();
-}
+void Occluder3D::_update() {
+ _update_arrays(vertices, indices);
-PackedVector3Array Occluder3D::get_vertices() const {
- return vertices;
-}
-
-void Occluder3D::set_indices(PackedInt32Array p_indices) {
- indices = p_indices;
- if (occluder.is_valid()) {
- RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
- }
- _update_changes();
-}
-
-PackedInt32Array Occluder3D::get_indices() const {
- return indices;
-}
-
-void Occluder3D::_update_changes() {
aabb = AABB();
const Vector3 *ptr = vertices.ptr();
@@ -75,9 +60,18 @@ void Occluder3D::_update_changes() {
debug_lines.clear();
debug_mesh.unref();
+ RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
emit_changed();
}
+PackedVector3Array Occluder3D::get_vertices() const {
+ return vertices;
+}
+
+PackedInt32Array Occluder3D::get_indices() const {
+ return indices;
+}
+
Vector<Vector3> Occluder3D::get_debug_lines() const {
if (!debug_lines.is_empty()) {
return debug_lines;
@@ -87,14 +81,18 @@ Vector<Vector3> Occluder3D::get_debug_lines() const {
return Vector<Vector3>();
}
+ const Vector3 *vertices_ptr = vertices.ptr();
+ debug_lines.resize(indices.size() / 3 * 6);
+ Vector3 *line_ptr = debug_lines.ptrw();
+ int line_i = 0;
for (int i = 0; i < indices.size() / 3; i++) {
for (int j = 0; j < 3; j++) {
int a = indices[i * 3 + j];
int b = indices[i * 3 + (j + 1) % 3];
ERR_FAIL_INDEX_V_MSG(a, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range.");
ERR_FAIL_INDEX_V_MSG(b, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range.");
- debug_lines.push_back(vertices[a]);
- debug_lines.push_back(vertices[b]);
+ line_ptr[line_i++] = vertices_ptr[a];
+ line_ptr[line_i++] = vertices_ptr[b];
}
}
return debug_lines;
@@ -105,7 +103,7 @@ Ref<ArrayMesh> Occluder3D::get_debug_mesh() const {
return debug_mesh;
}
- if (indices.size() % 3 != 0) {
+ if (vertices.is_empty() || indices.is_empty() || indices.size() % 3 != 0) {
return debug_mesh;
}
@@ -123,18 +121,19 @@ AABB Occluder3D::get_aabb() const {
return aabb;
}
-void Occluder3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &Occluder3D::set_vertices);
- ClassDB::bind_method(D_METHOD("get_vertices"), &Occluder3D::get_vertices);
-
- ClassDB::bind_method(D_METHOD("set_indices", "indices"), &Occluder3D::set_indices);
- ClassDB::bind_method(D_METHOD("get_indices"), &Occluder3D::get_indices);
+void Occluder3D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_POSTINITIALIZE: {
+ _update();
+ } break;
+ }
+}
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_vertices", "get_vertices");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_indices", "get_indices");
+void Occluder3D::_bind_methods() {
}
Occluder3D::Occluder3D() {
+ occluder = RS::get_singleton()->occluder_create();
}
Occluder3D::~Occluder3D() {
@@ -142,6 +141,289 @@ Occluder3D::~Occluder3D() {
RS::get_singleton()->free(occluder);
}
}
+
+/////////////////////////////////////////////////
+
+void ArrayOccluder3D::set_arrays(PackedVector3Array p_vertices, PackedInt32Array p_indices) {
+ vertices = p_vertices;
+ indices = p_indices;
+ _update();
+}
+
+void ArrayOccluder3D::set_vertices(PackedVector3Array p_vertices) {
+ vertices = p_vertices;
+ _update();
+}
+
+void ArrayOccluder3D::set_indices(PackedInt32Array p_indices) {
+ indices = p_indices;
+ _update();
+}
+
+void ArrayOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ r_vertices = vertices;
+ r_indices = indices;
+}
+
+void ArrayOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_arrays", "vertices", "indices"), &ArrayOccluder3D::set_arrays);
+
+ ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &ArrayOccluder3D::set_vertices);
+ ClassDB::bind_method(D_METHOD("get_vertices"), &ArrayOccluder3D::get_vertices);
+
+ ClassDB::bind_method(D_METHOD("set_indices", "indices"), &ArrayOccluder3D::set_indices);
+ ClassDB::bind_method(D_METHOD("get_indices"), &ArrayOccluder3D::get_indices);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_indices", "get_indices");
+}
+
+ArrayOccluder3D::ArrayOccluder3D() {
+}
+
+ArrayOccluder3D::~ArrayOccluder3D() {
+}
+
+/////////////////////////////////////////////////
+
+void QuadOccluder3D::set_size(const Vector2 &p_size) {
+ if (size == p_size) {
+ return;
+ }
+
+ size = p_size.max(Vector2());
+ _update();
+}
+
+Vector2 QuadOccluder3D::get_size() const {
+ return size;
+}
+
+void QuadOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f);
+
+ r_vertices = {
+ Vector3(-_size.x, -_size.y, 0),
+ Vector3(-_size.x, _size.y, 0),
+ Vector3(_size.x, _size.y, 0),
+ Vector3(_size.x, -_size.y, 0),
+ };
+
+ r_indices = {
+ 0, 1, 2,
+ 0, 2, 3
+ };
+}
+
+void QuadOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadOccluder3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &QuadOccluder3D::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
+}
+
+QuadOccluder3D::QuadOccluder3D() {
+}
+
+QuadOccluder3D::~QuadOccluder3D() {
+}
+
+/////////////////////////////////////////////////
+
+void BoxOccluder3D::set_size(const Vector3 &p_size) {
+ if (size == p_size) {
+ return;
+ }
+
+ size = Vector3(MAX(p_size.x, 0.0f), MAX(p_size.y, 0.0f), MAX(p_size.z, 0.0f));
+ _update();
+}
+
+Vector3 BoxOccluder3D::get_size() const {
+ return size;
+}
+
+void BoxOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ Vector3 _size = Vector3(size.x / 2.0f, size.y / 2.0f, size.z / 2.0f);
+
+ r_vertices = {
+ // front
+ Vector3(-_size.x, -_size.y, _size.z),
+ Vector3(_size.x, -_size.y, _size.z),
+ Vector3(_size.x, _size.y, _size.z),
+ Vector3(-_size.x, _size.y, _size.z),
+ // back
+ Vector3(-_size.x, -_size.y, -_size.z),
+ Vector3(_size.x, -_size.y, -_size.z),
+ Vector3(_size.x, _size.y, -_size.z),
+ Vector3(-_size.x, _size.y, -_size.z),
+ };
+
+ r_indices = {
+ // front
+ 0, 1, 2,
+ 2, 3, 0,
+ // right
+ 1, 5, 6,
+ 6, 2, 1,
+ // back
+ 7, 6, 5,
+ 5, 4, 7,
+ // left
+ 4, 0, 3,
+ 3, 7, 4,
+ // bottom
+ 4, 5, 1,
+ 1, 0, 4,
+ // top
+ 3, 2, 6,
+ 6, 7, 3
+ };
+}
+
+void BoxOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxOccluder3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &BoxOccluder3D::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
+}
+
+BoxOccluder3D::BoxOccluder3D() {
+}
+
+BoxOccluder3D::~BoxOccluder3D() {
+}
+
+/////////////////////////////////////////////////
+
+void SphereOccluder3D::set_radius(float p_radius) {
+ if (radius == p_radius) {
+ return;
+ }
+
+ radius = MAX(p_radius, 0.0f);
+ _update();
+}
+
+float SphereOccluder3D::get_radius() const {
+ return radius;
+}
+
+void SphereOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ r_vertices.resize((RINGS + 2) * (RADIAL_SEGMENTS + 1));
+ int vertex_i = 0;
+ Vector3 *vertex_ptr = r_vertices.ptrw();
+
+ r_indices.resize((RINGS + 1) * RADIAL_SEGMENTS * 6);
+ int idx_i = 0;
+ int *idx_ptr = r_indices.ptrw();
+
+ int current_row = 0;
+ int previous_row = 0;
+ int point = 0;
+ for (int j = 0; j <= (RINGS + 1); j++) {
+ float v = j / float(RINGS + 1);
+ float w = Math::sin(Math_PI * v);
+ float y = Math::cos(Math_PI * v);
+ for (int i = 0; i <= RADIAL_SEGMENTS; i++) {
+ float u = i / float(RADIAL_SEGMENTS);
+
+ float x = Math::cos(u * Math_TAU);
+ float z = Math::sin(u * Math_TAU);
+ vertex_ptr[vertex_i++] = Vector3(x * w, y, z * w) * radius;
+
+ if (i > 0 && j > 0) {
+ idx_ptr[idx_i++] = previous_row + i - 1;
+ idx_ptr[idx_i++] = previous_row + i;
+ idx_ptr[idx_i++] = current_row + i - 1;
+
+ idx_ptr[idx_i++] = previous_row + i;
+ idx_ptr[idx_i++] = current_row + i;
+ idx_ptr[idx_i++] = current_row + i - 1;
+ }
+
+ point++;
+ }
+
+ previous_row = current_row;
+ current_row = point;
+ }
+}
+
+void SphereOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereOccluder3D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &SphereOccluder3D::get_radius);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius"), "set_radius", "get_radius");
+}
+
+SphereOccluder3D::SphereOccluder3D() {
+}
+
+SphereOccluder3D::~SphereOccluder3D() {
+}
+
+/////////////////////////////////////////////////
+
+void PolygonOccluder3D::set_polygon(const Vector<Vector2> &p_polygon) {
+ polygon = p_polygon;
+ _update();
+}
+
+Vector<Vector2> PolygonOccluder3D::get_polygon() const {
+ return polygon;
+}
+
+void PolygonOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ if (polygon.size() < 3) {
+ r_vertices.clear();
+ r_indices.clear();
+ return;
+ }
+
+ Vector<Point2> occluder_polygon = polygon;
+ if (Triangulate::get_area(occluder_polygon) > 0) {
+ occluder_polygon.reverse();
+ }
+
+ Vector<int> occluder_indices = Geometry2D::triangulate_polygon(occluder_polygon);
+
+ if (occluder_indices.size() < 3) {
+ r_vertices.clear();
+ r_indices.clear();
+ ERR_FAIL_MSG("Failed to triangulate PolygonOccluder3D. Make sure the polygon doesn't have any intersecting edges.");
+ }
+
+ r_vertices.resize(occluder_polygon.size());
+ Vector3 *vertex_ptr = r_vertices.ptrw();
+ const Vector2 *polygon_ptr = occluder_polygon.ptr();
+ for (int i = 0; i < occluder_polygon.size(); i++) {
+ vertex_ptr[i] = Vector3(polygon_ptr[i].x, polygon_ptr[i].y, 0.0);
+ }
+
+ r_indices.resize(occluder_indices.size());
+ memcpy(r_indices.ptrw(), occluder_indices.ptr(), occluder_indices.size() * sizeof(int));
+}
+
+bool PolygonOccluder3D::_has_editable_3d_polygon_no_depth() const {
+ return false;
+}
+
+void PolygonOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &PolygonOccluder3D::set_polygon);
+ ClassDB::bind_method(D_METHOD("get_polygon"), &PolygonOccluder3D::get_polygon);
+
+ ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &PolygonOccluder3D::_has_editable_3d_polygon_no_depth);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
+}
+
+PolygonOccluder3D::PolygonOccluder3D() {
+}
+
+PolygonOccluder3D::~PolygonOccluder3D() {
+}
+
/////////////////////////////////////////////////
AABB OccluderInstance3D::get_aabb() const {
@@ -151,10 +433,6 @@ AABB OccluderInstance3D::get_aabb() const {
return AABB();
}
-Vector<Face3> OccluderInstance3D::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) {
if (occluder == p_occluder) {
return;
@@ -175,6 +453,13 @@ void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) {
update_gizmos();
update_configuration_warnings();
+
+#ifdef TOOLS_ENABLED
+ // PolygonOccluder3D is edited via an editor plugin, this ensures the plugin is shown/hidden when necessary
+ if (Engine::get_singleton()->is_editor_hint()) {
+ EditorNode::get_singleton()->call_deferred(SNAME("edit_current"));
+ }
+#endif
}
void OccluderInstance3D::_occluder_changed() {
@@ -195,6 +480,14 @@ uint32_t OccluderInstance3D::get_bake_mask() const {
return bake_mask;
}
+void OccluderInstance3D::set_bake_simplification_distance(float p_dist) {
+ bake_simplification_dist = MAX(p_dist, 0.0f);
+}
+
+float OccluderInstance3D::get_bake_simplification_distance() const {
+ return bake_simplification_dist;
+}
+
void OccluderInstance3D::set_bake_mask_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive.");
@@ -221,6 +514,47 @@ bool OccluderInstance3D::_bake_material_check(Ref<Material> p_material) {
return true;
}
+void OccluderInstance3D::_bake_surface(const Transform3D &p_transform, Array p_surface_arrays, Ref<Material> p_material, float p_simplification_dist, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ if (!_bake_material_check(p_material)) {
+ return;
+ }
+ ERR_FAIL_COND_MSG(p_surface_arrays.size() != Mesh::ARRAY_MAX, "Invalid surface array.");
+
+ PackedVector3Array vertices = p_surface_arrays[Mesh::ARRAY_VERTEX];
+ PackedInt32Array indices = p_surface_arrays[Mesh::ARRAY_INDEX];
+
+ if (vertices.size() == 0 || indices.size() == 0) {
+ return;
+ }
+
+ Vector3 *vertices_ptr = vertices.ptrw();
+ for (int j = 0; j < vertices.size(); j++) {
+ vertices_ptr[j] = p_transform.xform(vertices_ptr[j]);
+ }
+
+ if (!Math::is_zero_approx(p_simplification_dist) && SurfaceTool::simplify_func) {
+ float error_scale = SurfaceTool::simplify_scale_func((float *)vertices.ptr(), vertices.size(), sizeof(Vector3));
+ float target_error = p_simplification_dist / error_scale;
+ float error = -1.0f;
+ int target_index_count = MIN(indices.size(), 36);
+ uint32_t index_count = SurfaceTool::simplify_func((unsigned int *)indices.ptrw(), (unsigned int *)indices.ptr(), indices.size(), (float *)vertices.ptr(), vertices.size(), sizeof(Vector3), target_index_count, target_error, &error);
+ indices.resize(index_count);
+ }
+
+ SurfaceTool::strip_mesh_arrays(vertices, indices);
+
+ int vertex_offset = r_vertices.size();
+ r_vertices.resize(vertex_offset + vertices.size());
+ memcpy(r_vertices.ptrw() + vertex_offset, vertices.ptr(), vertices.size() * sizeof(Vector3));
+
+ int index_offset = r_indices.size();
+ r_indices.resize(index_offset + indices.size());
+ int *idx_ptr = r_indices.ptrw();
+ for (int j = 0; j < indices.size(); j++) {
+ idx_ptr[index_offset + j] = vertex_offset + indices[j];
+ }
+}
+
void OccluderInstance3D::_bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
if (mi && mi->is_visible_in_tree()) {
@@ -243,58 +577,76 @@ void OccluderInstance3D::_bake_node(Node *p_node, PackedVector3Array &r_vertices
Transform3D global_to_local = get_global_transform().affine_inverse() * mi->get_global_transform();
for (int i = 0; i < mesh->get_surface_count(); i++) {
- if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
- continue;
- }
+ _bake_surface(global_to_local, mesh->surface_get_arrays(i), mi->get_active_material(i), bake_simplification_dist, r_vertices, r_indices);
+ }
+ }
+ }
- if (mi->get_surface_override_material(i).is_valid()) {
- if (!_bake_material_check(mi->get_surface_override_material(i))) {
- continue;
- }
- } else {
- if (!_bake_material_check(mesh->surface_get_material(i))) {
- continue;
- }
- }
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ Node *child = p_node->get_child(i);
+ if (!child->get_owner()) {
+ continue; // may be a helper
+ }
- Array arrays = mesh->surface_get_arrays(i);
+ _bake_node(child, r_vertices, r_indices);
+ }
+}
- int vertex_offset = r_vertices.size();
- PackedVector3Array vertices = arrays[Mesh::ARRAY_VERTEX];
- r_vertices.resize(r_vertices.size() + vertices.size());
+void OccluderInstance3D::bake_single_node(const Node3D *p_node, float p_simplification_distance, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ ERR_FAIL_COND(!p_node);
- Vector3 *vtx_ptr = r_vertices.ptrw();
- for (int j = 0; j < vertices.size(); j++) {
- vtx_ptr[vertex_offset + j] = global_to_local.xform(vertices[j]);
- }
+ Transform3D xform = p_node->is_inside_tree() ? p_node->get_global_transform() : p_node->get_transform();
- int index_offset = r_indices.size();
- PackedInt32Array indices = arrays[Mesh::ARRAY_INDEX];
- r_indices.resize(r_indices.size() + indices.size());
+ const MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
+ if (mi) {
+ Ref<Mesh> mesh = mi->get_mesh();
+ bool valid = true;
- int *idx_ptr = r_indices.ptrw();
- for (int j = 0; j < indices.size(); j++) {
- idx_ptr[index_offset + j] = vertex_offset + indices[j];
- }
+ if (mesh.is_null()) {
+ valid = false;
+ }
+
+ if (valid && !_bake_material_check(mi->get_material_override())) {
+ valid = false;
+ }
+
+ if (valid) {
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ _bake_surface(xform, mesh->surface_get_arrays(i), mi->get_active_material(i), p_simplification_distance, r_vertices, r_indices);
}
}
}
- for (int i = 0; i < p_node->get_child_count(); i++) {
- Node *child = p_node->get_child(i);
- if (!child->get_owner()) {
- continue; //maybe a helper
+ const ImporterMeshInstance3D *imi = Object::cast_to<ImporterMeshInstance3D>(p_node);
+ if (imi) {
+ Ref<ImporterMesh> mesh = imi->get_mesh();
+ bool valid = true;
+
+ if (mesh.is_null()) {
+ valid = false;
}
- _bake_node(child, r_vertices, r_indices);
+ if (valid) {
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ Ref<Material> material = imi->get_surface_material(i);
+ if (material.is_null()) {
+ material = mesh->get_surface_material(i);
+ }
+ _bake_surface(xform, mesh->get_surface_arrays(i), material, p_simplification_distance, r_vertices, r_indices);
+ }
+ }
}
}
-OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String p_occluder_path) {
- if (p_occluder_path == "") {
+OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node, String p_occluder_path) {
+ if (p_occluder_path.is_empty()) {
if (get_occluder().is_null()) {
return BAKE_ERROR_NO_SAVE_PATH;
}
+ p_occluder_path = get_occluder()->get_path();
+ if (!p_occluder_path.is_resource_file()) {
+ return BAKE_ERROR_NO_SAVE_PATH;
+ }
}
PackedVector3Array vertices;
@@ -306,16 +658,25 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String
return BAKE_ERROR_NO_MESHES;
}
- Ref<Occluder3D> occ;
+ Ref<ArrayOccluder3D> occ;
if (get_occluder().is_valid()) {
occ = get_occluder();
- } else {
+ set_occluder(Ref<Occluder3D>()); // clear
+ }
+
+ if (occ.is_null()) {
occ.instantiate();
- occ->set_path(p_occluder_path);
}
- occ->set_vertices(vertices);
- occ->set_indices(indices);
+ occ->set_arrays(vertices, indices);
+
+ Error err = ResourceSaver::save(p_occluder_path, occ);
+
+ if (err != OK) {
+ return BAKE_ERROR_CANT_SAVE;
+ }
+
+ occ->set_path(p_occluder_path);
set_occluder(occ);
return BAKE_ERROR_OK;
@@ -325,36 +686,57 @@ TypedArray<String> OccluderInstance3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) {
- warnings.push_back(TTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling."));
+ warnings.push_back(RTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling."));
}
if (bake_mask == 0) {
- warnings.push_back(TTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property."));
+ warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property."));
}
if (occluder.is_null()) {
- warnings.push_back(TTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
- } else if (occluder->get_vertices().size() < 3) {
- // Using the "New Occluder" dropdown button won't result in a correct occluder,
- // so warn the user about this.
- warnings.push_back(TTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
+ warnings.push_back(RTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, set the Occluder property to one of the primitive occluder types or bake the scene meshes by selecting the OccluderInstance3D and pressing the Bake Occluders button at the top of the 3D editor viewport."));
+ } else {
+ Ref<ArrayOccluder3D> arr_occluder = occluder;
+ if (arr_occluder.is_valid() && arr_occluder->get_indices().size() < 3) {
+ // Setting a new ArrayOccluder3D from the inspector will create an empty occluder,
+ // so warn the user about this.
+ warnings.push_back(RTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
+ }
+ Ref<PolygonOccluder3D> poly_occluder = occluder;
+ if (poly_occluder.is_valid() && poly_occluder->get_polygon().size() < 3) {
+ warnings.push_back(RTR("The polygon occluder has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nVertices can be added in the inspector or using the polygon editing tools at the top of the 3D editor viewport."));
+ }
}
return warnings;
}
+bool OccluderInstance3D::_is_editable_3d_polygon() const {
+ return Ref<PolygonOccluder3D>(occluder).is_valid();
+}
+
+Ref<Resource> OccluderInstance3D::_get_editable_3d_polygon_resource() const {
+ return occluder;
+}
+
void OccluderInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask);
ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask);
ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &OccluderInstance3D::set_bake_mask_value);
ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &OccluderInstance3D::get_bake_mask_value);
+ ClassDB::bind_method(D_METHOD("set_bake_simplification_distance", "simplification_distance"), &OccluderInstance3D::set_bake_simplification_distance);
+ ClassDB::bind_method(D_METHOD("get_bake_simplification_distance"), &OccluderInstance3D::get_bake_simplification_distance);
ClassDB::bind_method(D_METHOD("set_occluder", "occluder"), &OccluderInstance3D::set_occluder);
ClassDB::bind_method(D_METHOD("get_occluder"), &OccluderInstance3D::get_occluder);
+ ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &OccluderInstance3D::_is_editable_3d_polygon);
+ ClassDB::bind_method(D_METHOD("_get_editable_3d_polygon_resource"), &OccluderInstance3D::_get_editable_3d_polygon_resource);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "Occluder3D"), "set_occluder", "get_occluder");
ADD_GROUP("Bake", "bake_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_bake_simplification_distance", "get_bake_simplification_distance");
}
OccluderInstance3D::OccluderInstance3D() {
diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h
index 173614b80c..ed6610074e 100644
--- a/scene/3d/occluder_instance_3d.h
+++ b/scene/3d/occluder_instance_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,24 +37,23 @@ class Occluder3D : public Resource {
GDCLASS(Occluder3D, Resource);
RES_BASE_EXTENSION("occ");
- mutable RID occluder;
- mutable Ref<ArrayMesh> debug_mesh;
- mutable Vector<Vector3> debug_lines;
- AABB aabb;
-
+ RID occluder;
PackedVector3Array vertices;
PackedInt32Array indices;
+ AABB aabb;
- void _update_changes();
+ mutable Ref<ArrayMesh> debug_mesh;
+ mutable Vector<Vector3> debug_lines;
protected:
+ void _update();
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) = 0;
+
static void _bind_methods();
+ void _notification(int p_what);
public:
- void set_vertices(PackedVector3Array p_vertices);
PackedVector3Array get_vertices() const;
-
- void set_indices(PackedInt32Array p_indices);
PackedInt32Array get_indices() const;
Vector<Vector3> get_debug_lines() const;
@@ -63,7 +62,102 @@ public:
virtual RID get_rid() const override;
Occluder3D();
- ~Occluder3D();
+ virtual ~Occluder3D();
+};
+
+class ArrayOccluder3D : public Occluder3D {
+ GDCLASS(ArrayOccluder3D, Occluder3D);
+
+ PackedVector3Array vertices;
+ PackedInt32Array indices;
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ void set_arrays(PackedVector3Array p_vertices, PackedInt32Array p_indices);
+ void set_vertices(PackedVector3Array p_vertices);
+ void set_indices(PackedInt32Array p_indices);
+
+ ArrayOccluder3D();
+ ~ArrayOccluder3D();
+};
+
+class QuadOccluder3D : public Occluder3D {
+ GDCLASS(QuadOccluder3D, Occluder3D);
+
+private:
+ Vector2 size = Vector2(1.0f, 1.0f);
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ Vector2 get_size() const;
+ void set_size(const Vector2 &p_size);
+
+ QuadOccluder3D();
+ ~QuadOccluder3D();
+};
+
+class BoxOccluder3D : public Occluder3D {
+ GDCLASS(BoxOccluder3D, Occluder3D);
+
+private:
+ Vector3 size = Vector3(1.0f, 1.0f, 1.0f);
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ Vector3 get_size() const;
+ void set_size(const Vector3 &p_size);
+
+ BoxOccluder3D();
+ ~BoxOccluder3D();
+};
+
+class SphereOccluder3D : public Occluder3D {
+ GDCLASS(SphereOccluder3D, Occluder3D);
+
+private:
+ static constexpr int RINGS = 7;
+ static constexpr int RADIAL_SEGMENTS = 7;
+ float radius = 1.0f;
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ float get_radius() const;
+ void set_radius(float p_radius);
+
+ SphereOccluder3D();
+ ~SphereOccluder3D();
+};
+
+class PolygonOccluder3D : public Occluder3D {
+ GDCLASS(PolygonOccluder3D, Occluder3D);
+
+private:
+ Vector<Vector2> polygon;
+
+ bool _has_editable_3d_polygon_no_depth() const;
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ void set_polygon(const Vector<Vector2> &p_polygon);
+ Vector<Vector2> get_polygon() const;
+
+ PolygonOccluder3D();
+ ~PolygonOccluder3D();
};
class OccluderInstance3D : public VisualInstance3D {
@@ -72,12 +166,17 @@ class OccluderInstance3D : public VisualInstance3D {
private:
Ref<Occluder3D> occluder;
uint32_t bake_mask = 0xFFFFFFFF;
+ float bake_simplification_dist = 0.1f;
void _occluder_changed();
- bool _bake_material_check(Ref<Material> p_material);
+ static bool _bake_material_check(Ref<Material> p_material);
+ static void _bake_surface(const Transform3D &p_transform, Array p_surface_arrays, Ref<Material> p_material, float p_simplification_dist, PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
void _bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
+ bool _is_editable_3d_polygon() const;
+ Ref<Resource> _get_editable_3d_polygon_resource() const;
+
protected:
static void _bind_methods();
@@ -88,21 +187,25 @@ public:
BAKE_ERROR_OK,
BAKE_ERROR_NO_SAVE_PATH,
BAKE_ERROR_NO_MESHES,
+ BAKE_ERROR_CANT_SAVE,
};
void set_occluder(const Ref<Occluder3D> &p_occluder);
Ref<Occluder3D> get_occluder() const;
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
void set_bake_mask(uint32_t p_mask);
uint32_t get_bake_mask() const;
+ void set_bake_simplification_distance(float p_dist);
+ float get_bake_simplification_distance() const;
+
void set_bake_mask_value(int p_layer_number, bool p_enable);
bool get_bake_mask_value(int p_layer_number) const;
- BakeError bake(Node *p_from_node, String p_occluder_path = "");
+ BakeError bake_scene(Node *p_from_node, String p_occluder_path = "");
+ static void bake_single_node(const Node3D *p_node, float p_simplification_distance, PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
OccluderInstance3D();
~OccluderInstance3D();
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index a0eac76e39..17a68f38f5 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,6 @@
#include "path_3d.h"
-void Path3D::_notification(int p_what) {
-}
-
void Path3D::_curve_changed() {
if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) {
update_gizmos();
@@ -149,7 +146,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) {
Vector3 sideways = up.cross(forward).normalized();
up = forward.cross(sideways).normalized();
- t.basis.set(sideways, up, forward);
+ t.basis.set_columns(sideways, up, forward);
t.basis.scale_local(scale);
t.origin = pos + sideways * h_offset + up * v_offset;
@@ -223,8 +220,8 @@ void PathFollow3D::_notification(int p_what) {
_update_transform(false);
}
}
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
path = nullptr;
} break;
@@ -256,11 +253,11 @@ TypedArray<String> PathFollow3D::get_configuration_warnings() const {
if (is_visible_in_tree() && is_inside_tree()) {
if (!Object::cast_to<Path3D>(get_parent())) {
- warnings.push_back(TTR("PathFollow3D only works when set as a child of a Path3D node."));
+ warnings.push_back(RTR("PathFollow3D only works when set as a child of a Path3D node."));
} else {
Path3D *path = Object::cast_to<Path3D>(get_parent());
if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) {
- warnings.push_back(TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource."));
+ warnings.push_back(RTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource."));
}
}
}
@@ -313,7 +310,7 @@ void PathFollow3D::set_offset(real_t p_offset) {
if (path->get_curve().is_valid()) {
real_t path_length = path->get_curve()->get_baked_length();
- if (loop) {
+ if (loop && path_length) {
offset = Math::fposmod(offset, path_length);
if (!Math::is_zero_approx(p_offset) && Math::is_zero_approx(offset)) {
offset = path_length;
diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h
index 1ffe291100..e9ab557693 100644
--- a/scene/3d/path_3d.h
+++ b/scene/3d/path_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,7 +42,6 @@ class Path3D : public Node3D {
void _curve_changed();
protected:
- void _notification(int p_what);
static void _bind_methods();
public:
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 5cb7f431e3..94cd5400db 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,8 +34,8 @@
#include "scene/scene_string_names.h"
void PhysicsBody3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1));
- ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1));
ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock);
ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock);
@@ -91,15 +91,16 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) {
PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid());
}
-Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_linear_velocity, bool p_test_only, real_t p_margin, int p_max_collisions) {
- // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
- double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
-
- PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin);
+Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_distance, bool p_test_only, real_t p_margin, int p_max_collisions) {
+ PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_distance, p_margin);
parameters.max_collisions = p_max_collisions;
PhysicsServer3D::MotionResult result;
- if (move_and_collide(parameters, result, p_test_only)) {
+
+ bool collided = move_and_collide(parameters, result, p_test_only);
+
+ // Don't report collision when the whole motion is done.
+ if (collided && result.collision_safe_fraction < 1) {
// Create a new instance when the cached reference is invalid or still in use in script.
if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) {
motion_cache.instantiate();
@@ -170,7 +171,7 @@ bool PhysicsBody3D::move_and_collide(const PhysicsServer3D::MotionParameters &p_
return colliding;
}
-bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) {
+bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) {
ERR_FAIL_COND_V(!is_inside_tree(), false);
PhysicsServer3D::MotionResult *r = nullptr;
@@ -182,10 +183,7 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear
r = &temp_result;
}
- // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
- double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
-
- PhysicsServer3D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
+ PhysicsServer3D::MotionParameters parameters(p_from, p_distance, p_margin);
bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r);
@@ -392,18 +390,18 @@ void RigidDynamicBody3D::_body_enter_tree(ObjectID p_id) {
ERR_FAIL_COND(!node);
ERR_FAIL_COND(!contact_monitor);
- Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id);
+ HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(E->get().in_tree);
+ ERR_FAIL_COND(E->value.in_tree);
- E->get().in_tree = true;
+ E->value.in_tree = true;
contact_monitor->locked = true;
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape);
}
contact_monitor->locked = false;
@@ -414,17 +412,17 @@ void RigidDynamicBody3D::_body_exit_tree(ObjectID p_id) {
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!node);
ERR_FAIL_COND(!contact_monitor);
- Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id);
+ HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id);
ERR_FAIL_COND(!E);
- ERR_FAIL_COND(!E->get().in_tree);
- E->get().in_tree = false;
+ ERR_FAIL_COND(!E->value.in_tree);
+ E->value.in_tree = false;
contact_monitor->locked = true;
emit_signal(SceneStringNames::get_singleton()->body_exited, node);
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape);
+ for (int i = 0; i < E->value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape);
}
contact_monitor->locked = false;
@@ -438,43 +436,43 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p
Node *node = Object::cast_to<Node>(obj);
ERR_FAIL_COND(!contact_monitor);
- Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid);
+ HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(objid);
ERR_FAIL_COND(!body_in && !E);
if (body_in) {
if (!E) {
E = contact_monitor->body_map.insert(objid, BodyState());
- E->get().rid = p_body;
- //E->get().rc=0;
- E->get().in_tree = node && node->is_inside_tree();
+ E->value.rid = p_body;
+ //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));
- if (E->get().in_tree) {
+ if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_entered, node);
}
}
}
- //E->get().rc++;
+ //E->value.rc++;
if (node) {
- E->get().shapes.insert(ShapePair(p_body_shape, p_local_shape));
+ E->value.shapes.insert(ShapePair(p_body_shape, p_local_shape));
}
- if (E->get().in_tree) {
+ if (E->value.in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_local_shape);
}
} else {
- //E->get().rc--;
+ //E->value.rc--;
if (node) {
- E->get().shapes.erase(ShapePair(p_body_shape, p_local_shape));
+ E->value.shapes.erase(ShapePair(p_body_shape, p_local_shape));
}
- bool in_tree = E->get().in_tree;
+ bool in_tree = E->value.in_tree;
- if (E->get().shapes.is_empty()) {
+ if (E->value.shapes.is_empty()) {
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree));
@@ -483,7 +481,7 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p
}
}
- contact_monitor->body_map.erase(E);
+ contact_monitor->body_map.remove(E);
}
if (node && in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, obj, p_body_shape, p_local_shape);
@@ -549,7 +547,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state)
//bool found=false;
- Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj);
+ HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(obj);
if (!E) {
toadd[toadd_count].rid = rid;
toadd[toadd_count].local_shape = local_shape;
@@ -560,7 +558,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state)
}
ShapePair sp(shape, local_shape);
- int idx = E->get().shapes.find(sp);
+ int idx = E->value.shapes.find(sp);
if (idx == -1) {
toadd[toadd_count].rid = rid;
toadd[toadd_count].local_shape = local_shape;
@@ -570,7 +568,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state)
continue;
}
- E->get().shapes[idx].tagged = true;
+ E->value.shapes[idx].tagged = true;
}
//put the ones to remove
@@ -607,7 +605,7 @@ void RigidDynamicBody3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint()) {
- set_notify_local_transform(true); //used for warnings and only in editor
+ set_notify_local_transform(true); // Used for warnings and only in editor.
}
} break;
@@ -875,30 +873,59 @@ int RigidDynamicBody3D::get_max_contacts_reported() const {
return max_contacts_reported;
}
-void RigidDynamicBody3D::add_central_force(const Vector3 &p_force) {
- PhysicsServer3D::get_singleton()->body_add_central_force(get_rid(), p_force);
+void RigidDynamicBody3D::apply_central_impulse(const Vector3 &p_impulse) {
+ PhysicsServer3D::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse);
}
-void RigidDynamicBody3D::add_force(const Vector3 &p_force, const Vector3 &p_position) {
+void RigidDynamicBody3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) {
PhysicsServer3D *singleton = PhysicsServer3D::get_singleton();
- singleton->body_add_force(get_rid(), p_force, p_position);
+ singleton->body_apply_impulse(get_rid(), p_impulse, p_position);
}
-void RigidDynamicBody3D::add_torque(const Vector3 &p_torque) {
- PhysicsServer3D::get_singleton()->body_add_torque(get_rid(), p_torque);
+void RigidDynamicBody3D::apply_torque_impulse(const Vector3 &p_impulse) {
+ PhysicsServer3D::get_singleton()->body_apply_torque_impulse(get_rid(), p_impulse);
}
-void RigidDynamicBody3D::apply_central_impulse(const Vector3 &p_impulse) {
- PhysicsServer3D::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse);
+void RigidDynamicBody3D::apply_central_force(const Vector3 &p_force) {
+ PhysicsServer3D::get_singleton()->body_apply_central_force(get_rid(), p_force);
}
-void RigidDynamicBody3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) {
+void RigidDynamicBody3D::apply_force(const Vector3 &p_force, const Vector3 &p_position) {
PhysicsServer3D *singleton = PhysicsServer3D::get_singleton();
- singleton->body_apply_impulse(get_rid(), p_impulse, p_position);
+ singleton->body_apply_force(get_rid(), p_force, p_position);
}
-void RigidDynamicBody3D::apply_torque_impulse(const Vector3 &p_impulse) {
- PhysicsServer3D::get_singleton()->body_apply_torque_impulse(get_rid(), p_impulse);
+void RigidDynamicBody3D::apply_torque(const Vector3 &p_torque) {
+ PhysicsServer3D::get_singleton()->body_apply_torque(get_rid(), p_torque);
+}
+
+void RigidDynamicBody3D::add_constant_central_force(const Vector3 &p_force) {
+ PhysicsServer3D::get_singleton()->body_add_constant_central_force(get_rid(), p_force);
+}
+
+void RigidDynamicBody3D::add_constant_force(const Vector3 &p_force, const Vector3 &p_position) {
+ PhysicsServer3D *singleton = PhysicsServer3D::get_singleton();
+ singleton->body_add_constant_force(get_rid(), p_force, p_position);
+}
+
+void RigidDynamicBody3D::add_constant_torque(const Vector3 &p_torque) {
+ PhysicsServer3D::get_singleton()->body_add_constant_torque(get_rid(), p_torque);
+}
+
+void RigidDynamicBody3D::set_constant_force(const Vector3 &p_force) {
+ PhysicsServer3D::get_singleton()->body_set_constant_force(get_rid(), p_force);
+}
+
+Vector3 RigidDynamicBody3D::get_constant_force() const {
+ return PhysicsServer3D::get_singleton()->body_get_constant_force(get_rid());
+}
+
+void RigidDynamicBody3D::set_constant_torque(const Vector3 &p_torque) {
+ PhysicsServer3D::get_singleton()->body_set_constant_torque(get_rid(), p_torque);
+}
+
+Vector3 RigidDynamicBody3D::get_constant_torque() const {
+ return PhysicsServer3D::get_singleton()->body_get_constant_torque(get_rid());
}
void RigidDynamicBody3D::set_use_continuous_collision_detection(bool p_enable) {
@@ -964,8 +991,8 @@ TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
- if (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05) {
- warnings.push_back(TTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ if (ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05) {
+ warnings.push_back(RTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
}
return warnings;
@@ -1024,14 +1051,24 @@ void RigidDynamicBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidDynamicBody3D::set_axis_velocity);
- ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidDynamicBody3D::add_central_force);
- ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &RigidDynamicBody3D::add_force, Vector3());
- ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidDynamicBody3D::add_torque);
-
ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidDynamicBody3D::apply_central_impulse);
ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &RigidDynamicBody3D::apply_impulse, Vector3());
ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidDynamicBody3D::apply_torque_impulse);
+ ClassDB::bind_method(D_METHOD("apply_central_force", "force"), &RigidDynamicBody3D::apply_central_force);
+ ClassDB::bind_method(D_METHOD("apply_force", "force", "position"), &RigidDynamicBody3D::apply_force, Vector3());
+ ClassDB::bind_method(D_METHOD("apply_torque", "torque"), &RigidDynamicBody3D::apply_torque);
+
+ ClassDB::bind_method(D_METHOD("add_constant_central_force", "force"), &RigidDynamicBody3D::add_constant_central_force);
+ ClassDB::bind_method(D_METHOD("add_constant_force", "force", "position"), &RigidDynamicBody3D::add_constant_force, Vector3());
+ ClassDB::bind_method(D_METHOD("add_constant_torque", "torque"), &RigidDynamicBody3D::add_constant_torque);
+
+ ClassDB::bind_method(D_METHOD("set_constant_force", "force"), &RigidDynamicBody3D::set_constant_force);
+ ClassDB::bind_method(D_METHOD("get_constant_force"), &RigidDynamicBody3D::get_constant_force);
+
+ ClassDB::bind_method(D_METHOD("set_constant_torque", "torque"), &RigidDynamicBody3D::set_constant_torque);
+ ClassDB::bind_method(D_METHOD("get_constant_torque"), &RigidDynamicBody3D::get_constant_torque);
+
ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidDynamicBody3D::set_sleeping);
ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidDynamicBody3D::is_sleeping);
@@ -1075,6 +1112,9 @@ void RigidDynamicBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
+ ADD_GROUP("Constant Forces", "constant_");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_force"), "set_constant_force", "get_constant_force");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_torque"), "set_constant_torque", "get_constant_torque");
ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index")));
ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape_index"), PropertyInfo(Variant::INT, "local_shape_index")));
@@ -1133,7 +1173,7 @@ bool CharacterBody3D::move_and_slide() {
for (int i = 0; i < 3; i++) {
if (locked_axis & (1 << i)) {
- motion_velocity[i] = 0.0;
+ velocity[i] = 0.0;
}
}
@@ -1191,7 +1231,7 @@ bool CharacterBody3D::move_and_slide() {
if (motion_mode == MOTION_MODE_GROUNDED) {
_move_and_slide_grounded(delta, was_on_floor);
} else {
- _move_and_slide_free(delta);
+ _move_and_slide_floating(delta);
}
// Compute real velocity.
@@ -1203,7 +1243,7 @@ bool CharacterBody3D::move_and_slide() {
if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) {
current_platform_velocity = current_platform_velocity.slide(up_direction);
}
- motion_velocity += current_platform_velocity;
+ velocity += current_platform_velocity;
}
}
@@ -1211,7 +1251,7 @@ bool CharacterBody3D::move_and_slide() {
}
void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) {
- Vector3 motion = motion_velocity * p_delta;
+ Vector3 motion = velocity * p_delta;
Vector3 motion_slide_up = motion.slide(up_direction);
Vector3 prev_floor_normal = floor_normal;
@@ -1231,7 +1271,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// If the platform's ceiling push down the body.
bool apply_ceiling_velocity = false;
bool first_slide = true;
- bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0;
+ bool vel_dir_facing_up = velocity.dot(up_direction) > 0;
Vector3 total_travel;
for (int iteration = 0; iteration < max_slides; ++iteration) {
@@ -1251,26 +1291,26 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
CollisionState result_state;
_set_collision_direction(result, result_state);
- // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one.
+ // If we hit a ceiling platform, we set the vertical velocity to at least the platform one.
if (collision_state.ceiling && platform_ceiling_velocity != Vector3() && platform_ceiling_velocity.dot(up_direction) < 0) {
// If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward.
if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (ceiling_normal + up_direction).length() < 0.01) {
apply_ceiling_velocity = true;
Vector3 ceiling_vertical_velocity = up_direction * up_direction.dot(platform_ceiling_velocity);
- Vector3 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity);
+ Vector3 motion_vertical_velocity = up_direction * up_direction.dot(velocity);
if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) {
- motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction);
+ velocity = ceiling_vertical_velocity + velocity.slide(up_direction);
}
}
}
- if (collision_state.floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
+ if (collision_state.floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) {
Transform3D gt = get_global_transform();
if (result.travel.length() <= margin + CMP_EPSILON) {
gt.origin -= result.travel;
}
set_global_transform(gt);
- motion_velocity = Vector3();
+ velocity = Vector3();
motion = Vector3();
last_motion = Vector3();
break;
@@ -1299,7 +1339,6 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// in order to avoid blocking lateral motion along a wall.
if (motion_angle < .5 * Math_PI) {
apply_default_sliding = false;
-
if (p_was_on_floor && !vel_dir_facing_up) {
// Cancel the motion.
Transform3D gt = get_global_transform();
@@ -1307,14 +1346,18 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
real_t cancel_dist_max = MIN(0.1, margin * 20);
if (travel_total <= margin + CMP_EPSILON) {
gt.origin -= result.travel;
+ result.travel = Vector3(); // Cancel for constant speed computation.
} else if (travel_total < cancel_dist_max) { // If the movement is large the body can be prevented from reaching the walls.
gt.origin -= result.travel.slide(up_direction);
// Keep remaining motion in sync with amount canceled.
motion = motion.slide(up_direction);
+ result.travel = Vector3();
+ } else {
+ // Travel is too high to be safely cancelled, we take it into account.
+ result.travel = result.travel.slide(up_direction);
+ motion = motion.normalized() * result.travel.length();
}
set_global_transform(gt);
- result.travel = Vector3(); // Cancel for constant speed computation.
-
// Determines if you are on the ground, and limits the possibility of climbing on the walls because of the approximations.
_snap_on_floor(true, false);
} else {
@@ -1325,8 +1368,15 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Apply slide on forward in order to allow only lateral motion on next step.
Vector3 forward = wall_normal.slide(up_direction).normalized();
motion = motion.slide(forward);
- // Avoid accelerating when you jump on the wall and smooth falling.
- motion_velocity = motion_velocity.slide(forward);
+
+ // Scales the horizontal velocity according to the wall slope.
+ if (vel_dir_facing_up) {
+ Vector3 slide_motion = velocity.slide(result.collisions[0].normal);
+ // Keeps the vertical motion from velocity and add the horizontal motion of the projection.
+ velocity = up_direction * up_direction.dot(velocity) + slide_motion.slide(up_direction);
+ } else {
+ velocity = velocity.slide(forward);
+ }
// Allow only lateral motion along previous floor when already on floor.
// Fixes slowing down when moving in diagonal against an inclined wall.
@@ -1355,7 +1405,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (stop_all_motion) {
motion = Vector3();
- motion_velocity = Vector3();
+ velocity = Vector3();
}
}
}
@@ -1366,7 +1416,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(motion_slide_up.normalized())));
if (motion_angle < wall_min_slide_angle) {
motion = up_direction * motion.dot(up_direction);
- motion_velocity = up_direction * motion_velocity.dot(up_direction);
+ velocity = up_direction * velocity.dot(up_direction);
apply_default_sliding = false;
}
@@ -1379,7 +1429,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
const PhysicsServer3D::MotionCollision &collision = result.collisions[0];
Vector3 slide_motion = result.remainder.slide(collision.normal);
- if (collision_state.floor && !collision_state.wall) {
+ if (collision_state.floor && !collision_state.wall && !motion_slide_up.is_equal_approx(Vector3())) {
// Slide using the intersection between the motion plane and the floor plane,
// in order to keep the direction intact.
real_t motion_length = slide_motion.length();
@@ -1391,7 +1441,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
slide_motion *= motion_length;
}
- if (slide_motion.dot(motion_velocity) > 0.0) {
+ if (slide_motion.dot(velocity) > 0.0) {
motion = slide_motion;
} else {
motion = Vector3();
@@ -1400,10 +1450,10 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (slide_on_ceiling && result_state.ceiling) {
// Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall.
if (vel_dir_facing_up) {
- motion_velocity = motion_velocity.slide(collision.normal);
+ velocity = velocity.slide(collision.normal);
} else {
// Avoid acceleration in slope when falling.
- motion_velocity = up_direction * up_direction.dot(motion_velocity);
+ velocity = up_direction * up_direction.dot(velocity);
}
}
}
@@ -1411,7 +1461,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
else {
motion = result.remainder;
if (result_state.ceiling && !slide_on_ceiling && vel_dir_facing_up) {
- motion_velocity = motion_velocity.slide(up_direction);
+ velocity = velocity.slide(up_direction);
motion = motion.slide(up_direction);
}
}
@@ -1456,12 +1506,12 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Reset the gravity accumulation when touching the ground.
if (collision_state.floor && !vel_dir_facing_up) {
- motion_velocity = motion_velocity.slide(up_direction);
+ velocity = velocity.slide(up_direction);
}
}
-void CharacterBody3D::_move_and_slide_free(double p_delta) {
- Vector3 motion = motion_velocity * p_delta;
+void CharacterBody3D::_move_and_slide_floating(double p_delta) {
+ Vector3 motion = velocity * p_delta;
platform_rid = RID();
platform_object_id = ObjectID();
@@ -1488,7 +1538,7 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
break;
}
- if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-motion_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
motion = Vector3();
if (result.travel.length() < margin + CMP_EPSILON) {
Transform3D gt = get_global_transform();
@@ -1502,7 +1552,7 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
motion = result.remainder.slide(wall_normal);
}
- if (motion.dot(motion_velocity) <= 0.0) {
+ if (motion.dot(velocity) <= 0.0) {
motion = Vector3();
}
}
@@ -1515,8 +1565,8 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
}
}
-void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) {
- if (collision_state.floor || !was_on_floor || vel_dir_facing_up) {
+void CharacterBody3D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up) {
+ if (collision_state.floor || !p_was_on_floor || p_vel_dir_facing_up) {
return;
}
@@ -1550,8 +1600,8 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
}
}
-bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) {
- if (up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) {
+bool CharacterBody3D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up) {
+ if (up_direction == Vector3() || collision_state.floor || !p_was_on_floor || p_vel_dir_facing_up) {
return false;
}
@@ -1584,6 +1634,7 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu
Vector3 prev_wall_normal = wall_normal;
int wall_collision_count = 0;
Vector3 combined_wall_normal;
+ Vector3 tmp_wall_col; // Avoid duplicate on average calculation.
for (int i = p_result.collision_count - 1; i >= 0; i--) {
const PhysicsServer3D::MotionCollision &collision = p_result.collisions[i];
@@ -1630,8 +1681,11 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu
}
// Collect normal for calculating average.
- combined_wall_normal += collision.normal;
- wall_collision_count++;
+ if (!collision.normal.is_equal_approx(tmp_wall_col)) {
+ tmp_wall_col = collision.normal;
+ combined_wall_normal += collision.normal;
+ wall_collision_count++;
+ }
}
if (r_state.wall) {
@@ -1673,12 +1727,12 @@ real_t CharacterBody3D::get_safe_margin() const {
return margin;
}
-const Vector3 &CharacterBody3D::get_motion_velocity() const {
- return motion_velocity;
+const Vector3 &CharacterBody3D::get_velocity() const {
+ return velocity;
}
-void CharacterBody3D::set_motion_velocity(const Vector3 &p_velocity) {
- motion_velocity = p_velocity;
+void CharacterBody3D::set_velocity(const Vector3 &p_velocity) {
+ velocity = p_velocity;
}
bool CharacterBody3D::is_on_floor() const {
@@ -1873,7 +1927,7 @@ const Vector3 &CharacterBody3D::get_up_direction() const {
}
void CharacterBody3D::set_up_direction(const Vector3 &p_up_direction) {
- ERR_FAIL_COND_MSG(p_up_direction == Vector3(), "up_direction can't be equal to Vector3.ZERO, consider using Free motion mode instead.");
+ ERR_FAIL_COND_MSG(p_up_direction == Vector3(), "up_direction can't be equal to Vector3.ZERO, consider using Floating motion mode instead.");
up_direction = p_up_direction.normalized();
}
@@ -1893,8 +1947,8 @@ void CharacterBody3D::_notification(int p_what) {
void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody3D::move_and_slide);
- ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody3D::set_motion_velocity);
- ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody3D::get_motion_velocity);
+ ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &CharacterBody3D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &CharacterBody3D::get_velocity);
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin);
@@ -1944,12 +1998,11 @@ void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody3D::_get_slide_collision);
ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody3D::_get_last_slide_collision);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Floating", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides");
- ADD_GROUP("Free Mode", "free_mode_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle");
ADD_GROUP("Floor", "floor_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled");
@@ -1957,14 +2010,14 @@ void CharacterBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_floor_snap_length", "get_floor_snap_length");
- ADD_GROUP("Moving platform", "moving_platform");
+ ADD_GROUP("Moving Platform", "moving_platform");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED);
- BIND_ENUM_CONSTANT(MOTION_MODE_FREE);
+ BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING);
BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS);
BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY);
@@ -1972,7 +2025,7 @@ void CharacterBody3D::_bind_methods() {
}
void CharacterBody3D::_validate_property(PropertyInfo &property) const {
- if (motion_mode == MOTION_MODE_FREE) {
+ if (motion_mode == MOTION_MODE_FLOATING) {
if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") {
property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
@@ -2110,6 +2163,37 @@ void PhysicalBone3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_po
PhysicsServer3D::get_singleton()->body_apply_impulse(get_rid(), p_impulse, p_position);
}
+void PhysicalBone3D::set_linear_velocity(const Vector3 &p_velocity) {
+ linear_velocity = p_velocity;
+ PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity);
+}
+
+Vector3 PhysicalBone3D::get_linear_velocity() const {
+ return linear_velocity;
+}
+
+void PhysicalBone3D::set_angular_velocity(const Vector3 &p_velocity) {
+ angular_velocity = p_velocity;
+ PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity);
+}
+
+Vector3 PhysicalBone3D::get_angular_velocity() const {
+ return angular_velocity;
+}
+
+void PhysicalBone3D::set_use_custom_integrator(bool p_enable) {
+ if (custom_integrator == p_enable) {
+ return;
+ }
+
+ custom_integrator = p_enable;
+ PhysicsServer3D::get_singleton()->body_set_omit_force_integration(get_rid(), p_enable);
+}
+
+bool PhysicalBone3D::is_using_custom_integrator() {
+ return custom_integrator;
+}
+
void PhysicalBone3D::reset_physics_simulation_state() {
if (simulate_physics) {
_start_physics_simulation();
@@ -2818,6 +2902,11 @@ void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
return;
}
+ linear_velocity = p_state->get_linear_velocity();
+ angular_velocity = p_state->get_angular_velocity();
+
+ GDVIRTUAL_CALL(_integrate_forces, p_state);
+
/// Update bone transform.
Transform3D global_transform(p_state->get_transform());
@@ -2880,9 +2969,20 @@ void PhysicalBone3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_angular_damp", "angular_damp"), &PhysicalBone3D::set_angular_damp);
ClassDB::bind_method(D_METHOD("get_angular_damp"), &PhysicalBone3D::get_angular_damp);
+ ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &PhysicalBone3D::set_linear_velocity);
+ ClassDB::bind_method(D_METHOD("get_linear_velocity"), &PhysicalBone3D::get_linear_velocity);
+
+ ClassDB::bind_method(D_METHOD("set_angular_velocity", "angular_velocity"), &PhysicalBone3D::set_angular_velocity);
+ ClassDB::bind_method(D_METHOD("get_angular_velocity"), &PhysicalBone3D::get_angular_velocity);
+
+ ClassDB::bind_method(D_METHOD("set_use_custom_integrator", "enable"), &PhysicalBone3D::set_use_custom_integrator);
+ ClassDB::bind_method(D_METHOD("is_using_custom_integrator"), &PhysicalBone3D::is_using_custom_integrator);
+
ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &PhysicalBone3D::set_can_sleep);
ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &PhysicalBone3D::is_able_to_sleep);
+ GDVIRTUAL_BIND(_integrate_forces, "state");
+
ADD_GROUP("Joint", "joint_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "joint_offset"), "set_joint_offset", "get_joint_offset");
@@ -2894,10 +2994,13 @@ void PhysicalBone3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
ADD_PROPERTY(PropertyInfo(Variant::INT, "linear_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_linear_damp_mode", "get_linear_damp_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
ADD_PROPERTY(PropertyInfo(Variant::INT, "angular_damp_mode", PROPERTY_HINT_ENUM, "Combine,Replace"), "set_angular_damp_mode", "get_angular_damp_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep");
BIND_ENUM_CONSTANT(DAMP_MODE_COMBINE);
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index 2ea796d335..22dcb218bc 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -50,11 +50,11 @@ protected:
uint16_t locked_axis = 0;
- Ref<KinematicCollision3D> _move(const Vector3 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1);
+ Ref<KinematicCollision3D> _move(const Vector3 &p_distance, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1);
public:
bool move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
- bool test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1);
+ bool test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1);
void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const;
@@ -212,7 +212,7 @@ private:
struct ContactMonitor {
bool locked = false;
- Map<ObjectID, BodyState> body_map;
+ HashMap<ObjectID, BodyState> body_map;
};
ContactMonitor *contact_monitor = nullptr;
@@ -306,14 +306,24 @@ public:
Array get_colliding_bodies() const;
- void add_central_force(const Vector3 &p_force);
- void add_force(const Vector3 &p_force, const Vector3 &p_position = Vector3());
- void add_torque(const Vector3 &p_torque);
-
void apply_central_impulse(const Vector3 &p_impulse);
void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3());
void apply_torque_impulse(const Vector3 &p_impulse);
+ void apply_central_force(const Vector3 &p_force);
+ void apply_force(const Vector3 &p_force, const Vector3 &p_position = Vector3());
+ void apply_torque(const Vector3 &p_torque);
+
+ void add_constant_central_force(const Vector3 &p_force);
+ void add_constant_force(const Vector3 &p_force, const Vector3 &p_position = Vector3());
+ void add_constant_torque(const Vector3 &p_torque);
+
+ void set_constant_force(const Vector3 &p_force);
+ Vector3 get_constant_force() const;
+
+ void set_constant_torque(const Vector3 &p_torque);
+ Vector3 get_constant_torque() const;
+
virtual TypedArray<String> get_configuration_warnings() const override;
RigidDynamicBody3D();
@@ -335,7 +345,7 @@ class CharacterBody3D : public PhysicsBody3D {
public:
enum MotionMode {
MOTION_MODE_GROUNDED,
- MOTION_MODE_FREE,
+ MOTION_MODE_FLOATING,
};
enum MovingPlatformApplyVelocityOnLeave {
PLATFORM_VEL_ON_LEAVE_ALWAYS,
@@ -344,8 +354,8 @@ public:
};
bool move_and_slide();
- const Vector3 &get_motion_velocity() const;
- void set_motion_velocity(const Vector3 &p_velocity);
+ const Vector3 &get_velocity() const;
+ void set_velocity(const Vector3 &p_velocity);
bool is_on_floor() const;
bool is_on_floor_only() const;
@@ -406,7 +416,7 @@ private:
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector3 up_direction = Vector3(0.0, 1.0, 0.0);
- Vector3 motion_velocity;
+ Vector3 velocity;
Vector3 floor_normal;
Vector3 wall_normal;
Vector3 ceiling_normal;
@@ -458,17 +468,17 @@ private:
void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity);
MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const;
- void _move_and_slide_free(double p_delta);
+ void _move_and_slide_floating(double p_delta);
void _move_and_slide_grounded(double p_delta, bool p_was_on_floor);
Ref<KinematicCollision3D> _get_slide_collision(int p_bounce);
Ref<KinematicCollision3D> _get_last_slide_collision();
const Vector3 &get_up_direction() const;
- bool _on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up);
+ bool _on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up);
void set_up_direction(const Vector3 &p_up_direction);
void _set_collision_direction(const PhysicsServer3D::MotionResult &p_result, CollisionState &r_state, CollisionState p_apply_state = CollisionState(true, true, true));
void _set_platform_data(const PhysicsServer3D::MotionCollision &p_collision);
- void _snap_on_floor(bool was_on_floor, bool vel_dir_facing_up);
+ void _snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up);
protected:
void _notification(int p_what);
@@ -652,9 +662,13 @@ private:
real_t bounce = 0.0;
real_t mass = 1.0;
real_t friction = 1.0;
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
real_t gravity_scale = 1.0;
bool can_sleep = true;
+ bool custom_integrator = false;
+
DampMode linear_damp_mode = DAMP_MODE_COMBINE;
DampMode angular_damp_mode = DAMP_MODE_COMBINE;
@@ -666,6 +680,7 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _notification(int p_what);
+ GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *)
static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state);
void _body_state_changed(PhysicsDirectBodyState3D *p_state);
@@ -681,6 +696,15 @@ private:
public:
void _on_bone_parent_changed();
+ void set_linear_velocity(const Vector3 &p_velocity);
+ Vector3 get_linear_velocity() const override;
+
+ void set_angular_velocity(const Vector3 &p_velocity);
+ Vector3 get_angular_velocity() const override;
+
+ void set_use_custom_integrator(bool p_enable);
+ bool is_using_custom_integrator();
+
#ifdef TOOLS_ENABLED
void _set_gizmo_move_joint(bool p_move_joint);
virtual Transform3D get_global_gizmo_transform() const override;
@@ -690,7 +714,9 @@ public:
const JointData *get_joint_data() const;
Skeleton3D *find_skeleton_parent();
- int get_bone_id() const { return bone_id; }
+ int get_bone_id() const {
+ return bone_id;
+ }
void set_joint_type(JointType p_joint_type);
JointType get_joint_type() const;
diff --git a/scene/3d/position_3d.cpp b/scene/3d/position_3d.cpp
index 9747465103..7dc1b1ace0 100644
--- a/scene/3d/position_3d.cpp
+++ b/scene/3d/position_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/position_3d.h b/scene/3d/position_3d.h
index 065b14c3bd..5514399e6e 100644
--- a/scene/3d/position_3d.h
+++ b/scene/3d/position_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/proximity_group_3d.cpp b/scene/3d/proximity_group_3d.cpp
deleted file mode 100644
index 23df00c1f6..0000000000
--- a/scene/3d/proximity_group_3d.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*************************************************************************/
-/* proximity_group_3d.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "proximity_group_3d.h"
-
-#include "core/math/math_funcs.h"
-
-void ProximityGroup3D::_clear_groups() {
- Map<StringName, uint32_t>::Element *E;
- const int size = 16;
-
- do {
- StringName remove_list[size];
- E = groups.front();
- int num = 0;
- while (E && num < size) {
- if (E->get() != group_version) {
- remove_list[num++] = E->key();
- }
-
- E = E->next();
- }
- for (int i = 0; i < num; i++) {
- groups.erase(remove_list[i]);
- }
- } while (E);
-}
-
-void ProximityGroup3D::_update_groups() {
- if (grid_radius == Vector3(0, 0, 0)) {
- return;
- }
-
- ++group_version;
-
- Vector3 pos = get_global_transform().get_origin();
- Vector3 vcell = pos / cell_size;
- int cell[3] = { Math::fast_ftoi(vcell.x), Math::fast_ftoi(vcell.y), Math::fast_ftoi(vcell.z) };
-
- _add_groups(cell, group_name, 0);
-
- _clear_groups();
-}
-
-void ProximityGroup3D::_add_groups(int *p_cell, String p_base, int p_depth) {
- p_base = p_base + "|";
- if (grid_radius[p_depth] == 0) {
- if (p_depth == 2) {
- _new_group(p_base);
- } else {
- _add_groups(p_cell, p_base, p_depth + 1);
- }
- }
-
- int start = p_cell[p_depth] - grid_radius[p_depth];
- int end = p_cell[p_depth] + grid_radius[p_depth];
-
- for (int i = start; i <= end; i++) {
- String gname = p_base + itos(i);
- if (p_depth == 2) {
- _new_group(gname);
- } else {
- _add_groups(p_cell, gname, p_depth + 1);
- }
- }
-}
-
-void ProximityGroup3D::_new_group(StringName p_name) {
- const Map<StringName, uint32_t>::Element *E = groups.find(p_name);
- if (!E) {
- add_to_group(p_name);
- }
-
- groups[p_name] = group_version;
-}
-
-void ProximityGroup3D::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_EXIT_TREE:
- ++group_version;
- _clear_groups();
- break;
- case NOTIFICATION_TRANSFORM_CHANGED:
- _update_groups();
- break;
- }
-}
-
-void ProximityGroup3D::broadcast(String p_method, Variant p_parameters) {
- Map<StringName, uint32_t>::Element *E;
- E = groups.front();
- while (E) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_method, p_parameters);
- E = E->next();
- }
-}
-
-void ProximityGroup3D::_proximity_group_broadcast(String p_method, Variant p_parameters) {
- if (dispatch_mode == MODE_PROXY) {
- ERR_FAIL_COND(!is_inside_tree());
- get_parent()->call(p_method, p_parameters);
- } else {
- emit_signal(SNAME("broadcast"), p_method, p_parameters);
- }
-}
-
-void ProximityGroup3D::set_group_name(const String &p_group_name) {
- group_name = p_group_name;
-}
-
-String ProximityGroup3D::get_group_name() const {
- return group_name;
-}
-
-void ProximityGroup3D::set_dispatch_mode(DispatchMode p_mode) {
- dispatch_mode = p_mode;
-}
-
-ProximityGroup3D::DispatchMode ProximityGroup3D::get_dispatch_mode() const {
- return dispatch_mode;
-}
-
-void ProximityGroup3D::set_grid_radius(const Vector3 &p_radius) {
- grid_radius = p_radius;
-}
-
-Vector3 ProximityGroup3D::get_grid_radius() const {
- return grid_radius;
-}
-
-void ProximityGroup3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_group_name", "name"), &ProximityGroup3D::set_group_name);
- ClassDB::bind_method(D_METHOD("get_group_name"), &ProximityGroup3D::get_group_name);
- ClassDB::bind_method(D_METHOD("set_dispatch_mode", "mode"), &ProximityGroup3D::set_dispatch_mode);
- ClassDB::bind_method(D_METHOD("get_dispatch_mode"), &ProximityGroup3D::get_dispatch_mode);
- ClassDB::bind_method(D_METHOD("set_grid_radius", "radius"), &ProximityGroup3D::set_grid_radius);
- ClassDB::bind_method(D_METHOD("get_grid_radius"), &ProximityGroup3D::get_grid_radius);
-
- ClassDB::bind_method(D_METHOD("broadcast", "method", "parameters"), &ProximityGroup3D::broadcast);
-
- ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "method", "parameters"), &ProximityGroup3D::_proximity_group_broadcast);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "group_name"), "set_group_name", "get_group_name");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "dispatch_mode", PROPERTY_HINT_ENUM, "Proxy,Signal"), "set_dispatch_mode", "get_dispatch_mode");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "grid_radius"), "set_grid_radius", "get_grid_radius");
-
- ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "method"), PropertyInfo(Variant::ARRAY, "parameters")));
-
- BIND_ENUM_CONSTANT(MODE_PROXY);
- BIND_ENUM_CONSTANT(MODE_SIGNAL);
-}
-
-ProximityGroup3D::ProximityGroup3D() {
- set_notify_transform(true);
-}
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index bfa397a1f5..f5e08b92ca 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -171,8 +171,8 @@ void RayCast3D::_notification(int p_what) {
exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid());
}
}
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (enabled) {
set_physics_process_internal(false);
@@ -181,8 +181,8 @@ void RayCast3D::_notification(int p_what) {
if (debug_shape) {
_clear_debug_shape();
}
-
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled) {
break;
@@ -193,7 +193,6 @@ void RayCast3D::_notification(int p_what) {
if (prev_collision_state != collided && get_tree()->is_debugging_collisions_hint()) {
_update_debug_shape_material(true);
}
-
} break;
}
}
@@ -243,30 +242,29 @@ void RayCast3D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void RayCast3D::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 RayCast3D::add_exception(const CollisionObject3D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D.");
+ add_exception_rid(p_node->get_rid());
}
void RayCast3D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void RayCast3D::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 RayCast3D::remove_exception(const CollisionObject3D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D.");
+ remove_exception_rid(p_node->get_rid());
}
void RayCast3D::clear_exceptions() {
exclude.clear();
+
+ if (exclude_parent_body && is_inside_tree()) {
+ CollisionObject3D *parent = Object::cast_to<CollisionObject3D>(get_parent());
+ if (parent) {
+ exclude.insert(parent->get_rid());
+ }
+ }
}
void RayCast3D::set_collide_with_areas(bool p_enabled) {
@@ -355,7 +353,7 @@ void RayCast3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_shape_thickness", PROPERTY_HINT_RANGE, "1,5"), "set_debug_shape_thickness", "get_debug_shape_thickness");
}
-float RayCast3D::get_debug_shape_thickness() const {
+int RayCast3D::get_debug_shape_thickness() const {
return debug_shape_thickness;
}
@@ -384,7 +382,7 @@ void RayCast3D::_update_debug_shape_vertices() {
}
}
-void RayCast3D::set_debug_shape_thickness(const float p_debug_shape_thickness) {
+void RayCast3D::set_debug_shape_thickness(const int p_debug_shape_thickness) {
debug_shape_thickness = p_debug_shape_thickness;
update_gizmos();
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index 5c2a61c35b..d6062969d8 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,8 @@
#include "scene/3d/node_3d.h"
+class CollisionObject3D;
+
class RayCast3D : public Node3D {
GDCLASS(RayCast3D, Node3D);
@@ -44,7 +46,7 @@ class RayCast3D : public Node3D {
Vector3 collision_normal;
Vector3 target_position = Vector3(0, -1, 0);
- Set<RID> exclude;
+ RBSet<RID> exclude;
uint32_t collision_mask = 1;
bool exclude_parent_body = true;
@@ -105,8 +107,8 @@ public:
Ref<StandardMaterial3D> get_debug_material();
- float get_debug_shape_thickness() const;
- void set_debug_shape_thickness(const float p_debug_thickness);
+ int get_debug_shape_thickness() const;
+ void set_debug_shape_thickness(const int p_debug_thickness);
void force_raycast_update();
bool is_colliding() const;
@@ -116,9 +118,9 @@ public:
Vector3 get_collision_normal() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject3D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject3D *p_node);
void clear_exceptions();
RayCast3D();
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index f7f19596a7..1bebd8e335 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -76,13 +76,13 @@ float ReflectionProbe::get_max_distance() const {
return max_distance;
}
-void ReflectionProbe::set_lod_threshold(float p_pixels) {
- lod_threshold = p_pixels;
- RS::get_singleton()->reflection_probe_set_lod_threshold(probe, p_pixels);
+void ReflectionProbe::set_mesh_lod_threshold(float p_pixels) {
+ mesh_lod_threshold = p_pixels;
+ RS::get_singleton()->reflection_probe_set_mesh_lod_threshold(probe, p_pixels);
}
-float ReflectionProbe::get_lod_threshold() const {
- return lod_threshold;
+float ReflectionProbe::get_mesh_lod_threshold() const {
+ return mesh_lod_threshold;
}
void ReflectionProbe::set_extents(const Vector3 &p_extents) {
@@ -178,10 +178,6 @@ AABB ReflectionProbe::get_aabb() const {
return aabb;
}
-Vector<Face3> ReflectionProbe::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
void ReflectionProbe::_validate_property(PropertyInfo &property) const {
if (property.name == "interior/ambient_color" || property.name == "interior/ambient_color_energy") {
if (ambient_mode != AMBIENT_COLOR) {
@@ -207,8 +203,8 @@ void ReflectionProbe::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_distance", "max_distance"), &ReflectionProbe::set_max_distance);
ClassDB::bind_method(D_METHOD("get_max_distance"), &ReflectionProbe::get_max_distance);
- ClassDB::bind_method(D_METHOD("set_lod_threshold", "ratio"), &ReflectionProbe::set_lod_threshold);
- ClassDB::bind_method(D_METHOD("get_lod_threshold"), &ReflectionProbe::get_lod_threshold);
+ ClassDB::bind_method(D_METHOD("set_mesh_lod_threshold", "ratio"), &ReflectionProbe::set_mesh_lod_threshold);
+ ClassDB::bind_method(D_METHOD("get_mesh_lod_threshold"), &ReflectionProbe::get_mesh_lod_threshold);
ClassDB::bind_method(D_METHOD("set_extents", "extents"), &ReflectionProbe::set_extents);
ClassDB::bind_method(D_METHOD("get_extents"), &ReflectionProbe::get_extents);
@@ -240,7 +236,7 @@ void ReflectionProbe::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_as_interior", "is_set_as_interior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold");
ADD_GROUP("Ambient", "ambient_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,Constant Color"), "set_ambient_mode", "get_ambient_mode");
diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h
index d1b9b12f65..424976d895 100644
--- a/scene/3d/reflection_probe.h
+++ b/scene/3d/reflection_probe.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -60,7 +60,7 @@ private:
AmbientMode ambient_mode = AMBIENT_ENVIRONMENT;
Color ambient_color = Color(0, 0, 0);
float ambient_color_energy = 1.0;
- float lod_threshold = 1.0;
+ float mesh_lod_threshold = 1.0;
uint32_t cull_mask = (1 << 20) - 1;
UpdateMode update_mode = UPDATE_ONCE;
@@ -88,8 +88,8 @@ public:
void set_max_distance(float p_distance);
float get_max_distance() const;
- void set_lod_threshold(float p_pixels);
- float get_lod_threshold() const;
+ void set_mesh_lod_threshold(float p_pixels);
+ float get_mesh_lod_threshold() const;
void set_extents(const Vector3 &p_extents);
Vector3 get_extents() const;
@@ -113,7 +113,6 @@ public:
UpdateMode get_update_mode() const;
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
ReflectionProbe();
~ReflectionProbe();
diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp
index d890609e23..9979052385 100644
--- a/scene/3d/remote_transform_3d.cpp
+++ b/scene/3d/remote_transform_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -111,8 +111,8 @@ void RemoteTransform3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_update_cache();
-
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
if (!is_inside_tree()) {
break;
@@ -121,7 +121,6 @@ void RemoteTransform3D::_notification(int p_what) {
if (cache.is_valid()) {
_update_remote();
}
-
} break;
}
}
@@ -183,7 +182,7 @@ TypedArray<String> RemoteTransform3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) {
- warnings.push_back(TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."));
+ warnings.push_back(RTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."));
}
return warnings;
diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h
index 321bd3b51e..03bb253578 100644
--- a/scene/3d/remote_transform_3d.h
+++ b/scene/3d/remote_transform_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index d4d3c0ebe1..f4a7767c44 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,6 @@
#include "core/object/message_queue.h"
#include "core/variant/type_info.h"
-#include "editor/plugins/skeleton_3d_editor_plugin.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
#include "scene/resources/surface_tool.h"
@@ -264,7 +263,7 @@ void Skeleton3D::_notification(int p_what) {
force_update_all_bone_transforms();
// Update skins.
- for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+ for (RBSet<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
const Skin *skin = E->get()->skin.operator->();
RID skeleton = E->get()->skeleton;
uint32_t bind_count = skin->get_bind_count();
@@ -318,11 +317,9 @@ void Skeleton3D::_notification(int p_what) {
rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i));
}
}
-
#ifdef TOOLS_ENABLED
emit_signal(SceneStringNames::get_singleton()->pose_updated);
#endif // TOOLS_ENABLED
-
} break;
#ifndef _3D_DISABLED
@@ -344,19 +341,14 @@ void Skeleton3D::_notification(int p_what) {
if (modification_stack.is_valid()) {
execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process);
}
-
} break;
-#endif // _3D_DISABLED
-#ifndef _3D_DISABLED
case NOTIFICATION_INTERNAL_PROCESS: {
if (modification_stack.is_valid()) {
execute_modifications(get_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_process);
}
} break;
-#endif // _3D_DISABLED
-#ifndef _3D_DISABLED
case NOTIFICATION_READY: {
set_physics_process_internal(true);
set_process_internal(true);
@@ -506,7 +498,7 @@ int Skeleton3D::get_bone_axis_forward_enum(int p_bone) {
// Skeleton creation api
void Skeleton3D::add_bone(const String &p_name) {
- ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1);
+ ERR_FAIL_COND(p_name.is_empty() || p_name.contains(":") || p_name.contains("/"));
for (int i = 0; i < bones.size(); i++) {
ERR_FAIL_COND(bones[i].name == p_name);
@@ -517,6 +509,7 @@ void Skeleton3D::add_bone(const String &p_name) {
bones.push_back(b);
process_order_dirty = true;
version++;
+ rest_dirty = true;
_make_dirty();
update_gizmos();
}
@@ -575,6 +568,7 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) {
bones.write[p_bone].parent = p_parent;
process_order_dirty = true;
+ rest_dirty = true;
_make_dirty();
}
@@ -593,6 +587,7 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) {
bones.write[p_bone].parent = -1;
process_order_dirty = true;
+ rest_dirty = true;
_make_dirty();
}
@@ -615,6 +610,7 @@ void Skeleton3D::set_bone_children(int p_bone, Vector<int> p_children) {
bones.write[p_bone].child_bones = p_children;
process_order_dirty = true;
+ rest_dirty = true;
_make_dirty();
}
@@ -624,6 +620,7 @@ void Skeleton3D::add_bone_child(int p_bone, int p_child) {
bones.write[p_bone].child_bones.push_back(p_child);
process_order_dirty = true;
+ rest_dirty = true;
_make_dirty();
}
@@ -633,12 +630,13 @@ void Skeleton3D::remove_bone_child(int p_bone, int p_child) {
int child_idx = bones[p_bone].child_bones.find(p_child);
if (child_idx >= 0) {
- bones.write[p_bone].child_bones.remove(child_idx);
+ bones.write[p_bone].child_bones.remove_at(child_idx);
} else {
WARN_PRINT("Cannot remove child bone: Child bone not found.");
}
process_order_dirty = true;
+ rest_dirty = true;
_make_dirty();
}
@@ -651,6 +649,7 @@ void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) {
ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].rest = p_rest;
+ rest_dirty = true;
_make_dirty();
}
Transform3D Skeleton3D::get_bone_rest(int p_bone) const {
@@ -659,6 +658,14 @@ Transform3D Skeleton3D::get_bone_rest(int p_bone) const {
return bones[p_bone].rest;
}
+Transform3D Skeleton3D::get_bone_global_rest(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
+ if (rest_dirty) {
+ const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
+ }
+ return bones[p_bone].global_rest;
+}
void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) {
const int bone_size = bones.size();
@@ -993,7 +1000,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>());
- for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+ for (RBSet<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
if (E->get()->skin == p_skin) {
return Ref<SkinReference>(E->get());
}
@@ -1066,6 +1073,9 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
b.pose_global_no_override = b.pose_global;
}
}
+ if (rest_dirty) {
+ b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest;
+ }
if (b.local_pose_override_amount >= CMP_EPSILON) {
Transform3D override_local_pose;
@@ -1096,6 +1106,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx);
}
+ rest_dirty = false;
}
// Helper functions
@@ -1214,6 +1225,7 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest);
ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest);
+ ClassDB::bind_method(D_METHOD("get_bone_global_rest", "bone_idx"), &Skeleton3D::get_bone_global_rest);
ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms"), &Skeleton3D::create_skin_from_rest_transforms);
ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton3D::register_skin);
@@ -1291,7 +1303,7 @@ Skeleton3D::Skeleton3D() {
Skeleton3D::~Skeleton3D() {
// Some skins may remain bound.
- for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+ for (RBSet<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
E->get()->skeleton_node = nullptr;
}
}
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index f7bc3df94e..c72792bd47 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,13 +44,13 @@ class SkinReference : public RefCounted {
GDCLASS(SkinReference, RefCounted)
friend class Skeleton3D;
- Skeleton3D *skeleton_node;
+ Skeleton3D *skeleton_node = nullptr;
RID skeleton;
Ref<Skin> skin;
uint32_t bind_count = 0;
uint64_t skeleton_version = 0;
Vector<uint32_t> skin_bone_indices;
- uint32_t *skin_bone_indices_ptrs;
+ uint32_t *skin_bone_indices_ptrs = nullptr;
void _skin_changed();
protected:
@@ -77,6 +77,7 @@ private:
int parent;
Transform3D rest;
+ Transform3D global_rest;
_FORCE_INLINE_ void update_pose_cache() {
if (pose_cache_dirty) {
@@ -130,18 +131,19 @@ private:
}
};
- Set<SkinReference *> skin_bindings;
+ RBSet<SkinReference *> skin_bindings;
void _skin_changed();
bool animate_physical_bones = true;
Vector<Bone> bones;
- bool process_order_dirty;
+ bool process_order_dirty = false;
Vector<int> parentless_bones;
void _make_dirty();
bool dirty = false;
+ bool rest_dirty = false;
bool show_rest_only = false;
@@ -198,6 +200,7 @@ public:
void set_bone_rest(int p_bone, const Transform3D &p_rest);
Transform3D get_bone_rest(int p_bone) const;
+ Transform3D get_bone_global_rest(int p_bone) const;
Transform3D get_bone_global_pose(int p_bone) const;
Transform3D get_bone_global_pose_no_override(int p_bone) const;
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 1498955ec0..5e1f9d047f 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,10 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-/**
- * @author AndreaCatania
- */
-
#include "skeleton_ik_3d.h"
#ifndef _3D_DISABLED
@@ -144,7 +140,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vec
}
}
-void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) {
+void FabrikInverseKinematic::solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet) {
if (p_solve_magnet && !r_chain.middle_chain_item) {
return;
}
@@ -411,14 +407,14 @@ void SkeletonIK3D::_notification(int p_what) {
set_process_priority(1);
reload_chain();
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
if (target_node_override) {
reload_goal();
}
-
_solve_chain();
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
reload_chain();
} break;
diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h
index ccb25bcd4c..0f656187de 100644
--- a/scene/3d/skeleton_ik_3d.h
+++ b/scene/3d/skeleton_ik_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -103,7 +103,7 @@ private:
static void solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos);
/// Special solvers that solve only chains with one end effector
- static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet);
+ static void solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet);
static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos);
public:
diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp
index 903eedb58b..7d786a41bf 100644
--- a/scene/3d/soft_dynamic_body_3d.cpp
+++ b/scene/3d/soft_dynamic_body_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -100,12 +100,11 @@ SoftDynamicBody3D::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) {
offset = obj_tocopy.offset;
}
-SoftDynamicBody3D::PinnedPoint &SoftDynamicBody3D::PinnedPoint::operator=(const PinnedPoint &obj) {
+void SoftDynamicBody3D::PinnedPoint::operator=(const PinnedPoint &obj) {
point_index = obj.point_index;
spatial_attachment_path = obj.spatial_attachment_path;
spatial_attachment = obj.spatial_attachment;
offset = obj.offset;
- return *this;
}
void SoftDynamicBody3D::_update_pickable() {
@@ -379,12 +378,12 @@ TypedArray<String> SoftDynamicBody3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (mesh.is_null()) {
- warnings.push_back(TTR("This body will be ignored until you set a mesh."));
+ warnings.push_back(RTR("This body will be ignored until you set a mesh."));
}
Transform3D t = get_transform();
- if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) {
- warnings.push_back(TTR("Size changes to SoftDynamicBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ if ((ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05)) {
+ warnings.push_back(RTR("Size changes to SoftDynamicBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
}
return warnings;
@@ -418,8 +417,8 @@ void SoftDynamicBody3D::_draw_soft_mesh() {
PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh_rid);
}
- if (!rendering_server_handler.is_ready(mesh_rid)) {
- rendering_server_handler.prepare(mesh_rid, 0);
+ if (!rendering_server_handler->is_ready(mesh_rid)) {
+ rendering_server_handler->prepare(mesh_rid, 0);
/// Necessary in order to render the mesh correctly (Soft body nodes are in global space)
simulation_started = true;
@@ -429,11 +428,11 @@ void SoftDynamicBody3D::_draw_soft_mesh() {
_update_physics_server();
- rendering_server_handler.open();
- PhysicsServer3D::get_singleton()->soft_body_update_rendering_server(physics_rid, &rendering_server_handler);
- rendering_server_handler.close();
+ rendering_server_handler->open();
+ PhysicsServer3D::get_singleton()->soft_body_update_rendering_server(physics_rid, rendering_server_handler);
+ rendering_server_handler->close();
- rendering_server_handler.commit_changes();
+ rendering_server_handler->commit_changes();
}
void SoftDynamicBody3D::_prepare_physics_server() {
@@ -689,10 +688,12 @@ bool SoftDynamicBody3D::is_ray_pickable() const {
SoftDynamicBody3D::SoftDynamicBody3D() :
physics_rid(PhysicsServer3D::get_singleton()->soft_body_create()) {
+ rendering_server_handler = memnew(SoftDynamicBodyRenderingServerHandler);
PhysicsServer3D::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id());
}
SoftDynamicBody3D::~SoftDynamicBody3D() {
+ memdelete(rendering_server_handler);
PhysicsServer3D::get_singleton()->free(physics_rid);
}
@@ -773,7 +774,7 @@ void SoftDynamicBody3D::_reset_points_offsets() {
void SoftDynamicBody3D::_remove_pinned_point(int p_point_index) {
const int id(_has_pinned_point(p_point_index));
if (-1 != id) {
- pinned_points.remove(id);
+ pinned_points.remove_at(id);
}
}
diff --git a/scene/3d/soft_dynamic_body_3d.h b/scene/3d/soft_dynamic_body_3d.h
index 57e116aa05..e11e5c73df 100644
--- a/scene/3d/soft_dynamic_body_3d.h
+++ b/scene/3d/soft_dynamic_body_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,7 +36,7 @@
class SoftDynamicBody3D;
-class SoftDynamicBodyRenderingServerHandler : public RenderingServerHandler {
+class SoftDynamicBodyRenderingServerHandler : public PhysicsServer3DRenderingServerHandler {
friend class SoftDynamicBody3D;
RID mesh;
@@ -80,11 +80,11 @@ public:
PinnedPoint();
PinnedPoint(const PinnedPoint &obj_tocopy);
- PinnedPoint &operator=(const PinnedPoint &obj);
+ void operator=(const PinnedPoint &obj);
};
private:
- SoftDynamicBodyRenderingServerHandler rendering_server_handler;
+ SoftDynamicBodyRenderingServerHandler *rendering_server_handler = nullptr;
RID physics_rid;
diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp
index 0b5af8823b..230801bd52 100644
--- a/scene/3d/spring_arm_3d.cpp
+++ b/scene/3d/spring_arm_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,23 +29,26 @@
/*************************************************************************/
#include "spring_arm_3d.h"
+
#include "scene/3d/camera_3d.h"
void SpringArm3D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_ENTER_TREE: {
if (!Engine::get_singleton()->is_editor_hint()) {
set_physics_process_internal(true);
}
- break;
- case NOTIFICATION_EXIT_TREE:
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
if (!Engine::get_singleton()->is_editor_hint()) {
set_physics_process_internal(false);
}
- break;
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS:
+ } break;
+
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
process_spring();
- break;
+ } break;
}
}
@@ -185,14 +188,14 @@ void SpringArm3D::process_spring() {
}
current_spring_length = spring_length * motion_delta;
- Transform3D childs_transform;
- childs_transform.origin = get_global_transform().origin + cast_direction * (spring_length * motion_delta);
+ Transform3D child_transform;
+ child_transform.origin = get_global_transform().origin + cast_direction * (spring_length * motion_delta);
for (int i = get_child_count() - 1; 0 <= i; --i) {
Node3D *child = Object::cast_to<Node3D>(get_child(i));
if (child) {
- childs_transform.basis = child->get_global_transform().basis;
- child->set_global_transform(childs_transform);
+ child_transform.basis = child->get_global_transform().basis;
+ child->set_global_transform(child_transform);
}
}
}
diff --git a/scene/3d/spring_arm_3d.h b/scene/3d/spring_arm_3d.h
index 63505ab9d3..78d9db7259 100644
--- a/scene/3d/spring_arm_3d.h
+++ b/scene/3d/spring_arm_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,7 +37,7 @@ class SpringArm3D : public Node3D {
GDCLASS(SpringArm3D, Node3D);
Ref<Shape3D> shape;
- Set<RID> excluded_objects;
+ RBSet<RID> excluded_objects;
real_t spring_length = 1.0;
real_t current_spring_length = 0.0;
bool keep_child_basis = false;
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 90af70e7c2..8cb5081047 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -66,23 +66,25 @@ void SpriteBase3D::_propagate_color_changed() {
}
void SpriteBase3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (!pending_update) {
- _im_update();
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (!pending_update) {
+ _im_update();
+ }
- parent_sprite = Object::cast_to<SpriteBase3D>(get_parent());
- if (parent_sprite) {
- pI = parent_sprite->children.push_back(this);
- }
- }
+ parent_sprite = Object::cast_to<SpriteBase3D>(get_parent());
+ if (parent_sprite) {
+ pI = parent_sprite->children.push_back(this);
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- if (parent_sprite) {
- parent_sprite->children.erase(pI);
- pI = nullptr;
- parent_sprite = nullptr;
- }
+ case NOTIFICATION_EXIT_TREE: {
+ if (parent_sprite) {
+ parent_sprite->children.erase(pI);
+ pI = nullptr;
+ parent_sprite = nullptr;
+ }
+ } break;
}
}
@@ -132,22 +134,23 @@ Color SpriteBase3D::get_modulate() const {
return modulate;
}
-void SpriteBase3D::set_pixel_size(real_t p_amount) {
- pixel_size = p_amount;
+void SpriteBase3D::set_render_priority(int p_priority) {
+ ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX);
+ render_priority = p_priority;
_queue_update();
}
-real_t SpriteBase3D::get_pixel_size() const {
- return pixel_size;
+int SpriteBase3D::get_render_priority() const {
+ return render_priority;
}
-void SpriteBase3D::set_opacity(float p_amount) {
- opacity = p_amount;
+void SpriteBase3D::set_pixel_size(real_t p_amount) {
+ pixel_size = p_amount;
_queue_update();
}
-float SpriteBase3D::get_opacity() const {
- return opacity;
+real_t SpriteBase3D::get_pixel_size() const {
+ return pixel_size;
}
void SpriteBase3D::set_axis(Vector3::Axis p_axis) {
@@ -184,10 +187,6 @@ AABB SpriteBase3D::get_aabb() const {
return aabb;
}
-Vector<Face3> SpriteBase3D::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const {
if (triangle_mesh.is_valid()) {
return triangle_mesh;
@@ -279,6 +278,17 @@ StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const {
return billboard_mode;
}
+void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) {
+ if (texture_filter != p_filter) {
+ texture_filter = p_filter;
+ _queue_update();
+ }
+}
+
+StandardMaterial3D::TextureFilter SpriteBase3D::get_texture_filter() const {
+ return texture_filter;
+}
+
void SpriteBase3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_centered", "centered"), &SpriteBase3D::set_centered);
ClassDB::bind_method(D_METHOD("is_centered"), &SpriteBase3D::is_centered);
@@ -295,8 +305,8 @@ void SpriteBase3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &SpriteBase3D::set_modulate);
ClassDB::bind_method(D_METHOD("get_modulate"), &SpriteBase3D::get_modulate);
- ClassDB::bind_method(D_METHOD("set_opacity", "opacity"), &SpriteBase3D::set_opacity);
- ClassDB::bind_method(D_METHOD("get_opacity"), &SpriteBase3D::get_opacity);
+ ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &SpriteBase3D::set_render_priority);
+ ClassDB::bind_method(D_METHOD("get_render_priority"), &SpriteBase3D::get_render_priority);
ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &SpriteBase3D::set_pixel_size);
ClassDB::bind_method(D_METHOD("get_pixel_size"), &SpriteBase3D::get_pixel_size);
@@ -313,6 +323,9 @@ void SpriteBase3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &SpriteBase3D::set_billboard_mode);
ClassDB::bind_method(D_METHOD("get_billboard_mode"), &SpriteBase3D::get_billboard_mode);
+ ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &SpriteBase3D::set_texture_filter);
+ ClassDB::bind_method(D_METHOD("get_texture_filter"), &SpriteBase3D::get_texture_filter);
+
ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect);
ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh);
@@ -324,7 +337,6 @@ void SpriteBase3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "opacity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_opacity", "get_opacity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001"), "set_pixel_size", "get_pixel_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "axis", PROPERTY_HINT_ENUM, "X-Axis,Y-Axis,Z-Axis"), "set_axis", "get_axis");
ADD_GROUP("Flags", "");
@@ -332,11 +344,17 @@ void SpriteBase3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_draw_flag", "get_draw_flag", FLAG_TRANSPARENT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE);
ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority");
BIND_ENUM_CONSTANT(FLAG_TRANSPARENT);
BIND_ENUM_CONSTANT(FLAG_SHADED);
BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED);
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST);
+ BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED);
@@ -468,7 +486,6 @@ void Sprite3D::_draw() {
}
Color color = _get_color_accum();
- color.a *= get_opacity();
real_t pixel_size = get_pixel_size();
@@ -560,7 +577,7 @@ void Sprite3D::_draw() {
value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10;
value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20;
if (t.d > 0) {
- value |= 3 << 30;
+ value |= 3UL << 30;
}
v_tangent = value;
}
@@ -602,7 +619,7 @@ void Sprite3D::_draw() {
set_aabb(aabb);
RID shader_rid;
- StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid);
+ StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid);
if (last_shader != shader_rid) {
RS::get_singleton()->material_set_shader(get_material(), shader_rid);
last_shader = shader_rid;
@@ -611,6 +628,10 @@ void Sprite3D::_draw() {
RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid());
last_texture = texture->get_rid();
}
+ if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) {
+ RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority());
+ RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material());
+ }
}
void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) {
@@ -625,6 +646,7 @@ void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) {
texture->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update"));
}
_queue_update();
+ emit_signal(SceneStringNames::get_singleton()->texture_changed);
}
Ref<Texture2D> Sprite3D::get_texture() const {
@@ -707,10 +729,6 @@ Rect2 Sprite3D::get_item_rect() const {
if (texture.is_null()) {
return Rect2(0, 0, 1, 1);
}
- /*
- if (texture.is_null())
- return CanvasItem::get_item_rect();
- */
Size2 s;
@@ -780,6 +798,7 @@ void Sprite3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
ADD_SIGNAL(MethodInfo("frame_changed"));
+ ADD_SIGNAL(MethodInfo("texture_changed"));
}
Sprite3D::Sprite3D() {
@@ -835,7 +854,6 @@ void AnimatedSprite3D::_draw() {
}
Color color = _get_color_accum();
- color.a *= get_opacity();
real_t pixel_size = get_pixel_size();
@@ -926,7 +944,7 @@ void AnimatedSprite3D::_draw() {
value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10;
value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20;
if (t.d > 0) {
- value |= 3 << 30;
+ value |= 3UL << 30;
}
v_tangent = value;
}
@@ -967,7 +985,7 @@ void AnimatedSprite3D::_draw() {
set_aabb(aabb);
RID shader_rid;
- StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid);
+ StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid);
if (last_shader != shader_rid) {
RS::get_singleton()->material_set_shader(get_material(), shader_rid);
last_shader = shader_rid;
@@ -976,6 +994,10 @@ void AnimatedSprite3D::_draw() {
RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid());
last_texture = texture->get_rid();
}
+ if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) {
+ RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority());
+ RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material());
+ }
}
void AnimatedSprite3D::_validate_property(PropertyInfo &property) const {
@@ -996,13 +1018,13 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &property) const {
}
property.hint_string += String(E->get());
- if (animation == E) {
+ if (animation == E->get()) {
current_found = true;
}
}
if (!current_found) {
- if (property.hint_string == String()) {
+ if (property.hint_string.is_empty()) {
property.hint_string = String(animation);
} else {
property.hint_string = String(animation) + "," + property.hint_string;
@@ -1034,14 +1056,14 @@ void AnimatedSprite3D::_notification(int p_what) {
return;
}
- float speed = frames->get_animation_speed(animation);
- if (speed == 0) {
- return; //do nothing
- }
-
double remaining = get_process_delta_time();
while (remaining) {
+ double speed = frames->get_animation_speed(animation);
+ if (speed == 0) {
+ return; // Do nothing.
+ }
+
if (timeout <= 0) {
timeout = 1.0 / speed;
@@ -1101,8 +1123,9 @@ void AnimatedSprite3D::set_frame(int p_frame) {
if (frames->has_animation(animation)) {
int limit = frames->get_frame_count(animation);
- if (p_frame >= limit)
+ if (p_frame >= limit) {
p_frame = limit - 1;
+ }
}
if (p_frame < 0) {
@@ -1218,12 +1241,23 @@ StringName AnimatedSprite3D::get_animation() const {
TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const {
TypedArray<String> warnings = SpriteBase3D::get_configuration_warnings();
if (frames.is_null()) {
- warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames."));
+ warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames."));
}
return warnings;
}
+void AnimatedSprite3D::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ if (p_idx == 0 && p_function == "play" && frames.is_valid()) {
+ List<StringName> al;
+ frames->get_animation_list(&al);
+ for (const StringName &name : al) {
+ r_options->push_back(String(name).quote());
+ }
+ }
+ Node::get_argument_options(p_function, p_idx, r_options);
+}
+
void AnimatedSprite3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite3D::set_sprite_frames);
ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite3D::get_sprite_frames);
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index 61448c0e32..6ac85a7bbc 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,6 +44,8 @@ public:
FLAG_TRANSPARENT,
FLAG_SHADED,
FLAG_DOUBLE_SIDED,
+ FLAG_DISABLE_DEPTH_TEST,
+ FLAG_FIXED_SIZE,
FLAG_MAX
};
@@ -69,7 +71,7 @@ private:
bool vflip = false;
Color modulate = Color(1, 1, 1, 1);
- float opacity = 1.0;
+ int render_priority = 0;
Vector3::Axis axis = Vector3::AXIS_Z;
real_t pixel_size = 0.01;
@@ -81,6 +83,7 @@ private:
bool flags[FLAG_MAX] = {};
AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED;
StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED;
+ StandardMaterial3D::TextureFilter texture_filter = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS;
bool pending_update = false;
void _im_update();
@@ -98,10 +101,10 @@ protected:
uint32_t mesh_surface_offsets[RS::ARRAY_MAX];
PackedByteArray vertex_buffer;
PackedByteArray attribute_buffer;
- uint32_t vertex_stride;
- uint32_t attrib_stride;
- uint32_t skin_stride;
- uint32_t mesh_surface_format;
+ uint32_t vertex_stride = 0;
+ uint32_t attrib_stride = 0;
+ uint32_t skin_stride = 0;
+ uint32_t mesh_surface_format = 0;
void _queue_update();
@@ -118,12 +121,12 @@ public:
void set_flip_v(bool p_flip);
bool is_flipped_v() const;
+ void set_render_priority(int p_priority);
+ int get_render_priority() const;
+
void set_modulate(const Color &p_color);
Color get_modulate() const;
- void set_opacity(float p_amount);
- float get_opacity() const;
-
void set_pixel_size(real_t p_amount);
real_t get_pixel_size() const;
@@ -135,13 +138,17 @@ public:
void set_alpha_cut_mode(AlphaCutMode p_mode);
AlphaCutMode get_alpha_cut_mode() const;
+
void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode);
StandardMaterial3D::BillboardMode get_billboard_mode() const;
+ void set_texture_filter(StandardMaterial3D::TextureFilter p_filter);
+ StandardMaterial3D::TextureFilter get_texture_filter() const;
+
virtual Rect2 get_item_rect() const = 0;
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
+
Ref<TriangleMesh> generate_triangle_mesh() const;
SpriteBase3D();
@@ -241,6 +248,8 @@ public:
virtual Rect2 get_item_rect() const override;
virtual TypedArray<String> get_configuration_warnings() const override;
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
+
AnimatedSprite3D();
};
diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp
index 90db093137..19c90cdeb5 100644
--- a/scene/3d/vehicle_body_3d.cpp
+++ b/scene/3d/vehicle_body_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,7 +40,7 @@ public:
Vector3 m_0MinvJt;
Vector3 m_1MinvJt;
//Optimization: can be stored in the w/last component of one of the vectors
- real_t m_Adiag;
+ real_t m_Adiag = 0.0;
real_t getDiagonal() const { return m_Adiag; }
@@ -90,8 +90,8 @@ void VehicleWheel3D::_notification(int p_what) {
cb->wheels.push_back(this);
m_chassisConnectionPointCS = get_transform().origin;
- m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized();
- m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized();
+ m_wheelDirectionCS = -get_transform().basis.get_column(Vector3::AXIS_Y).normalized();
+ m_wheelAxleCS = get_transform().basis.get_column(Vector3::AXIS_X).normalized();
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -109,16 +109,14 @@ TypedArray<String> VehicleWheel3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<VehicleBody3D>(get_parent())) {
- warnings.push_back(TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."));
+ warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."));
}
return warnings;
}
void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) {
- if (m_raycastInfo.m_isInContact)
-
- {
+ if (m_raycastInfo.m_isInContact) {
real_t project = m_raycastInfo.m_contactNormalWS.dot(m_raycastInfo.m_wheelDirectionWS);
Vector3 chassis_velocity_at_contactPoint;
Vector3 relpos = m_raycastInfo.m_contactPointWS - s->get_transform().origin;
@@ -135,11 +133,7 @@ void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) {
m_suspensionRelativeVelocity = projVel * inv;
m_clippedInvContactDotSuspension = inv;
}
-
- }
-
- else // Not in contact : position wheel in a nice (rest length) position
- {
+ } else { // Not in contact : position wheel in a nice (rest length) position
m_raycastInfo.m_suspensionLength = m_suspensionRestLength;
m_suspensionRelativeVelocity = real_t(0.0);
m_raycastInfo.m_contactNormalWS = -m_raycastInfo.m_wheelDirectionWS;
@@ -225,6 +219,10 @@ bool VehicleWheel3D::is_in_contact() const {
return m_raycastInfo.m_isInContact;
}
+Node3D *VehicleWheel3D::get_contact_body() const {
+ return m_raycastInfo.m_groundObject;
+}
+
void VehicleWheel3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "length"), &VehicleWheel3D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &VehicleWheel3D::get_radius);
@@ -257,6 +255,7 @@ void VehicleWheel3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_friction_slip"), &VehicleWheel3D::get_friction_slip);
ClassDB::bind_method(D_METHOD("is_in_contact"), &VehicleWheel3D::is_in_contact);
+ ClassDB::bind_method(D_METHOD("get_contact_body"), &VehicleWheel3D::get_contact_body);
ClassDB::bind_method(D_METHOD("set_roll_influence", "roll_influence"), &VehicleWheel3D::set_roll_influence);
ClassDB::bind_method(D_METHOD("get_roll_influence"), &VehicleWheel3D::get_roll_influence);
@@ -413,9 +412,8 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) {
ray_params.exclude = exclude;
ray_params.collision_mask = get_collision_mask();
- bool col = ss->intersect_ray(ray_params, rr);
-
wheel.m_raycastInfo.m_groundObject = nullptr;
+ bool col = ss->intersect_ray(ray_params, rr);
if (col) {
param = source.distance_to(rr.position) / source.distance_to(target);
@@ -686,7 +684,7 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) {
Basis wheelBasis0 = wheelInfo.m_worldTransform.basis; //get_global_transform().basis;
- m_axle.write[i] = wheelBasis0.get_axis(Vector3::AXIS_X);
+ m_axle.write[i] = wheelBasis0.get_column(Vector3::AXIS_X);
//m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS;
const Vector3 &surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS;
diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h
index a798c76c1f..4ef70f7764 100644
--- a/scene/3d/vehicle_body_3d.h
+++ b/scene/3d/vehicle_body_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -129,6 +129,8 @@ public:
bool is_in_contact() const;
+ Node3D *get_contact_body() const;
+
void set_roll_influence(real_t p_value);
real_t get_roll_influence() const;
@@ -160,7 +162,7 @@ class VehicleBody3D : public RigidDynamicBody3D {
real_t m_steeringValue = 0.0;
real_t m_currentVehicleSpeedKmHour = 0.0;
- Set<RID> exclude;
+ RBSet<RID> exclude;
Vector<Vector3> m_forwardWS;
Vector<Vector3> m_axle;
diff --git a/scene/3d/velocity_tracker_3d.cpp b/scene/3d/velocity_tracker_3d.cpp
index 200664a41b..5bfe519440 100644
--- a/scene/3d/velocity_tracker_3d.cpp
+++ b/scene/3d/velocity_tracker_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/velocity_tracker_3d.h b/scene/3d/velocity_tracker_3d.h
index 827c3f5bd8..7fdcacc9c1 100644
--- a/scene/3d/velocity_tracker_3d.h
+++ b/scene/3d/velocity_tracker_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp
index 3d0bc3df9c..41cd604a4f 100644
--- a/scene/3d/visible_on_screen_notifier_3d.cpp
+++ b/scene/3d/visible_on_screen_notifier_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -71,8 +71,11 @@ bool VisibleOnScreenNotifier3D::is_on_screen() const {
}
void VisibleOnScreenNotifier3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_EXIT_TREE) {
- on_screen = false;
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_EXIT_TREE: {
+ on_screen = false;
+ } break;
}
}
@@ -86,10 +89,6 @@ void VisibleOnScreenNotifier3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("screen_exited"));
}
-Vector<Face3> VisibleOnScreenNotifier3D::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
VisibleOnScreenNotifier3D::VisibleOnScreenNotifier3D() {
RID notifier = RS::get_singleton()->visibility_notifier_create();
RS::get_singleton()->visibility_notifier_set_aabb(notifier, aabb);
@@ -161,21 +160,23 @@ void VisibleOnScreenEnabler3D::_update_enable_mode(bool p_enable) {
}
}
void VisibleOnScreenEnabler3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
- node_id = ObjectID();
- Node *node = get_node(enable_node_path);
- if (node) {
- node_id = node->get_instance_id();
- node->set_process_mode(PROCESS_MODE_DISABLED);
- }
- }
+ node_id = ObjectID();
+ Node *node = get_node(enable_node_path);
+ if (node) {
+ node_id = node->get_instance_id();
+ node->set_process_mode(PROCESS_MODE_DISABLED);
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- node_id = ObjectID();
+ case NOTIFICATION_EXIT_TREE: {
+ node_id = ObjectID();
+ } break;
}
}
diff --git a/scene/3d/visible_on_screen_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h
index fb7137c4f0..fe17f1e444 100644
--- a/scene/3d/visible_on_screen_notifier_3d.h
+++ b/scene/3d/visible_on_screen_notifier_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,8 +57,6 @@ public:
virtual AABB get_aabb() const override;
bool is_on_screen() const;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
-
VisibleOnScreenNotifier3D();
~VisibleOnScreenNotifier3D();
};
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index d407592376..669017c4b4 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,6 +32,14 @@
#include "scene/scene_string_names.h"
+AABB VisualInstance3D::get_aabb() const {
+ AABB ret;
+ if (GDVIRTUAL_CALL(_get_aabb, ret)) {
+ return ret;
+ }
+ return AABB();
+}
+
AABB VisualInstance3D::get_transformed_aabb() const {
return get_global_transform().xform(get_aabb());
}
@@ -47,27 +55,21 @@ void VisualInstance3D::_update_visibility() {
void VisualInstance3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_WORLD: {
- // CHECK SKELETON => moving skeleton attaching logic to MeshInstance
- /*
- Skeleton *skeleton=Object::cast_to<Skeleton>(get_parent());
- if (skeleton)
- RenderingServer::get_singleton()->instance_attach_skeleton( instance, skeleton->get_skeleton() );
- */
ERR_FAIL_COND(get_world_3d().is_null());
RenderingServer::get_singleton()->instance_set_scenario(instance, get_world_3d()->get_scenario());
_update_visibility();
-
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
Transform3D gt = get_global_transform();
RenderingServer::get_singleton()->instance_set_transform(instance, gt);
} break;
+
case NOTIFICATION_EXIT_WORLD: {
RenderingServer::get_singleton()->instance_set_scenario(instance, RID());
RenderingServer::get_singleton()->instance_attach_skeleton(instance, RID());
- //RS::get_singleton()->instance_geometry_set_baked_light_sampler(instance, RID() );
-
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
_update_visibility();
} break;
@@ -121,6 +123,7 @@ void VisualInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance3D::get_transformed_aabb);
+ GDVIRTUAL_BIND(_get_aabb);
ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_3D_RENDER), "set_layer_mask", "get_layer_mask");
}
@@ -152,6 +155,15 @@ Ref<Material> GeometryInstance3D::get_material_override() const {
return material_override;
}
+void GeometryInstance3D::set_material_overlay(const Ref<Material> &p_material) {
+ material_overlay = p_material;
+ RS::get_singleton()->instance_geometry_set_material_overlay(get_instance(), p_material.is_valid() ? p_material->get_rid() : RID());
+}
+
+Ref<Material> GeometryInstance3D::get_material_overlay() const {
+ return material_overlay;
+}
+
void GeometryInstance3D::set_transparecy(float p_transparency) {
transparency = CLAMP(p_transparency, 0.0f, 1.0f);
RS::get_singleton()->instance_geometry_set_transparency(get_instance(), transparency);
@@ -184,6 +196,7 @@ float GeometryInstance3D::get_visibility_range_end() const {
void GeometryInstance3D::set_visibility_range_begin_margin(float p_dist) {
visibility_range_begin_margin = p_dist;
RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin, (RS::VisibilityRangeFadeMode)visibility_range_fade_mode);
+ update_configuration_warnings();
}
float GeometryInstance3D::get_visibility_range_begin_margin() const {
@@ -193,6 +206,7 @@ float GeometryInstance3D::get_visibility_range_begin_margin() const {
void GeometryInstance3D::set_visibility_range_end_margin(float p_dist) {
visibility_range_end_margin = p_dist;
RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin, (RS::VisibilityRangeFadeMode)visibility_range_fade_mode);
+ update_configuration_warnings();
}
float GeometryInstance3D::get_visibility_range_end_margin() const {
@@ -202,15 +216,13 @@ float GeometryInstance3D::get_visibility_range_end_margin() const {
void GeometryInstance3D::set_visibility_range_fade_mode(VisibilityRangeFadeMode p_mode) {
visibility_range_fade_mode = p_mode;
RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin, (RS::VisibilityRangeFadeMode)visibility_range_fade_mode);
+ update_configuration_warnings();
}
GeometryInstance3D::VisibilityRangeFadeMode GeometryInstance3D::get_visibility_range_fade_mode() const {
return visibility_range_fade_mode;
}
-void GeometryInstance3D::_notification(int p_what) {
-}
-
const StringName *GeometryInstance3D::_instance_uniform_get_remap(const StringName p_name) const {
StringName *r = instance_uniform_property_remap.getptr(p_name);
if (!r) {
@@ -235,7 +247,7 @@ bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value)
}
#ifndef DISABLE_DEPRECATED
if (p_name == SceneStringNames::get_singleton()->use_in_baked_light && bool(p_value)) {
- set_gi_mode(GI_MODE_BAKED);
+ set_gi_mode(GI_MODE_STATIC);
return true;
}
@@ -346,7 +358,7 @@ void GeometryInstance3D::set_gi_mode(GIMode p_mode) {
RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false);
RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, false);
} break;
- case GI_MODE_BAKED: {
+ case GI_MODE_STATIC: {
RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, true);
RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, false);
@@ -377,7 +389,15 @@ TypedArray<String> GeometryInstance3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) {
- warnings.push_back(TTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance."));
+ warnings.push_back(RTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance."));
+ }
+
+ if ((visibility_range_fade_mode == VISIBILITY_RANGE_FADE_SELF || visibility_range_fade_mode == VISIBILITY_RANGE_FADE_DEPENDENCIES) && !Math::is_zero_approx(visibility_range_begin) && Math::is_zero_approx(visibility_range_begin_margin)) {
+ warnings.push_back(RTR("The GeometryInstance3D is configured to fade in smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range Begin Margin above 0."));
+ }
+
+ if ((visibility_range_fade_mode == VISIBILITY_RANGE_FADE_SELF || visibility_range_fade_mode == VISIBILITY_RANGE_FADE_DEPENDENCIES) && !Math::is_zero_approx(visibility_range_end) && Math::is_zero_approx(visibility_range_end_margin)) {
+ warnings.push_back(RTR("The GeometryInstance3D is configured to fade out smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range End Margin above 0."));
}
return warnings;
@@ -387,6 +407,9 @@ void GeometryInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance3D::set_material_override);
ClassDB::bind_method(D_METHOD("get_material_override"), &GeometryInstance3D::get_material_override);
+ ClassDB::bind_method(D_METHOD("set_material_overlay", "material"), &GeometryInstance3D::set_material_overlay);
+ ClassDB::bind_method(D_METHOD("get_material_overlay"), &GeometryInstance3D::get_material_overlay);
+
ClassDB::bind_method(D_METHOD("set_cast_shadows_setting", "shadow_casting_setting"), &GeometryInstance3D::set_cast_shadows_setting);
ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &GeometryInstance3D::get_cast_shadows_setting);
@@ -432,13 +455,14 @@ void GeometryInstance3D::_bind_methods() {
ADD_GROUP("Geometry", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_overlay", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_overlay", "get_material_overlay");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "transparency", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_transparency", "get_transparency");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "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,Baked,Dynamic"), "set_gi_mode", "get_gi_mode");
+ 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_");
@@ -455,7 +479,7 @@ void GeometryInstance3D::_bind_methods() {
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY);
BIND_ENUM_CONSTANT(GI_MODE_DISABLED);
- BIND_ENUM_CONSTANT(GI_MODE_BAKED);
+ BIND_ENUM_CONSTANT(GI_MODE_STATIC);
BIND_ENUM_CONSTANT(GI_MODE_DYNAMIC);
BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_1X);
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index acdbc4666a..f8fd4378fe 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,6 @@
class VisualInstance3D : public Node3D {
GDCLASS(VisualInstance3D, Node3D);
- OBJ_CATEGORY("3D Visual Nodes");
RID base;
RID instance;
@@ -49,6 +48,7 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ GDVIRTUAL0RC(AABB, _get_aabb)
public:
enum GetFacesFlags {
FACES_SOLID = 1, // solid geometry
@@ -58,8 +58,7 @@ public:
};
RID get_instance() const;
- virtual AABB get_aabb() const = 0;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const = 0;
+ virtual AABB get_aabb() const;
virtual AABB get_transformed_aabb() const; // helper
@@ -89,7 +88,7 @@ public:
enum GIMode {
GI_MODE_DISABLED,
- GI_MODE_BAKED,
+ GI_MODE_STATIC,
GI_MODE_DYNAMIC
};
@@ -110,6 +109,7 @@ public:
private:
ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON;
Ref<Material> material_override;
+ Ref<Material> material_overlay;
float visibility_range_begin = 0.0;
float visibility_range_end = 0.0;
@@ -136,7 +136,6 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
- void _notification(int p_what);
static void _bind_methods();
public:
@@ -164,6 +163,9 @@ public:
void set_material_override(const Ref<Material> &p_material);
Ref<Material> get_material_override() const;
+ void set_material_overlay(const Ref<Material> &p_material);
+ Ref<Material> get_material_overlay() const;
+
void set_extra_cull_margin(float p_margin);
float get_extra_cull_margin() const;
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index c7108cbae0..29e495ce1b 100644
--- a/scene/3d/voxel_gi.cpp
+++ b/scene/3d/voxel_gi.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -227,7 +227,7 @@ void VoxelGIData::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "dynamic_range", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_dynamic_range", "get_dynamic_range");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dynamic_range", PROPERTY_HINT_RANGE, "1,8,0.01"), "set_dynamic_range", "get_dynamic_range");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_energy", "get_energy");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_bias", "get_bias");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_bias", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_normal_bias", "get_normal_bias");
@@ -282,7 +282,7 @@ Vector3 VoxelGI::get_extents() const {
void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
- if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) {
+ if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) {
Ref<Mesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
AABB aabb = mesh->get_aabb();
@@ -450,17 +450,13 @@ AABB VoxelGI::get_aabb() const {
return AABB(-extents, extents * 2);
}
-Vector<Face3> VoxelGI::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
TypedArray<String> VoxelGI::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("VoxelGIs are not supported by the OpenGL video driver.\nUse a LightmapGI instead."));
+ warnings.push_back(RTR("VoxelGIs are not supported by the OpenGL video driver.\nUse a LightmapGI instead."));
} else if (probe_data.is_null()) {
- warnings.push_back(TTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI."));
+ warnings.push_back(RTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI."));
}
return warnings;
}
diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h
index 5d0dda1ba3..e1a38dd7a0 100644
--- a/scene/3d/voxel_gi.h
+++ b/scene/3d/voxel_gi.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,7 +45,7 @@ class VoxelGIData : public Resource {
AABB bounds;
Vector3 octree_size;
- float dynamic_range = 4.0;
+ float dynamic_range = 2.0;
float energy = 1.0;
float bias = 1.5;
float normal_bias = 0.0;
@@ -149,7 +149,6 @@ public:
void bake(Node *p_from_node = nullptr, bool p_create_visual_debug = false);
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
TypedArray<String> get_configuration_warnings() const override;
diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp
index aa1236521d..42a2a68e2d 100644
--- a/scene/3d/voxelizer.cpp
+++ b/scene/3d/voxelizer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -592,7 +592,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) {
bake_cells.write[p_idx].albedo[2] = 0;
float alpha_average = 0;
- int children_found = 0;
for (int i = 0; i < 8; i++) {
uint32_t child = bake_cells[p_idx].children[i];
@@ -603,8 +602,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) {
_fixup_plot(child, p_level + 1);
alpha_average += bake_cells[child].alpha;
-
- children_found++;
}
bake_cells.write[p_idx].alpha = alpha_average / 8.0;
@@ -777,8 +774,8 @@ Vector<int> Voxelizer::get_voxel_gi_level_cell_count() const {
/* dt of 1d function using squared distance */
static void edt(float *f, int stride, int n) {
float *d = (float *)alloca(sizeof(float) * n + sizeof(int) * n + sizeof(float) * (n + 1));
- int *v = (int *)&(d[n]);
- float *z = (float *)&v[n];
+ int *v = reinterpret_cast<int *>(&(d[n]));
+ float *z = reinterpret_cast<float *>(&v[n]);
int k = 0;
v[0] = 0;
@@ -872,7 +869,7 @@ Vector<uint8_t> Voxelizer::get_sdf_3d_image() const {
if (d == 0) {
w[i] = 0;
} else {
- w[i] = MIN(d, 254) + 1;
+ w[i] = MIN(d, 254u) + 1;
}
}
}
diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h
index 09c126bc4e..0179795ddc 100644
--- a/scene/3d/voxelizer.h
+++ b/scene/3d/voxelizer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -86,7 +86,7 @@ private:
Vector<Color> emission;
};
- Map<Ref<Material>, MaterialCache> material_cache;
+ HashMap<Ref<Material>, MaterialCache> material_cache;
AABB original_bounds;
AABB po2_bounds;
int axis_cell_size[3] = {};
diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp
index 26fa43b969..fe9d9ae4dd 100644
--- a/scene/3d/world_environment.cpp
+++ b/scene/3d/world_environment.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,27 +34,32 @@
#include "scene/main/window.h"
void WorldEnvironment::_notification(int p_what) {
- if (p_what == Node3D::NOTIFICATION_ENTER_WORLD || p_what == Node3D::NOTIFICATION_ENTER_TREE) {
- if (environment.is_valid()) {
- add_to_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()));
- _update_current_environment();
- }
-
- if (camera_effects.is_valid()) {
- add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()));
- _update_current_camera_effects();
- }
-
- } else if (p_what == Node3D::NOTIFICATION_EXIT_WORLD || p_what == Node3D::NOTIFICATION_EXIT_TREE) {
- if (environment.is_valid()) {
- remove_from_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()));
- _update_current_environment();
- }
-
- if (camera_effects.is_valid()) {
- remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()));
- _update_current_camera_effects();
- }
+ switch (p_what) {
+ case Node3D::NOTIFICATION_ENTER_WORLD:
+ case Node3D::NOTIFICATION_ENTER_TREE: {
+ if (environment.is_valid()) {
+ add_to_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()));
+ _update_current_environment();
+ }
+
+ if (camera_effects.is_valid()) {
+ add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()));
+ _update_current_camera_effects();
+ }
+ } break;
+
+ case Node3D::NOTIFICATION_EXIT_WORLD:
+ case Node3D::NOTIFICATION_EXIT_TREE: {
+ if (environment.is_valid()) {
+ remove_from_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()));
+ _update_current_environment();
+ }
+
+ if (camera_effects.is_valid()) {
+ remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()));
+ _update_current_camera_effects();
+ }
+ } break;
}
}
@@ -66,7 +71,7 @@ void WorldEnvironment::_update_current_environment() {
} else {
get_viewport()->find_world_3d()->set_environment(Ref<Environment>());
}
- get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
}
void WorldEnvironment::_update_current_camera_effects() {
@@ -77,7 +82,7 @@ void WorldEnvironment::_update_current_camera_effects() {
get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>());
}
- get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings");
}
void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) {
@@ -133,8 +138,8 @@ Ref<CameraEffects> WorldEnvironment::get_camera_effects() const {
TypedArray<String> WorldEnvironment::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
- if (!environment.is_valid()) {
- warnings.push_back(TTR("WorldEnvironment requires its \"Environment\" property to contain an Environment to have a visible effect."));
+ if (!environment.is_valid() && !camera_effects.is_valid()) {
+ warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Effects\" property to contain a CameraEffects resource, or both."));
}
if (!is_inside_tree()) {
@@ -146,7 +151,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const {
}
if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) {
- warnings.push_back(TTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes)."));
+ warnings.push_back(RTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes)."));
}
return warnings;
diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h
index 310d1e96a5..8dbb57364c 100644
--- a/scene/3d/world_environment.h
+++ b/scene/3d/world_environment.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index a16820cbdc..1dad6078b4 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,7 @@
#include "xr_nodes.h"
+#include "core/config/project_settings.h"
#include "scene/main/viewport.h"
#include "servers/xr/xr_interface.h"
@@ -43,16 +44,17 @@ void XRCamera3D::_notification(int p_what) {
if (origin != nullptr) {
origin->set_tracked_camera(this);
}
- }; break;
+ } break;
+
case NOTIFICATION_EXIT_TREE: {
// need to find our XROrigin3D parent and let it know we're no longer its camera!
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin != nullptr && origin->get_tracked_camera() == this) {
origin->set_tracked_camera(nullptr);
}
- }; break;
- };
-};
+ } break;
+ }
+}
void XRCamera3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) {
if (p_tracker_name == tracker_name) {
@@ -93,7 +95,7 @@ TypedArray<String> XRCamera3D::get_configuration_warnings() const {
// must be child node of XROrigin3D!
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
- warnings.push_back(TTR("XRCamera3D must have an XROrigin3D node as its parent."));
+ warnings.push_back(RTR("XRCamera3D must have an XROrigin3D node as its parent."));
};
}
@@ -421,15 +423,15 @@ TypedArray<String> XRNode3D::get_configuration_warnings() const {
// must be child node of XROrigin!
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
- warnings.push_back(TTR("XRController3D must have an XROrigin3D node as its parent."));
+ warnings.push_back(RTR("XRController3D must have an XROrigin3D node as its parent."));
}
if (tracker_name == "") {
- warnings.push_back(TTR("No tracker name is set."));
+ warnings.push_back(RTR("No tracker name is set."));
}
if (pose_name == "") {
- warnings.push_back(TTR("No pose is set."));
+ warnings.push_back(RTR("No pose is set."));
}
}
@@ -446,11 +448,6 @@ void XRController3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand);
- ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble);
- ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble");
- ADD_PROPERTY_DEFAULT("rumble", 0.0);
-
ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name")));
ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name")));
ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value")));
@@ -482,22 +479,22 @@ void XRController3D::_unbind_tracker() {
void XRController3D::_button_pressed(const String &p_name) {
// just pass it on...
- emit_signal("button_pressed", p_name);
+ emit_signal(SNAME("button_pressed"), p_name);
}
void XRController3D::_button_released(const String &p_name) {
// just pass it on...
- emit_signal("button_released", p_name);
+ emit_signal(SNAME("button_released"), p_name);
}
void XRController3D::_input_value_changed(const String &p_name, float p_value) {
// just pass it on...
- emit_signal("input_value_changed", p_name, p_value);
+ emit_signal(SNAME("input_value_changed"), p_name, p_value);
}
void XRController3D::_input_axis_changed(const String &p_name, Vector2 p_value) {
// just pass it on...
- emit_signal("input_axis_changed", p_name, p_value);
+ emit_signal(SNAME("input_axis_changed"), p_name, p_value);
}
bool XRController3D::is_button_pressed(const StringName &p_name) const {
@@ -556,20 +553,6 @@ Vector2 XRController3D::get_axis(const StringName &p_name) const {
}
}
-real_t XRController3D::get_rumble() const {
- if (!tracker.is_valid()) {
- return 0.0;
- }
-
- return tracker->get_rumble();
-}
-
-void XRController3D::set_rumble(real_t p_rumble) {
- if (tracker.is_valid()) {
- tracker->set_rumble(p_rumble);
- }
-}
-
XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const {
// get our XRServer
if (!tracker.is_valid()) {
@@ -594,7 +577,7 @@ Plane XRAnchor3D::get_plane() const {
Vector3 location = get_position();
Basis orientation = get_transform().basis;
- Plane plane(orientation.get_axis(1).normalized(), location);
+ Plane plane(orientation.get_column(1).normalized(), location);
return plane;
}
@@ -606,13 +589,13 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const {
if (is_visible() && is_inside_tree()) {
if (tracked_camera == nullptr) {
- warnings.push_back(TTR("XROrigin3D requires an XRCamera3D child node."));
+ warnings.push_back(RTR("XROrigin3D requires an XRCamera3D child node."));
}
}
- bool xr_enabled = GLOBAL_GET("rendering/xr/enabled");
+ bool xr_enabled = GLOBAL_GET("xr/shaders/enabled");
if (!xr_enabled) {
- warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
+ warnings.push_back(RTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
}
return warnings;
@@ -656,10 +639,12 @@ void XROrigin3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
set_process_internal(true);
- }; break;
+ } break;
+
case NOTIFICATION_EXIT_TREE: {
set_process_internal(false);
- }; break;
+ } break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
// set our world origin to our node transform
xr_server->set_world_origin(get_global_transform());
@@ -672,11 +657,9 @@ void XROrigin3D::_notification(int p_what) {
// now apply this to our camera
tracked_camera->set_transform(t);
- };
- }; break;
- default:
- break;
- };
+ }
+ } break;
+ }
// send our notification to all active XE interfaces, they may need to react to it also
for (int i = 0; i < xr_server->get_interface_count(); i++) {
diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h
index 5e7d06093d..3079e20dc7 100644
--- a/scene/3d/xr_nodes.h
+++ b/scene/3d/xr_nodes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,13 +34,10 @@
#include "scene/3d/camera_3d.h"
#include "servers/xr/xr_positional_tracker.h"
-/**
- @author Bastiaan Olij <mux213@gmail.com>
-**/
-
/*
XRCamera is a subclass of camera which will register itself with its parent XROrigin and as a result is automatically positioned
*/
+
class XRCamera3D : public Camera3D {
GDCLASS(XRCamera3D, Camera3D);
@@ -142,9 +139,6 @@ public:
float get_value(const StringName &p_name) const;
Vector2 get_axis(const StringName &p_name) const;
- real_t get_rumble() const;
- void set_rumble(real_t p_rumble);
-
XRPositionalTracker::TrackerHand get_tracker_hand() const;
XRController3D() {}
@@ -181,6 +175,7 @@ public:
Our camera and controllers will always be child nodes and thus place relative to this origin point.
This node will automatically locate any camera child nodes and update its position while our XRController3D node will handle tracked controllers.
*/
+
class XROrigin3D : public Node3D {
GDCLASS(XROrigin3D, Node3D);
@@ -204,4 +199,4 @@ public:
~XROrigin3D() {}
};
-#endif /* XR_NODES_H */
+#endif // XR_NODES_H
diff --git a/scene/SCsub b/scene/SCsub
index 92288211bb..a7b23af598 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -9,6 +9,7 @@ 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 9a71e7bf55..849316c568 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -292,10 +292,10 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
// actually blend the animations now
- float max_time_remaining = 0.0;
+ double max_time_remaining = 0.0;
for (int i = 0; i < blend_points_used; i++) {
- float remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
+ double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
max_time_remaining = MAX(max_time_remaining, remaining);
}
diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h
index 6730c09fd4..7038cece06 100644
--- a/scene/animation/animation_blend_space_1d.h
+++ b/scene/animation/animation_blend_space_1d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index e621f06ce9..a3aa3f6cc8 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -134,7 +134,7 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
}
}
if (erase) {
- triangles.remove(i);
+ triangles.remove_at(i);
i--;
}
@@ -224,7 +224,7 @@ int AnimationNodeBlendSpace2D::get_triangle_point(int p_triangle, int p_point) {
void AnimationNodeBlendSpace2D::remove_triangle(int p_triangle) {
ERR_FAIL_INDEX(p_triangle, triangles.size());
- triangles.remove(p_triangle);
+ triangles.remove_at(p_triangle);
}
int AnimationNodeBlendSpace2D::get_triangle_count() const {
@@ -438,7 +438,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
Vector2 blend_pos = get_parameter(blend_position);
int closest = get_parameter(this->closest);
double length_internal = get_parameter(this->length_internal);
- float mind = 0.0; //time of min distance point
+ double mind = 0.0; //time of min distance point
if (blend_mode == BLEND_MODE_INTERPOLATED) {
if (triangles.size() == 0) {
@@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
for (int j = 0; j < 3; j++) {
if (i == triangle_points[j]) {
//blend with the given weight
- float t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
+ double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
if (first || t < mind) {
mind = t;
first = false;
@@ -530,7 +530,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
}
if (new_closest != closest && new_closest != -1) {
- float from = 0.0;
+ double from = 0.0;
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && closest != -1) {
//for ping-pong loop
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[closest].node);
diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h
index a919fff1d2..1356656bf8 100644
--- a/scene/animation/animation_blend_space_2d.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index d6c5d0b51c..433f21f91f 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,7 +57,7 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const {
}
anims += String(names[i]);
}
- if (anims != String()) {
+ if (!anims.is_empty()) {
property.hint = PROPERTY_HINT_ENUM;
property.hint_string = anims;
}
@@ -371,10 +371,12 @@ void AnimationNodeOneShot::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync);
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode");
+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadein_time", "get_fadein_time");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadeout_time", "get_fadeout_time");
- ADD_GROUP("autorestart_", "Auto Restart");
+ ADD_GROUP("Auto Restart", "autorestart_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_autorestart_delay", "get_autorestart_delay");
@@ -748,7 +750,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
return 0;
}
- float rem = 0.0;
+ double rem = 0.0;
if (prev < 0) { // process current animation, check for transition
@@ -855,7 +857,7 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
ERR_FAIL_COND(nodes.has(p_name));
ERR_FAIL_COND(p_node.is_null());
ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output);
- ERR_FAIL_COND(String(p_name).find("/") != -1);
+ ERR_FAIL_COND(String(p_name).contains("/"));
Node n;
n.node = p_node;
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index e55dfb58ed..73bde633cb 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -354,7 +354,7 @@ class AnimationNodeBlendTree : public AnimationRootNode {
Vector<StringName> connections;
};
- Map<StringName, Node> nodes;
+ HashMap<StringName, Node> nodes;
Vector2 graph_offset;
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index b5d7a0555e..81df12791c 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -50,9 +50,9 @@ bool AnimationNodeStateMachineTransition::has_auto_advance() const {
void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) {
String cs = p_condition;
- ERR_FAIL_COND(cs.find("/") != -1 || cs.find(":") != -1);
+ ERR_FAIL_COND(cs.contains("/") || cs.contains(":"));
advance_condition = p_condition;
- if (cs != String()) {
+ if (!cs.is_empty()) {
advance_condition_name = "conditions/" + cs;
} else {
advance_condition_name = StringName();
@@ -185,27 +185,29 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
return true; //nothing to do
}
- loops_current = 0; // reset loops, so fade does not happen immediately
-
Vector2 current_pos = p_state_machine->states[current].position;
Vector2 target_pos = p_state_machine->states[p_travel].position;
- Map<StringName, AStarCost> cost_map;
+ HashMap<StringName, AStarCost> cost_map;
List<int> open_list;
//build open list
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].from == current) {
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (p_state_machine->transitions[i].local_from == current) {
open_list.push_back(i);
- float cost = p_state_machine->states[p_state_machine->transitions[i].to].position.distance_to(current_pos);
+ float cost = p_state_machine->states[p_state_machine->transitions[i].local_to].position.distance_to(current_pos);
cost *= p_state_machine->transitions[i].transition->get_priority();
AStarCost ap;
ap.prev = current;
ap.distance = cost;
- cost_map[p_state_machine->transitions[i].to] = ap;
+ cost_map[p_state_machine->transitions[i].local_to] = ap;
- if (p_state_machine->transitions[i].to == p_travel) { //prematurely found it! :D
+ if (p_state_machine->transitions[i].local_to == p_travel) { //prematurely found it! :D
path.push_back(p_travel);
return true;
}
@@ -224,8 +226,8 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
float least_cost = 1e20;
for (List<int>::Element *E = open_list.front(); E; E = E->next()) {
- float cost = cost_map[p_state_machine->transitions[E->get()].to].distance;
- cost += p_state_machine->states[p_state_machine->transitions[E->get()].to].position.distance_to(target_pos);
+ float cost = cost_map[p_state_machine->transitions[E->get()].local_to].distance;
+ cost += p_state_machine->states[p_state_machine->transitions[E->get()].local_to].position.distance_to(target_pos);
if (cost < least_cost) {
least_cost_transition = E;
@@ -233,34 +235,38 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
}
}
- StringName transition_prev = p_state_machine->transitions[least_cost_transition->get()].from;
- StringName transition = p_state_machine->transitions[least_cost_transition->get()].to;
+ StringName transition_prev = p_state_machine->transitions[least_cost_transition->get()].local_from;
+ StringName transition = p_state_machine->transitions[least_cost_transition->get()].local_to;
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].from != transition || p_state_machine->transitions[i].to == transition_prev) {
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (p_state_machine->transitions[i].local_from != transition || p_state_machine->transitions[i].local_to == transition_prev) {
continue; //not interested on those
}
- float distance = p_state_machine->states[p_state_machine->transitions[i].from].position.distance_to(p_state_machine->states[p_state_machine->transitions[i].to].position);
+ float distance = p_state_machine->states[p_state_machine->transitions[i].local_from].position.distance_to(p_state_machine->states[p_state_machine->transitions[i].local_to].position);
distance *= p_state_machine->transitions[i].transition->get_priority();
- distance += cost_map[p_state_machine->transitions[i].from].distance;
+ distance += cost_map[p_state_machine->transitions[i].local_from].distance;
- if (cost_map.has(p_state_machine->transitions[i].to)) {
+ if (cost_map.has(p_state_machine->transitions[i].local_to)) {
//oh this was visited already, can we win the cost?
- if (distance < cost_map[p_state_machine->transitions[i].to].distance) {
- cost_map[p_state_machine->transitions[i].to].distance = distance;
- cost_map[p_state_machine->transitions[i].to].prev = p_state_machine->transitions[i].from;
+ if (distance < cost_map[p_state_machine->transitions[i].local_to].distance) {
+ cost_map[p_state_machine->transitions[i].local_to].distance = distance;
+ cost_map[p_state_machine->transitions[i].local_to].prev = p_state_machine->transitions[i].local_from;
}
} else {
//add to open list
AStarCost ac;
- ac.prev = p_state_machine->transitions[i].from;
+ ac.prev = p_state_machine->transitions[i].local_from;
ac.distance = distance;
- cost_map[p_state_machine->transitions[i].to] = ac;
+ cost_map[p_state_machine->transitions[i].local_to] = ac;
open_list.push_back(i);
- if (p_state_machine->transitions[i].to == p_travel) {
+ if (p_state_machine->transitions[i].local_to == p_travel) {
found_route = true;
break;
}
@@ -352,7 +358,6 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false);
pos_current = 0;
- loops_current = 0;
}
if (!p_state_machine->states.has(current)) {
@@ -388,12 +393,8 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
{ //advance and loop check
-
float next_pos = len_current - rem;
-
- if (next_pos < pos_current) {
- loops_current++;
- }
+ end_loop = next_pos < pos_current;
pos_current = next_pos; //looped
}
@@ -404,7 +405,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (path.size()) {
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].from == current && p_state_machine->transitions[i].to == path[0]) {
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (p_state_machine->transitions[i].local_from == current && p_state_machine->transitions[i].local_to == path[0]) {
next_xfade = p_state_machine->transitions[i].transition->get_xfade_time();
switch_mode = p_state_machine->transitions[i].transition->get_switch_mode();
next = path[0];
@@ -413,17 +418,39 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
} else {
float priority_best = 1e20;
int auto_advance_to = -1;
+
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- bool auto_advance = false;
- if (p_state_machine->transitions[i].transition->has_auto_advance()) {
- auto_advance = true;
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
}
- StringName advance_condition_name = p_state_machine->transitions[i].transition->get_advance_condition_name();
- if (advance_condition_name != StringName() && bool(p_state_machine->get_parameter(advance_condition_name))) {
- auto_advance = true;
+
+ // handles end_node: when end_node is reached in a sub state machine, find and activate the current_transition
+ if (force_auto_advance) {
+ if (p_state_machine->transitions[i].from == current_transition.from && p_state_machine->transitions[i].to == current_transition.to) {
+ auto_advance_to = i;
+ force_auto_advance = false;
+ break;
+ }
}
- if (p_state_machine->transitions[i].from == current && auto_advance) {
+ // handles start_node: if previous state machine is pointing to a node inside the current state machine, starts the current machine from start_node to prev_local_to
+ if (p_state_machine->start_node == current && p_state_machine->transitions[i].local_from == current) {
+ if (p_state_machine->prev_state_machine.is_valid()) {
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback");
+
+ if (prev_playback.is_valid()) {
+ StringName prev_local_to = String(prev_playback->current_transition.next).replace_first(String(p_state_machine->state_machine_name) + "/", "");
+
+ if (p_state_machine->transitions[i].to == prev_local_to) {
+ auto_advance_to = i;
+ prev_playback->current_transition.next = StringName();
+ break;
+ }
+ }
+ }
+ }
+
+ if (p_state_machine->transitions[i].from == current && _check_advance_condition(p_state_machine, p_state_machine->transitions[i].transition)) {
if (p_state_machine->transitions[i].transition->get_priority() <= priority_best) {
priority_best = p_state_machine->transitions[i].transition->get_priority();
auto_advance_to = i;
@@ -432,26 +459,69 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
if (auto_advance_to != -1) {
- next = p_state_machine->transitions[auto_advance_to].to;
+ next = p_state_machine->transitions[auto_advance_to].local_to;
+ Transition tr;
+ tr.from = String(p_state_machine->state_machine_name) + "/" + String(p_state_machine->transitions[auto_advance_to].from);
+ tr.to = String(p_state_machine->transitions[auto_advance_to].to).replace_first("../", "");
+ tr.next = p_state_machine->transitions[auto_advance_to].to;
+ current_transition = tr;
next_xfade = p_state_machine->transitions[auto_advance_to].transition->get_xfade_time();
switch_mode = p_state_machine->transitions[auto_advance_to].transition->get_switch_mode();
}
}
+ if (next == p_state_machine->end_node) {
+ Ref<AnimationNodeStateMachine> prev_state_machine = p_state_machine->prev_state_machine;
+
+ if (prev_state_machine.is_valid()) {
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter("playback");
+
+ if (prev_playback.is_valid()) {
+ if (next_xfade) {
+ prev_playback->current_transition = current_transition;
+ prev_playback->force_auto_advance = true;
+
+ return rem;
+ }
+ float priority_best = 1e20;
+ int auto_advance_to = -1;
+
+ for (int i = 0; i < prev_state_machine->transitions.size(); i++) {
+ if (prev_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (current_transition.next == prev_state_machine->end_node && _check_advance_condition(prev_state_machine, prev_state_machine->transitions[i].transition)) {
+ if (prev_state_machine->transitions[i].transition->get_priority() <= priority_best) {
+ priority_best = prev_state_machine->transitions[i].transition->get_priority();
+ auto_advance_to = i;
+ }
+ }
+ }
+
+ if (auto_advance_to != -1) {
+ if (prev_state_machine->transitions[auto_advance_to].transition->get_xfade_time()) {
+ return rem;
+ }
+ }
+ }
+ }
+ }
+
//if next, see when to transition
if (next != StringName()) {
bool goto_next = false;
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_AT_END) {
- goto_next = next_xfade >= (len_current - pos_current) || loops_current > 0;
- if (loops_current > 0) {
+ goto_next = next_xfade >= (len_current - pos_current) || end_loop;
+ if (end_loop) {
next_xfade = 0;
}
} else {
goto_next = fading_from == StringName();
}
- if (goto_next) { //loops should be used because fade time may be too small or zero and animation may have looped
+ if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped
if (next_xfade) {
//time to fade, baby
@@ -464,7 +534,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
if (path.size()) { //if it came from path, remove path
- path.remove(0);
+ path.remove_at(0);
}
current = next;
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
@@ -478,18 +548,38 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
rem = len_current; //so it does not show 0 on transition
- loops_current = 0;
}
}
- //compute time left for transitions by using the end node
- if (p_state_machine->end_node != StringName() && p_state_machine->end_node != current) {
- rem = p_state_machine->blend_node(p_state_machine->end_node, p_state_machine->states[p_state_machine->end_node].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+ // time left must always be 1 because the end node don't lenght to compute
+ if (p_state_machine->end_node != current) {
+ rem = 1;
+ } else {
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback");
+
+ if (prev_playback.is_valid()) {
+ prev_playback->current_transition = current_transition;
+ prev_playback->force_auto_advance = true;
+ }
}
return rem;
}
+bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<AnimationNodeStateMachine> state_machine, const Ref<AnimationNodeStateMachineTransition> transition) const {
+ if (transition->has_auto_advance()) {
+ return true;
+ }
+
+ StringName advance_condition_name = transition->get_advance_condition_name();
+
+ if (advance_condition_name != StringName() && bool(state_machine->get_parameter(advance_condition_name))) {
+ return true;
+ }
+
+ return false;
+}
+
void AnimationNodeStateMachinePlayback::_bind_methods() {
ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel);
ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start);
@@ -521,6 +611,23 @@ void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) c
for (const StringName &E : advance_conditions) {
r_list->push_back(PropertyInfo(Variant::BOOL, E));
}
+
+ // for (const KeyValue<StringName, State> &E : states) {
+ // if (E->node == ansm) {
+ // for (int i = 0; i < E->node->transitions.size(); i++) {
+ // StringName ac = E->node->transitions[i].transition->get_advance_condition_name();
+ // if (ac != StringName() && advance_conditions.find(ac) == nullptr) {
+ // advance_conditions.push_back(ac);
+ // }
+ // }
+
+ // advance_conditions.sort_custom<StringName::AlphCompare>();
+
+ // for (const StringName &E : advance_conditions) {
+ // r_list->push_back(PropertyInfo(Variant::BOOL, E));
+ // }
+ // }
+ // }
}
Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const {
@@ -536,7 +643,7 @@ Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName
void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) {
ERR_FAIL_COND(states.has(p_name));
ERR_FAIL_COND(p_node.is_null());
- ERR_FAIL_COND(String(p_name).find("/") != -1);
+ ERR_FAIL_COND(String(p_name).contains("/"));
State state;
state.node = p_node;
@@ -544,6 +651,13 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
states[p_name] = state;
+ Ref<AnimationNodeStateMachine> anodesm = p_node;
+
+ if (anodesm.is_valid()) {
+ anodesm->state_machine_name = p_name;
+ anodesm->prev_state_machine = (Ref<AnimationNodeStateMachine>)this;
+ }
+
emit_changed();
emit_signal(SNAME("tree_changed"));
@@ -553,7 +667,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
ERR_FAIL_COND(states.has(p_name) == false);
ERR_FAIL_COND(p_node.is_null());
- ERR_FAIL_COND(String(p_name).find("/") != -1);
+ ERR_FAIL_COND(String(p_name).contains("/"));
{
Ref<AnimationNode> node = states[p_name].node;
@@ -570,6 +684,14 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
+bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const {
+ if (states.has(p_name)) {
+ return !(states[p_name].node->is_class("AnimationNodeStartState") || states[p_name].node->is_class("AnimationNodeEndState"));
+ }
+
+ return true;
+}
+
Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const {
ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>());
@@ -610,36 +732,24 @@ bool AnimationNodeStateMachine::has_node(const StringName &p_name) const {
void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
ERR_FAIL_COND(!states.has(p_name));
- {
- Ref<AnimationNode> node = states[p_name].node;
-
- ERR_FAIL_COND(node.is_null());
-
- node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
+ if (!can_edit_node(p_name)) {
+ return;
}
- states.erase(p_name);
- //path.erase(p_name);
-
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_name || transitions[i].to == p_name) {
- transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
- transitions.remove(i);
+ if (transitions[i].local_from == p_name || transitions[i].local_to == p_name) {
+ remove_transition_by_index(i);
i--;
}
}
- if (start_node == p_name) {
- start_node = StringName();
- }
-
- if (end_node == p_name) {
- end_node = StringName();
+ {
+ Ref<AnimationNode> node = states[p_name].node;
+ ERR_FAIL_COND(node.is_null());
+ node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
}
- /*if (playing && current == p_name) {
- stop();
- }*/
+ states.erase(p_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
@@ -648,39 +758,73 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) {
ERR_FAIL_COND(!states.has(p_name));
ERR_FAIL_COND(states.has(p_new_name));
+ ERR_FAIL_COND(!can_edit_node(p_name));
states[p_new_name] = states[p_name];
states.erase(p_name);
+ Ref<AnimationNodeStateMachine> anodesm = states[p_new_name].node;
+ if (anodesm.is_valid()) {
+ anodesm->state_machine_name = p_new_name;
+ }
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_name) {
- transitions.write[i].from = p_new_name;
+ if (transitions[i].local_from == p_name) {
+ _rename_transition(transitions[i].from, String(transitions[i].from).replace_first(p_name, p_new_name));
}
- if (transitions[i].to == p_name) {
- transitions.write[i].to = p_new_name;
+ if (transitions[i].local_to == p_name) {
+ _rename_transition(transitions[i].to, String(transitions[i].to).replace_first(p_name, p_new_name));
}
}
- if (start_node == p_name) {
- start_node = p_new_name;
- }
+ emit_signal("tree_changed");
+}
- if (end_node == p_name) {
- end_node = p_new_name;
+void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, const StringName &p_new_name) {
+ if (updating_transitions) {
+ return;
}
- /*if (playing && current == p_name) {
- current = p_new_name;
- }*/
+ updating_transitions = true;
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == p_name) {
+ Vector<String> path = String(transitions[i].to).split("/");
+ if (path.size() > 1) {
+ if (path[0] == "..") {
+ prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_to].node)->_rename_transition("../" + p_name, "../" + p_new_name);
+ }
+ }
+
+ transitions.write[i].from = p_new_name;
+ }
- //path.clear(); //clear path
- emit_signal(SNAME("tree_changed"));
+ if (transitions[i].to == p_name) {
+ Vector<String> path = String(transitions[i].from).split("/");
+ if (path.size() > 1) {
+ if (path[0] == "..") {
+ prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_from].node)->_rename_transition("../" + p_name, "../" + p_new_name);
+ }
+ }
+
+ transitions.write[i].to = p_new_name;
+ }
+
+ updating_transitions = false;
+ }
}
void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
List<StringName> nodes;
for (const KeyValue<StringName, State> &E : states) {
+ if (E.key == end_node && !prev_state_machine.is_valid()) {
+ continue;
+ }
+
nodes.push_back(E.key);
}
nodes.sort_custom<StringName::AlphCompare>();
@@ -690,9 +834,16 @@ void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
}
}
+Ref<AnimationNodeStateMachine> AnimationNodeStateMachine::get_prev_state_machine() const {
+ return prev_state_machine;
+}
+
bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
+ if (transitions[i].from == from && transitions[i].to == to) {
return true;
}
}
@@ -700,32 +851,148 @@ bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const S
}
int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
+ if (transitions[i].from == from && transitions[i].to == to) {
return i;
}
}
return -1;
}
+bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<Ref<AnimationNodeStateMachine>> p_parents) const {
+ if (p_parents.is_empty()) {
+ Ref<AnimationNodeStateMachine> prev = (Ref<AnimationNodeStateMachine>)this;
+ while (prev.is_valid()) {
+ p_parents.push_back(prev);
+ prev = prev->prev_state_machine;
+ }
+ }
+
+ if (states.has(p_name)) {
+ Ref<AnimationNodeStateMachine> anodesm = states[p_name].node;
+
+ if (anodesm.is_valid() && p_parents.find(anodesm) != -1) {
+ return false;
+ }
+
+ return true;
+ }
+
+ String name = p_name;
+ Vector<String> path = name.split("/");
+
+ if (path.size() < 2) {
+ return false;
+ }
+
+ if (path[0] == "..") {
+ if (prev_state_machine.is_valid()) {
+ return prev_state_machine->_can_connect(name.replace_first("../", ""), p_parents);
+ }
+ } else if (states.has(path[0])) {
+ Ref<AnimationNodeStateMachine> anodesm = states[path[0]].node;
+ if (anodesm.is_valid()) {
+ return anodesm->_can_connect(name.replace_first(path[0] + "/", ""), p_parents);
+ }
+ }
+
+ return false;
+}
+
+StringName AnimationNodeStateMachine::_get_shortest_path(const StringName &p_path) const {
+ // If p_path is something like StateMachine/../StateMachine2/State1,
+ // the result will be StateMachine2/State1. This avoid duplicate
+ // transitions when using add_transition. eg, this two calls is the same:
+ //
+ // add_transition("State1", "StateMachine/../State2", tr)
+ // add_transition("State1", "State2", tr)
+ //
+ // but the second call must be invalid because the transition already exists
+
+ Vector<String> path = String(p_path).split("/");
+ Vector<String> new_path;
+
+ for (int i = 0; i < path.size(); i++) {
+ if (i > 0 && path[i] == ".." && new_path[i - 1] != "..") {
+ new_path.remove_at(i - 1);
+ } else {
+ new_path.push_back(path[i]);
+ }
+ }
+
+ String result;
+ for (int i = 0; i < new_path.size(); i++) {
+ result += new_path[i] + "/";
+ }
+ result.remove_at(result.length() - 1);
+
+ return result;
+}
+
void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) {
- ERR_FAIL_COND(p_from == p_to);
- ERR_FAIL_COND(!states.has(p_from));
- ERR_FAIL_COND(!states.has(p_to));
+ if (updating_transitions) {
+ return;
+ }
+
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+ Vector<String> path_from = String(from).split("/");
+ Vector<String> path_to = String(to).split("/");
+
+ ERR_FAIL_COND(from == end_node || to == start_node);
+ ERR_FAIL_COND(from == to);
+ ERR_FAIL_COND(!_can_connect(from));
+ ERR_FAIL_COND(!_can_connect(to));
ERR_FAIL_COND(p_transition.is_null());
for (int i = 0; i < transitions.size(); i++) {
- ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to);
+ ERR_FAIL_COND(transitions[i].from == from && transitions[i].to == to);
}
+ if (path_from.size() > 1 || path_to.size() > 1) {
+ ERR_FAIL_COND(path_from[0] == path_to[0]);
+ }
+
+ updating_transitions = true;
+
+ StringName local_from = String(from).get_slicec('/', 0);
+ StringName local_to = String(to).get_slicec('/', 0);
+ local_from = local_from == ".." ? "Start" : local_from;
+ local_to = local_to == ".." ? "End" : local_to;
+
Transition tr;
- tr.from = p_from;
- tr.to = p_to;
+ tr.from = from;
+ tr.to = to;
+ tr.local_from = local_from;
+ 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);
transitions.push_back(tr);
+
+ // do recursive
+ if (path_from.size() > 1) {
+ StringName local_path = String(from).replace_first(path_from[0] + "/", "");
+ if (path_from[0] == "..") {
+ prev_state_machine->add_transition(local_path, String(state_machine_name) + "/" + to, p_transition);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[path_from[0]].node)->add_transition(local_path, "../" + to, p_transition);
+ }
+ }
+ if (path_to.size() > 1) {
+ StringName local_path = String(to).replace_first(path_to[0] + "/", "");
+ if (path_to[0] == "..") {
+ prev_state_machine->add_transition(String(state_machine_name) + "/" + from, local_path, p_transition);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[path_to[0]].node)->add_transition("../" + from, local_path, p_transition);
+ }
+ }
+
+ updating_transitions = false;
}
Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const {
@@ -748,44 +1015,52 @@ int AnimationNodeStateMachine::get_transition_count() const {
}
void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
- transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
- transitions.remove(i);
+ if (transitions[i].from == from && transitions[i].to == to) {
+ remove_transition_by_index(i);
return;
}
}
-
- /*if (playing) {
- path.clear();
- }*/
}
-void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) {
+void AnimationNodeStateMachine::remove_transition_by_index(const int p_transition) {
ERR_FAIL_INDEX(p_transition, transitions.size());
+ Transition tr = transitions[p_transition];
transitions.write[p_transition].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
- transitions.remove(p_transition);
- /*if (playing) {
- path.clear();
- }*/
-}
+ transitions.remove_at(p_transition);
-void AnimationNodeStateMachine::set_start_node(const StringName &p_node) {
- ERR_FAIL_COND(p_node != StringName() && !states.has(p_node));
- start_node = p_node;
-}
+ Vector<String> path_from = String(tr.from).split("/");
+ Vector<String> path_to = String(tr.to).split("/");
-String AnimationNodeStateMachine::get_start_node() const {
- return start_node;
-}
+ List<Vector<String>> paths;
+ paths.push_back(path_from);
+ paths.push_back(path_to);
+
+ for (List<Vector<String>>::Element *E = paths.front(); E; E = E->next()) {
+ if (E->get()[0].size() > 1) {
+ if (E->get()[0] == "..") {
+ prev_state_machine->_remove_transition(tr.transition);
+ } else if (states.has(E->get()[0])) {
+ Ref<AnimationNodeStateMachine> anodesm = states[E->get()[0]].node;
-void AnimationNodeStateMachine::set_end_node(const StringName &p_node) {
- ERR_FAIL_COND(p_node != StringName() && !states.has(p_node));
- end_node = p_node;
+ if (anodesm.is_valid()) {
+ anodesm->_remove_transition(tr.transition);
+ }
+ }
+ }
+ }
}
-String AnimationNodeStateMachine::get_end_node() const {
- return end_node;
+void AnimationNodeStateMachine::_remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition) {
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].transition == p_transition) {
+ remove_transition_by_index(i);
+ return;
+ }
+ }
}
void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) {
@@ -807,7 +1082,16 @@ String AnimationNodeStateMachine::get_caption() const {
return "StateMachine";
}
-void AnimationNodeStateMachine::_notification(int p_what) {
+bool AnimationNodeStateMachine::has_local_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].local_from == from && transitions[i].local_to == to) {
+ return true;
+ }
+ }
+ return false;
}
Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName &p_name) {
@@ -842,12 +1126,6 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_
add_transition(trans[i], trans[i + 1], trans[i + 2]);
}
return true;
- } else if (name == "start_node") {
- set_start_node(p_value);
- return true;
- } else if (name == "end_node") {
- set_end_node(p_value);
- return true;
} else if (name == "graph_offset") {
set_graph_offset(p_value);
return true;
@@ -863,7 +1141,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
String what = name.get_slicec('/', 2);
if (what == "node") {
- if (states.has(node_name)) {
+ if (states.has(node_name) && can_edit_node(node_name)) {
r_ret = states[node_name].node;
return true;
}
@@ -877,22 +1155,21 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
}
} else if (name == "transitions") {
Array trans;
- trans.resize(transitions.size() * 3);
-
for (int i = 0; i < transitions.size(); i++) {
- trans[i * 3 + 0] = transitions[i].from;
- trans[i * 3 + 1] = transitions[i].to;
- trans[i * 3 + 2] = transitions[i].transition;
+ String from = transitions[i].from;
+ String to = transitions[i].to;
+
+ if (from.get_slicec('/', 0) == ".." || to.get_slicec('/', 0) == "..") {
+ continue;
+ }
+
+ trans.push_back(from);
+ trans.push_back(to);
+ trans.push_back(transitions[i].transition);
}
r_ret = trans;
return true;
- } else if (name == "start_node") {
- r_ret = get_start_node();
- return true;
- } else if (name == "end_node") {
- r_ret = get_end_node();
- return true;
} else if (name == "graph_offset") {
r_ret = get_graph_offset();
return true;
@@ -914,8 +1191,6 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c
}
p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
@@ -923,10 +1198,24 @@ void AnimationNodeStateMachine::reset_state() {
states.clear();
transitions.clear();
playback = "playback";
- start_node = StringName();
- end_node = StringName();
+ start_node = "Start";
+ end_node = "End";
graph_offset = Vector2();
+ Ref<AnimationNodeStartState> s;
+ s.instantiate();
+ State start;
+ start.node = s;
+ start.position = Vector2(200, 100);
+ states[start_node] = start;
+
+ Ref<AnimationNodeEndState> e;
+ e.instantiate();
+ State end;
+ end.node = e;
+ end.position = Vector2(900, 100);
+ states[end_node] = end;
+
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -966,15 +1255,22 @@ void AnimationNodeStateMachine::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_transition_by_index", "idx"), &AnimationNodeStateMachine::remove_transition_by_index);
ClassDB::bind_method(D_METHOD("remove_transition", "from", "to"), &AnimationNodeStateMachine::remove_transition);
- ClassDB::bind_method(D_METHOD("set_start_node", "name"), &AnimationNodeStateMachine::set_start_node);
- ClassDB::bind_method(D_METHOD("get_start_node"), &AnimationNodeStateMachine::get_start_node);
-
- ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node);
- ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node);
-
ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeStateMachine::set_graph_offset);
ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset);
}
AnimationNodeStateMachine::AnimationNodeStateMachine() {
+ Ref<AnimationNodeStartState> s;
+ s.instantiate();
+ State start;
+ start.node = s;
+ start.position = Vector2(200, 100);
+ states[start_node] = start;
+
+ Ref<AnimationNodeEndState> e;
+ e.instantiate();
+ State end;
+ end.node = e;
+ end.position = Vector2(900, 100);
+ states[end_node] = end;
}
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 6f0e3107fd..07d0579533 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -93,13 +93,19 @@ class AnimationNodeStateMachinePlayback : public Resource {
StringName prev;
};
- float len_total = 0.0;
+ struct Transition {
+ StringName from;
+ StringName to;
+ StringName next;
+ };
float len_current = 0.0;
float pos_current = 0.0;
- int loops_current = 0;
+ bool end_loop = false;
StringName current;
+ Transition current_transition;
+ bool force_auto_advance = false;
StringName fading_from;
float fading_time = 0.0;
@@ -116,6 +122,8 @@ class AnimationNodeStateMachinePlayback : public Resource {
double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek);
+ bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
+
protected:
static void _bind_methods();
@@ -144,36 +152,45 @@ private:
Vector2 position;
};
- Map<StringName, State> states;
+ HashMap<StringName, State> states;
struct Transition {
StringName from;
StringName to;
+ StringName local_from;
+ StringName local_to;
Ref<AnimationNodeStateMachineTransition> transition;
};
Vector<Transition> transitions;
StringName playback = "playback";
-
- StringName start_node;
- StringName end_node;
+ StringName state_machine_name;
+ Ref<AnimationNodeStateMachine> prev_state_machine;
+ bool updating_transitions = false;
Vector2 graph_offset;
void _tree_changed();
+ void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
+ void _rename_transition(const StringName &p_name, const StringName &p_new_name);
+ bool _can_connect(const StringName &p_name, const Vector<Ref<AnimationNodeStateMachine>> p_parents = Vector<Ref<AnimationNodeStateMachine>>()) const;
+ StringName _get_shortest_path(const StringName &p_path) const;
protected:
- void _notification(int p_what);
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;
+ bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
virtual void reset_state() override;
public:
+ StringName start_node = "Start";
+ StringName end_node = "End";
+
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -192,20 +209,19 @@ public:
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
bool has_transition(const StringName &p_from, const StringName &p_to) const;
+ bool has_local_transition(const StringName &p_from, const StringName &p_to) const;
int find_transition(const StringName &p_from, const StringName &p_to) const;
void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition);
Ref<AnimationNodeStateMachineTransition> get_transition(int p_transition) const;
StringName get_transition_from(int p_transition) const;
StringName get_transition_to(int p_transition) const;
int get_transition_count() const;
- void remove_transition_by_index(int p_transition);
+ void remove_transition_by_index(const int p_transition);
void remove_transition(const StringName &p_from, const StringName &p_to);
- void set_start_node(const StringName &p_node);
- String get_start_node() const;
+ bool can_edit_node(const StringName &p_name) const;
- void set_end_node(const StringName &p_node);
- String get_end_node() const;
+ Ref<AnimationNodeStateMachine> get_prev_state_machine() const;
void set_graph_offset(const Vector2 &p_offset);
Vector2 get_graph_offset() const;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 93339711bd..9ace6db505 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -83,8 +83,31 @@ bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) {
set_current_animation(p_value);
} else if (name.begins_with("anims/")) {
+ // Backwards compatibility with 3.x, add them to "default" library.
String which = name.get_slicec('/', 1);
- add_animation(which, p_value);
+
+ Ref<Animation> anim = p_value;
+ Ref<AnimationLibrary> al;
+ if (!has_animation_library(StringName())) {
+ al.instantiate();
+ add_animation_library(StringName(), al);
+ } else {
+ al = get_animation_library(StringName());
+ }
+ al->add_animation(which, anim);
+
+ } else if (name.begins_with("libraries")) {
+ Dictionary d = p_value;
+ while (animation_libraries.size()) {
+ remove_animation_library(animation_libraries[0].name);
+ }
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ for (const Variant &K : keys) {
+ StringName lib_name = K;
+ Ref<AnimationLibrary> lib = d[lib_name];
+ add_animation_library(lib_name, lib);
+ }
} else if (name.begins_with("next/")) {
String which = name.get_slicec('/', 1);
@@ -117,9 +140,13 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_current_animation();
- } else if (name.begins_with("anims/")) {
- String which = name.get_slicec('/', 1);
- r_ret = get_animation(which);
+ } else if (name.begins_with("libraries")) {
+ Dictionary d;
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ d[animation_libraries[i].name] = animation_libraries[i].library;
+ }
+
+ r_ret = d;
} else if (name.begins_with("next/")) {
String which = name.get_slicec('/', 1);
@@ -136,7 +163,7 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const {
for (int i = 0; i < keys.size(); i++) {
array.push_back(keys[i].from);
array.push_back(keys[i].to);
- array.push_back(blend_times[keys[i]]);
+ array.push_back(blend_times.get(keys[i]));
}
r_ret = array;
@@ -173,8 +200,9 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const {
void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> anim_names;
+ anim_names.push_back(PropertyInfo(Variant::DICTIONARY, "libraries"));
+
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
- anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
if (E.value.next != StringName()) {
anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
@@ -202,15 +230,16 @@ void AnimationPlayer::_notification(int p_what) {
set_physics_process_internal(false);
set_process_internal(false);
}
- //_set_process(false);
clear_caches();
} break;
+
case NOTIFICATION_READY: {
if (!Engine::get_singleton()->is_editor_hint() && animation_set.has(autoplay)) {
play(autoplay);
_animation_process(0);
}
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
if (process_callback == ANIMATION_PROCESS_PHYSICS) {
break;
@@ -220,6 +249,7 @@ void AnimationPlayer::_notification(int p_what) {
_animation_process(get_process_delta_time());
}
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (process_callback == ANIMATION_PROCESS_IDLE) {
break;
@@ -229,6 +259,7 @@ void AnimationPlayer::_notification(int p_what) {
_animation_process(get_physics_process_delta_time());
}
} break;
+
case NOTIFICATION_EXIT_TREE: {
clear_caches();
} break;
@@ -253,7 +284,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
for (int i = 0; i < a->get_track_count(); i++) {
p_anim->node_cache.write[i] = nullptr;
- RES resource;
+ Ref<Resource> resource;
Vector<StringName> leftover_path;
Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path);
ERR_CONTINUE_MSG(!child, "On Animation: '" + p_anim->name + "', couldn't resolve track: '" + String(a->track_get_path(i)) + "'."); // couldn't find the child node
@@ -398,6 +429,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
}
}
+static void _call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) {
+ // Separate function to use alloca() more efficiently
+ const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * p_params.size());
+ const Variant *args = p_params.ptr();
+ uint32_t argcount = p_params.size();
+ for (uint32_t i = 0; i < argcount; i++) {
+ argptrs[i] = &args[i];
+ }
+ if (p_deferred) {
+ MessageQueue::get_singleton()->push_callp(p_object, p_method, argptrs, argcount);
+ } else {
+ Callable::CallError ce;
+ p_object->callp(p_method, argptrs, argcount, ce);
+ }
+}
+
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());
@@ -541,10 +588,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
//StringName property=a->track_get_path(i).get_property();
- Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames());
+ HashMap<StringName, TrackNodeCache::PropertyAnim>::Iterator E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames());
ERR_CONTINUE(!E); //should it continue, or create a new one?
- TrackNodeCache::PropertyAnim *pa = &E->get();
+ TrackNodeCache::PropertyAnim *pa = &E->value;
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
@@ -592,18 +639,12 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
}
if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE || (p_delta == 0 && update_mode == Animation::UPDATE_DISCRETE)) { //delta == 0 means seek
-
Variant value = a->value_track_interpolate(i, p_time);
if (value == Variant()) {
continue;
}
- //thanks to trigger mode, this should be solved now..
- /*
- if (p_delta==0 && value.get_type()==Variant::STRING)
- continue; // doing this with strings is messy, should find another way
- */
if (pa->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX);
cache_update_prop[cache_update_prop_size++] = pa;
@@ -625,7 +666,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
pa->object->set_indexed(pa->subpath, value, &valid); //you are not speshul
#ifdef DEBUG_ENABLED
if (!valid) {
- ERR_PRINT("Failed setting track value '" + String(pa->owner->path) + "'. Check if property exists or the type of key is valid. Animation '" + a->get_name() + "' at node '" + get_path() + "'.");
+ ERR_PRINT("Failed setting track value '" + String(pa->owner->path) + "'. Check if the property exists or the type of key is valid. Animation '" + a->get_name() + "' at node '" + get_path() + "'.");
}
#endif
@@ -680,41 +721,14 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
StringName method = a->method_track_get_name(i, E);
Vector<Variant> params = a->method_track_get_params(i, E);
- int s = params.size();
-
- ERR_CONTINUE(s > VARIANT_ARG_MAX);
#ifdef DEBUG_ENABLED
if (!nc->node->has_method(method)) {
ERR_PRINT("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'.");
}
#endif
- static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
if (can_call) {
- if (method_call_mode == ANIMATION_METHOD_CALL_DEFERRED) {
- MessageQueue::get_singleton()->push_call(
- nc->node,
- method,
- s >= 1 ? params[0] : Variant(),
- s >= 2 ? params[1] : Variant(),
- s >= 3 ? params[2] : Variant(),
- s >= 4 ? params[3] : Variant(),
- s >= 5 ? params[4] : Variant(),
- s >= 6 ? params[5] : Variant(),
- s >= 7 ? params[6] : Variant(),
- s >= 8 ? params[7] : Variant());
- } else {
- nc->node->call(
- method,
- s >= 1 ? params[0] : Variant(),
- s >= 2 ? params[1] : Variant(),
- s >= 3 ? params[2] : Variant(),
- s >= 4 ? params[3] : Variant(),
- s >= 5 ? params[4] : Variant(),
- s >= 6 ? params[5] : Variant(),
- s >= 7 ? params[6] : Variant(),
- s >= 8 ? params[7] : Variant());
- }
+ _call_object(nc->node, method, params, method_call_mode == ANIMATION_METHOD_CALL_DEFERRED);
}
}
@@ -724,10 +738,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
continue;
}
- Map<StringName, TrackNodeCache::BezierAnim>::Element *E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames());
+ HashMap<StringName, TrackNodeCache::BezierAnim>::Iterator E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames());
ERR_CONTINUE(!E); //should it continue, or create a new one?
- TrackNodeCache::BezierAnim *ba = &E->get();
+ TrackNodeCache::BezierAnim *ba = &E->value;
real_t bezier = a->bezier_track_interpolate(i, p_time);
if (ba->accum_pass != accum_pass) {
@@ -736,7 +750,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
ba->bezier_accum = bezier;
ba->accum_pass = accum_pass;
} else {
- ba->bezier_accum = Math::lerp(ba->bezier_accum, bezier, p_interp);
+ ba->bezier_accum = Math::lerp(ba->bezier_accum, (float)bezier, p_interp);
}
} break;
@@ -757,7 +771,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
if (!stream.is_valid()) {
- nc->node->call("stop");
+ nc->node->call(SNAME("stop"));
nc->audio_playing = false;
playing_caches.erase(nc);
} else {
@@ -767,14 +781,14 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
float len = stream->get_length();
if (start_ofs > len - end_ofs) {
- nc->node->call("stop");
+ nc->node->call(SNAME("stop"));
nc->audio_playing = false;
playing_caches.erase(nc);
continue;
}
- nc->node->call("set_stream", stream);
- nc->node->call("play", start_ofs);
+ nc->node->call(SNAME("set_stream"), stream);
+ nc->node->call(SNAME("play"), start_ofs);
nc->audio_playing = true;
playing_caches.insert(nc);
@@ -796,7 +810,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
if (!stream.is_valid()) {
- nc->node->call("stop");
+ nc->node->call(SNAME("stop"));
nc->audio_playing = false;
playing_caches.erase(nc);
} else {
@@ -804,8 +818,8 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
float end_ofs = a->audio_track_get_key_end_offset(i, idx);
float len = stream->get_length();
- nc->node->call("set_stream", stream);
- nc->node->call("play", start_ofs);
+ nc->node->call(SNAME("set_stream"), stream);
+ nc->node->call(SNAME("play"), start_ofs);
nc->audio_playing = true;
playing_caches.insert(nc);
@@ -836,7 +850,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (stop) {
//time to stop
- nc->node->call("stop");
+ nc->node->call(SNAME("stop"));
nc->audio_playing = false;
playing_caches.erase(nc);
}
@@ -1070,8 +1084,24 @@ void AnimationPlayer::_animation_update_transforms() {
bool valid;
pa->object->set_indexed(pa->subpath, pa->value_accum, &valid); //you are not speshul
#ifdef DEBUG_ENABLED
+
if (!valid) {
- ERR_PRINT("Failed setting key at time " + rtos(playback.current.pos) + " in Animation '" + get_current_animation() + "' at Node '" + get_path() + "', Track '" + String(pa->owner->path) + "'. Check if property exists or the type of key is right for the property");
+ // Get subpath as string for printing the error
+ // Cannot use `String::join(Vector<String>)` because this is a vector of StringName
+ String key_debug;
+ if (pa->subpath.size() > 0) {
+ key_debug = pa->subpath[0];
+ for (int subpath_index = 1; subpath_index < pa->subpath.size(); ++subpath_index) {
+ key_debug += ".";
+ key_debug += pa->subpath[subpath_index];
+ }
+ }
+ ERR_PRINT("Failed setting key '" + key_debug +
+ "' at time " + rtos(playback.current.pos) +
+ " in Animation '" + get_current_animation() +
+ "' at Node '" + get_path() +
+ "', Track '" + String(pa->owner->path) +
+ "'. Check if the property exists or the type of key is right for the property.");
}
#endif
@@ -1153,71 +1183,106 @@ void AnimationPlayer::_animation_process(double p_delta) {
}
}
-Error AnimationPlayer::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_V_MSG(String(p_name).find("/") != -1 || String(p_name).find(":") != -1 || String(p_name).find(",") != -1 || String(p_name).find("[") != -1, ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
-#endif
+void AnimationPlayer::_animation_set_cache_update() {
+ // Relatively fast function to update all animations.
- ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER);
+ animation_set_update_pass++;
+ bool clear_cache_needed = false;
- if (animation_set.has(p_name)) {
- _unref_anim(animation_set[p_name].animation);
- animation_set[p_name].animation = p_animation;
- clear_caches();
- } else {
- AnimationData ad;
- ad.animation = p_animation;
- ad.name = p_name;
- animation_set[p_name] = ad;
- }
+ // Update changed and add otherwise
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) {
+ StringName key = animation_libraries[i].name == StringName() ? K.key : StringName(String(animation_libraries[i].name) + "/" + String(K.key));
+ if (!animation_set.has(key)) {
+ AnimationData ad;
+ ad.animation = K.value;
+ ad.animation_library = animation_libraries[i].name;
+ ad.name = key;
+ ad.last_update = animation_set_update_pass;
+ animation_set.insert(ad.name, ad);
+ } else {
+ AnimationData &ad = animation_set[key];
+ if (ad.last_update != animation_set_update_pass) {
+ // Was not updated, update. If the animation is duplicated, the second one will be ignored.
+ if (ad.animation != K.value || ad.animation_library != animation_libraries[i].name) {
+ // Animation changed, update and clear caches.
+ clear_cache_needed = true;
+ ad.animation = K.value;
+ ad.animation_library = animation_libraries[i].name;
+ }
- _ref_anim(p_animation);
- notify_property_list_changed();
- return OK;
-}
+ ad.last_update = animation_set_update_pass;
+ }
+ }
+ }
+ }
-void AnimationPlayer::remove_animation(const StringName &p_name) {
- ERR_FAIL_COND(!animation_set.has(p_name));
+ // Check removed
+ List<StringName> to_erase;
+ for (const KeyValue<StringName, AnimationData> &E : animation_set) {
+ if (E.value.last_update != animation_set_update_pass) {
+ // Was not updated, must be erased
+ to_erase.push_back(E.key);
+ clear_cache_needed = true;
+ }
+ }
- stop();
- _unref_anim(animation_set[p_name].animation);
- animation_set.erase(p_name);
+ while (to_erase.size()) {
+ animation_set.erase(to_erase.front()->get());
+ to_erase.pop_front();
+ }
- clear_caches();
- notify_property_list_changed();
+ if (clear_cache_needed) {
+ // If something was modified or removed, caches need to be cleared
+ clear_caches();
+ }
}
-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);
-}
+void AnimationPlayer::_animation_added(const StringName &p_name, const StringName &p_library) {
+ _animation_set_cache_update();
-void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) {
- Ref<Animation>(p_anim)->disconnect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed));
+ update_configuration_warnings();
}
-void AnimationPlayer::rename_animation(const StringName &p_name, const StringName &p_new_name) {
- ERR_FAIL_COND(!animation_set.has(p_name));
- ERR_FAIL_COND(String(p_new_name).find("/") != -1 || String(p_new_name).find(":") != -1);
- ERR_FAIL_COND(animation_set.has(p_new_name));
+void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) {
+ StringName name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name));
- stop();
- AnimationData ad = animation_set[p_name];
- ad.name = p_new_name;
- animation_set.erase(p_name);
- animation_set[p_new_name] = ad;
+ if (!animation_set.has(name)) {
+ return; // No need to update because not the one from the library being used.
+ }
+ _animation_set_cache_update();
+ // Erase blends if needed
List<BlendKey> to_erase;
- Map<BlendKey, float> to_insert;
+ for (const KeyValue<BlendKey, float> &E : blend_times) {
+ BlendKey bk = E.key;
+ if (bk.from == name || bk.to == name) {
+ to_erase.push_back(bk);
+ }
+ }
+
+ while (to_erase.size()) {
+ blend_times.erase(to_erase.front()->get());
+ to_erase.pop_front();
+ }
+
+ update_configuration_warnings();
+}
+
+void AnimationPlayer::_rename_animation(const StringName &p_from_name, const StringName &p_to_name) {
+ // Rename autoplay or blends if needed.
+ List<BlendKey> to_erase;
+ HashMap<BlendKey, float, BlendKey> to_insert;
for (const KeyValue<BlendKey, float> &E : blend_times) {
BlendKey bk = E.key;
BlendKey new_bk = bk;
bool erase = false;
- if (bk.from == p_name) {
- new_bk.from = p_new_name;
+ if (bk.from == p_from_name) {
+ new_bk.from = p_to_name;
erase = true;
}
- if (bk.to == p_name) {
- new_bk.to = p_new_name;
+ if (bk.to == p_from_name) {
+ new_bk.to = p_to_name;
erase = true;
}
@@ -1233,16 +1298,188 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam
}
while (to_insert.size()) {
- blend_times[to_insert.front()->key()] = to_insert.front()->get();
- to_insert.erase(to_insert.front());
+ blend_times[to_insert.begin()->key] = to_insert.begin()->value;
+ to_insert.remove(to_insert.begin());
}
- if (autoplay == p_name) {
- autoplay = p_new_name;
+ if (autoplay == p_from_name) {
+ autoplay = p_to_name;
}
+}
+
+void AnimationPlayer::_animation_renamed(const StringName &p_name, const StringName &p_to_name, const StringName &p_library) {
+ StringName from_name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name));
+ StringName to_name = p_library == StringName() ? p_to_name : StringName(String(p_library) + "/" + String(p_to_name));
+
+ if (!animation_set.has(from_name)) {
+ return; // No need to update because not the one from the library being used.
+ }
+ _animation_set_cache_update();
+
+ _rename_animation(from_name, to_name);
+ update_configuration_warnings();
+}
+
+Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library) {
+ ERR_FAIL_COND_V(p_animation_library.is_null(), ERR_INVALID_PARAMETER);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
+#endif
+
+ int insert_pos = 0;
+
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ ERR_FAIL_COND_V_MSG(animation_libraries[i].name == p_name, ERR_ALREADY_EXISTS, "Can't add animation library twice with name: " + String(p_name));
+ ERR_FAIL_COND_V_MSG(animation_libraries[i].library == p_animation_library, ERR_ALREADY_EXISTS, "Can't add animation library twice (adding as '" + p_name.operator String() + "', exists as '" + animation_libraries[i].name.operator String() + "'.");
+
+ if (animation_libraries[i].name.operator String() >= p_name.operator String()) {
+ break;
+ }
+
+ insert_pos++;
+ }
+
+ AnimationLibraryData ald;
+ ald.name = p_name;
+ ald.library = p_animation_library;
+
+ 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));
+
+ _animation_set_cache_update();
+
+ notify_property_list_changed();
+
+ update_configuration_warnings();
+ return OK;
+}
+
+void AnimationPlayer::remove_animation_library(const StringName &p_name) {
+ int at_pos = -1;
+
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ if (animation_libraries[i].name == p_name) {
+ at_pos = i;
+ break;
+ }
+ }
+
+ ERR_FAIL_COND(at_pos == -1);
+
+ animation_libraries[at_pos].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added));
+ animation_libraries[at_pos].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added));
+ animation_libraries[at_pos].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed));
+
+ stop();
+
+ for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[at_pos].library->animations) {
+ _unref_anim(K.value);
+ }
+
+ animation_libraries.remove_at(at_pos);
+ _animation_set_cache_update();
- clear_caches();
notify_property_list_changed();
+ update_configuration_warnings();
+}
+
+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);
+}
+
+void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) {
+ Ref<Animation>(p_anim)->disconnect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed));
+}
+
+void AnimationPlayer::rename_animation_library(const StringName &p_name, const StringName &p_new_name) {
+ if (p_name == p_new_name) {
+ return;
+ }
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(String(p_new_name).contains("/") || String(p_new_name).contains(":") || String(p_new_name).contains(",") || String(p_new_name).contains("["), "Invalid animation library name: " + String(p_new_name) + ".");
+#endif
+
+ bool found = false;
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ ERR_FAIL_COND_MSG(animation_libraries[i].name == p_new_name, "Can't rename animation library to another existing name: " + String(p_new_name));
+ if (animation_libraries[i].name == p_name) {
+ found = true;
+ animation_libraries[i].name = p_new_name;
+ // rename connections
+ animation_libraries[i].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added));
+ 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));
+
+ 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));
+ StringName new_name = p_new_name == StringName() ? K.key : StringName(String(p_new_name) + "/" + String(K.key));
+ _rename_animation(old_name, new_name);
+ }
+ }
+ }
+
+ ERR_FAIL_COND(!found);
+
+ stop();
+
+ animation_libraries.sort(); // Must keep alphabetical order.
+
+ _animation_set_cache_update(); // Update cache.
+
+ notify_property_list_changed();
+}
+
+bool AnimationPlayer::has_animation_library(const StringName &p_name) const {
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ if (animation_libraries[i].name == p_name) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Ref<AnimationLibrary> AnimationPlayer::get_animation_library(const StringName &p_name) const {
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ if (animation_libraries[i].name == p_name) {
+ return animation_libraries[i].library;
+ }
+ }
+ ERR_FAIL_V(Ref<AnimationLibrary>());
+}
+
+TypedArray<StringName> AnimationPlayer::_get_animation_library_list() const {
+ TypedArray<StringName> ret;
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ ret.push_back(animation_libraries[i].name);
+ }
+ return ret;
+}
+
+void AnimationPlayer::get_animation_library_list(List<StringName> *p_libraries) const {
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ p_libraries->push_back(animation_libraries[i].name);
+ }
+}
+
+TypedArray<String> AnimationPlayer::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) {
+ if (animation_set.has(K.key) && animation_set[K.key].animation_library != animation_libraries[i].name) {
+ warnings.push_back(vformat(RTR("Animation '%s' in library '%s' is unused because another animation with the same name exists in library '%s'."), K.key, animation_libraries[i].name, animation_set[K.key].animation_library));
+ }
+ }
+ }
+ return warnings;
}
bool AnimationPlayer::has_animation(const StringName &p_name) const {
@@ -1416,7 +1653,7 @@ bool AnimationPlayer::is_playing() const {
}
void AnimationPlayer::set_current_animation(const String &p_anim) {
- if (p_anim == "[stop]" || p_anim == "") {
+ if (p_anim == "[stop]" || p_anim.is_empty()) {
stop();
} else if (!is_playing() || playback.assigned != p_anim) {
play(p_anim);
@@ -1529,9 +1766,9 @@ void AnimationPlayer::_animation_changed() {
}
void AnimationPlayer::_stop_playing_caches() {
- for (Set<TrackNodeCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
+ for (RBSet<TrackNodeCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
if (E->get()->node && E->get()->audio_playing) {
- E->get()->node->call("stop");
+ E->get()->node->call(SNAME("stop"));
}
if (E->get()->node && E->get()->animation_playing) {
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(E->get()->node);
@@ -1583,7 +1820,16 @@ StringName AnimationPlayer::find_animation(const Ref<Animation> &p_animation) co
}
}
- return "";
+ return StringName();
+}
+
+StringName AnimationPlayer::find_animation_library(const Ref<Animation> &p_animation) const {
+ for (const KeyValue<StringName, AnimationData> &E : animation_set) {
+ if (E.value.animation == p_animation) {
+ return E.value.animation_library;
+ }
+ }
+ return StringName();
}
void AnimationPlayer::set_autoplay(const String &p_name) {
@@ -1754,7 +2000,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o
Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
ERR_FAIL_COND_V(!can_apply_reset(), Ref<AnimatedValuesBackup>());
- Ref<Animation> reset_anim = animation_set["RESET"].animation;
+ Ref<Animation> reset_anim = animation_set[SceneStringNames::get_singleton()->RESET].animation;
ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>());
Node *root_node = get_node_or_null(root);
@@ -1762,8 +2008,11 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
AnimationPlayer *aux_player = memnew(AnimationPlayer);
EditorNode::get_singleton()->add_child(aux_player);
- aux_player->add_animation("RESET", reset_anim);
- aux_player->set_assigned_animation("RESET");
+ Ref<AnimationLibrary> al;
+ al.instantiate();
+ al->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim);
+ aux_player->add_animation_library("default", al);
+ aux_player->set_assigned_animation(SceneStringNames::get_singleton()->RESET);
// Forcing the use of the original root because the scene where original player belongs may be not the active one
Node *root = get_node(get_root());
Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(root);
@@ -1785,14 +2034,18 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
}
bool AnimationPlayer::can_apply_reset() const {
- return has_animation("RESET") && playback.assigned != StringName("RESET");
+ return has_animation(SceneStringNames::get_singleton()->RESET) && playback.assigned != SceneStringNames::get_singleton()->RESET;
}
-#endif
+#endif // TOOLS_ENABLED
void AnimationPlayer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_animation", "name", "animation"), &AnimationPlayer::add_animation);
- ClassDB::bind_method(D_METHOD("remove_animation", "name"), &AnimationPlayer::remove_animation);
- ClassDB::bind_method(D_METHOD("rename_animation", "name", "newname"), &AnimationPlayer::rename_animation);
+ ClassDB::bind_method(D_METHOD("add_animation_library", "name", "library"), &AnimationPlayer::add_animation_library);
+ ClassDB::bind_method(D_METHOD("remove_animation_library", "name"), &AnimationPlayer::remove_animation_library);
+ ClassDB::bind_method(D_METHOD("rename_animation_library", "name", "newname"), &AnimationPlayer::rename_animation_library);
+ ClassDB::bind_method(D_METHOD("has_animation_library", "name"), &AnimationPlayer::has_animation_library);
+ ClassDB::bind_method(D_METHOD("get_animation_library", "name"), &AnimationPlayer::get_animation_library);
+ ClassDB::bind_method(D_METHOD("get_animation_library_list"), &AnimationPlayer::_get_animation_library_list);
+
ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationPlayer::has_animation);
ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationPlayer::get_animation);
ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationPlayer::_get_animation_list);
@@ -1836,6 +2089,7 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::get_root);
ClassDB::bind_method(D_METHOD("find_animation", "animation"), &AnimationPlayer::find_animation);
+ ClassDB::bind_method(D_METHOD("find_animation_library", "animation"), &AnimationPlayer::find_animation_library);
ClassDB::bind_method(D_METHOD("clear_caches"), &AnimationPlayer::clear_caches);
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index ea04918988..3ef87dba28 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,6 +36,7 @@
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/animation.h"
+#include "scene/resources/animation_library.h"
#ifdef TOOLS_ENABLED
class AnimatedValuesBackup : public RefCounted {
@@ -62,7 +63,6 @@ public:
class AnimationPlayer : public Node {
GDCLASS(AnimationPlayer, Node);
- OBJ_CATEGORY("Animation Nodes");
public:
enum AnimationProcessCallback {
@@ -94,7 +94,7 @@ private:
struct TrackNodeCache {
NodePath path;
uint32_t id = 0;
- RES resource;
+ Ref<Resource> resource;
Node *node = nullptr;
Node2D *node_2d = nullptr;
#ifndef _3D_DISABLED
@@ -132,7 +132,7 @@ private:
Variant capture;
};
- Map<StringName, PropertyAnim> property_anim;
+ HashMap<StringName, PropertyAnim> property_anim;
struct BezierAnim {
Vector<StringName> bezier_property;
@@ -142,7 +142,7 @@ private:
uint64_t accum_pass = 0;
};
- Map<StringName, BezierAnim> bezier_anim;
+ HashMap<StringName, BezierAnim> bezier_anim;
uint32_t last_setup_pass = 0;
TrackNodeCache() {}
@@ -153,6 +153,16 @@ private:
int bone_idx = -1;
int blend_shape_idx = -1;
+ static uint32_t hash(const TrackNodeCacheKey &p_key) {
+ uint32_t h = hash_one_uint64(p_key.id);
+ h = hash_djb2_one_32(p_key.bone_idx, h);
+ return hash_djb2_one_32(p_key.blend_shape_idx, h);
+ }
+
+ inline bool operator==(const TrackNodeCacheKey &p_right) const {
+ return id == p_right.id && bone_idx == p_right.bone_idx && blend_shape_idx == p_right.blend_shape_idx;
+ }
+
inline bool operator<(const TrackNodeCacheKey &p_right) const {
if (id == p_right.id) {
if (blend_shape_idx == p_right.blend_shape_idx) {
@@ -166,7 +176,7 @@ private:
}
};
- Map<TrackNodeCacheKey, TrackNodeCache> node_cache_map;
+ HashMap<TrackNodeCacheKey, TrackNodeCache, TrackNodeCacheKey> node_cache_map;
TrackNodeCache *cache_update[NODE_CACHE_UPDATE_MAX];
int cache_update_size = 0;
@@ -174,7 +184,7 @@ private:
int cache_update_prop_size = 0;
TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX];
int cache_update_bezier_size = 0;
- Set<TrackNodeCache *> playing_caches;
+ RBSet<TrackNodeCache *> playing_caches;
uint64_t accum_pass = 1;
float speed_scale = 1.0;
@@ -185,20 +195,43 @@ private:
StringName next;
Vector<TrackNodeCache *> node_cache;
Ref<Animation> animation;
+ StringName animation_library;
+ uint64_t last_update = 0;
+ };
+
+ HashMap<StringName, AnimationData> animation_set;
+
+ struct AnimationLibraryData {
+ StringName name;
+ Ref<AnimationLibrary> library;
+ bool operator<(const AnimationLibraryData &p_data) const { return name.operator String() < p_data.name.operator String(); }
};
- Map<StringName, AnimationData> animation_set;
+ LocalVector<AnimationLibraryData> animation_libraries;
+
struct BlendKey {
StringName from;
StringName to;
- bool operator<(const BlendKey &bk) const { return from == bk.from ? String(to) < String(bk.to) : String(from) < String(bk.from); }
+ static uint32_t hash(const BlendKey &p_key) {
+ return hash_one_uint64((uint64_t(p_key.from.hash()) << 32) | uint32_t(p_key.to.hash()));
+ }
+ bool operator==(const BlendKey &bk) const {
+ return from == bk.from && to == bk.to;
+ }
+ bool operator<(const BlendKey &bk) const {
+ if (from == bk.from) {
+ return to < bk.to;
+ } else {
+ return from < bk.from;
+ }
+ }
};
- Map<BlendKey, float> blend_times;
+ HashMap<BlendKey, float, BlendKey> blend_times;
struct PlaybackData {
AnimationData *from = nullptr;
- float pos = 0.0;
+ double pos = 0.0;
float speed_scale = 1.0;
};
@@ -262,6 +295,15 @@ private:
bool playing = false;
+ uint64_t animation_set_update_pass = 1;
+ void _animation_set_cache_update();
+ void _animation_added(const StringName &p_name, const StringName &p_library);
+ void _animation_removed(const StringName &p_name, const StringName &p_library);
+ void _animation_renamed(const StringName &p_name, const StringName &p_to_name, const StringName &p_library);
+ void _rename_animation(const StringName &p_from_name, const StringName &p_to_name);
+
+ TypedArray<StringName> _get_animation_library_list() const;
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -273,13 +315,18 @@ protected:
public:
StringName find_animation(const Ref<Animation> &p_animation) const;
+ StringName find_animation_library(const Ref<Animation> &p_animation) const;
+
+ Error add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library);
+ void remove_animation_library(const StringName &p_name);
+ void rename_animation_library(const StringName &p_name, const StringName &p_new_name);
+ Ref<AnimationLibrary> get_animation_library(const StringName &p_name) const;
+ void get_animation_library_list(List<StringName> *p_animations) const;
+ bool has_animation_library(const StringName &p_name) const;
- Error add_animation(const StringName &p_name, const Ref<Animation> &p_animation);
- void remove_animation(const StringName &p_name);
- void rename_animation(const StringName &p_name, const StringName &p_new_name);
- bool has_animation(const StringName &p_name) const;
Ref<Animation> get_animation(const StringName &p_name) const;
void get_animation_list(List<StringName> *p_animations) const;
+ bool has_animation(const StringName &p_name) const;
void set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time);
float get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const;
@@ -341,6 +388,8 @@ public:
bool can_apply_reset() const;
#endif
+ TypedArray<String> get_configuration_warnings() const override;
+
AnimationPlayer();
~AnimationPlayer();
};
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 37e754148c..92af15d5d3 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -88,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
-void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
+void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@@ -119,13 +119,13 @@ void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time
state->animation_states.push_back(anim_state);
}
-real_t AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, real_t p_time, bool p_seek, const Vector<StringName> &p_connections) {
+double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections) {
base_path = p_base_path;
parent = p_parent;
connections = p_connections;
state = p_state;
- real_t t = process(p_time, p_seek);
+ double t = process(p_time, p_seek);
state = nullptr;
parent = nullptr;
@@ -138,13 +138,13 @@ real_t AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode
void AnimationNode::make_invalid(const String &p_reason) {
ERR_FAIL_COND(!state);
state->valid = false;
- if (state->invalid_reasons != String()) {
+ if (!state->invalid_reasons.is_empty()) {
state->invalid_reasons += "\n";
}
state->invalid_reasons += String::utf8("• ") + p_reason;
}
-real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
ERR_FAIL_COND_V(!state, 0);
@@ -163,7 +163,7 @@ real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_
//inputs.write[p_input].last_pass = state->last_pass;
real_t activity = 0.0;
- real_t ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity);
+ double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity);
Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path);
@@ -174,11 +174,11 @@ real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_
return ret;
}
-real_t AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_blend, p_filter, p_optimize);
}
-real_t AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
+double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
ERR_FAIL_COND_V(!p_node.is_valid(), 0);
ERR_FAIL_COND_V(!state, 0);
@@ -198,12 +198,11 @@ real_t AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
blendw[i] = 0.0; //all to zero by default
}
- const NodePath *K = nullptr;
- while ((K = filter.next(K))) {
- if (!state->track_map.has(*K)) {
+ for (const KeyValue<NodePath, bool> &E : filter) {
+ if (!state->track_map.has(E.key)) {
continue;
}
- int idx = state->track_map[*K];
+ int idx = state->track_map[E.key];
blendw[idx] = 1.0; //filtered goes to one
}
@@ -273,10 +272,6 @@ real_t AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
}
}
- if (!p_seek && p_optimize && !any_valid) { //pointless to go on, all are zero
- return 0;
- }
-
String new_path;
AnimationNode *new_parent;
@@ -289,6 +284,10 @@ real_t AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
new_parent = parent;
new_path = String(parent->base_path) + String(p_subpath) + "/";
}
+
+ if (!p_seek && p_optimize && !any_valid) {
+ return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_connections);
+ }
return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_connections);
}
@@ -314,7 +313,7 @@ void AnimationNode::add_input(const String &p_name) {
//root nodes can't add inputs
ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != nullptr);
Input input;
- ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1);
+ ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/"));
input.name = p_name;
inputs.push_back(input);
emit_changed();
@@ -322,14 +321,14 @@ void AnimationNode::add_input(const String &p_name) {
void AnimationNode::set_input_name(int p_input, const String &p_name) {
ERR_FAIL_INDEX(p_input, inputs.size());
- ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1);
+ ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/"));
inputs.write[p_input].name = p_name;
emit_changed();
}
void AnimationNode::remove_input(int p_index) {
ERR_FAIL_INDEX(p_index, inputs.size());
- inputs.remove(p_index);
+ inputs.remove_at(p_index);
emit_changed();
}
@@ -374,9 +373,8 @@ bool AnimationNode::has_filter() const {
Array AnimationNode::_get_filters() const {
Array paths;
- const NodePath *K = nullptr;
- while ((K = filter.next(K))) {
- paths.push_back(String(*K)); //use strings, so sorting is possible
+ for (const KeyValue<NodePath, bool> &E : filter) {
+ paths.push_back(String(E.key)); //use strings, so sorting is possible
}
paths.sort(); //done so every time the scene is saved, it does not change
@@ -488,9 +486,9 @@ void AnimationTree::set_active(bool p_active) {
}
if (!active && is_inside_tree()) {
- for (Set<TrackCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
+ for (RBSet<TrackCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
if (ObjectDB::get_instance(E->get()->object_id)) {
- E->get()->object->call("stop");
+ E->get()->object->call(SNAME("stop"));
}
}
@@ -540,6 +538,11 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
List<StringName> sname;
player->get_animation_list(&sname);
+ Ref<Animation> reset_anim;
+ bool has_reset_anim = player->has_animation(SceneStringNames::get_singleton()->RESET);
+ if (has_reset_anim) {
+ reset_anim = player->get_animation(SceneStringNames::get_singleton()->RESET);
+ }
for (const StringName &E : sname) {
Ref<Animation> anim = player->get_animation(E);
for (int i = 0; i < anim->get_track_count(); i++) {
@@ -565,7 +568,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
if (!track) {
- RES resource;
+ Ref<Resource> resource;
Vector<StringName> leftover_path;
Node *child = parent->get_node_and_resource(path, resource, leftover_path);
@@ -593,6 +596,12 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track = track_value;
+ if (has_reset_anim) {
+ int rt = reset_anim->find_track(path, track_type);
+ if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
+ track_value->init_value = reset_anim->track_get_key_value(rt, 0);
+ }
+ }
} break;
case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
@@ -612,12 +621,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_xform->skeleton = nullptr;
track_xform->bone_idx = -1;
+ bool has_rest = false;
if (path.get_subname_count() == 1 && Object::cast_to<Skeleton3D>(node_3d)) {
Skeleton3D *sk = Object::cast_to<Skeleton3D>(node_3d);
track_xform->skeleton = sk;
int bone_idx = sk->find_bone(path.get_subname(0));
if (bone_idx != -1) {
+ has_rest = true;
track_xform->bone_idx = bone_idx;
+ Transform3D rest = sk->get_bone_rest(bone_idx);
+ track_xform->init_loc = rest.origin;
+ track_xform->init_rot = rest.basis.get_rotation_quaternion();
+ track_xform->init_scale = rest.basis.get_scale();
}
}
@@ -640,11 +655,29 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
}
+ // For non Skeleton3D bone animation.
+ if (has_reset_anim && !has_rest) {
+ int rt = reset_anim->find_track(path, track_type);
+ if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
+ switch (track_type) {
+ case Animation::TYPE_POSITION_3D: {
+ track_xform->init_loc = reset_anim->track_get_key_value(rt, 0);
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ track_xform->init_rot = reset_anim->track_get_key_value(rt, 0);
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ track_xform->init_scale = reset_anim->track_get_key_value(rt, 0);
+ } break;
+ default: {
+ }
+ }
+ }
+ }
#endif // _3D_DISABLED
} break;
case Animation::TYPE_BLEND_SHAPE: {
#ifndef _3D_DISABLED
-
if (path.get_subname_count() != 1) {
ERR_PRINT("AnimationTree: '" + String(E) + "', blend shape track does not contain a blend shape subname: '" + String(path) + "'");
continue;
@@ -671,6 +704,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_bshape->object = mesh_3d;
track_bshape->object_id = mesh_3d->get_instance_id();
track = track_bshape;
+
+ if (has_reset_anim) {
+ int rt = reset_anim->find_track(path, track_type);
+ if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
+ track_bshape->init_value = reset_anim->track_get_key_value(rt, 0);
+ }
+ }
#endif
} break;
case Animation::TYPE_METHOD: {
@@ -700,6 +740,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_bezier->object_id = track_bezier->object->get_instance_id();
track = track_bezier;
+
+ if (has_reset_anim) {
+ int rt = reset_anim->find_track(path, track_type);
+ if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
+ track_bezier->init_value = reset_anim->track_get_key_value(rt, 0);
+ }
+ }
} break;
case Animation::TYPE_AUDIO: {
TrackCacheAudio *track_audio = memnew(TrackCacheAudio);
@@ -754,11 +801,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
List<NodePath> to_delete;
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- TrackCache *tc = track_cache[*K];
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ TrackCache *tc = track_cache[K.key];
if (tc->setup_pass != setup_pass) {
- to_delete.push_back(*K);
+ to_delete.push_back(K.key);
}
}
@@ -771,10 +817,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
state.track_map.clear();
- K = nullptr;
int idx = 0;
- while ((K = track_cache.next(K))) {
- state.track_map[*K] = idx;
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ state.track_map[K.key] = idx;
idx++;
}
@@ -786,9 +831,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
void AnimationTree::_clear_caches() {
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- memdelete(track_cache[*K]);
+ for (KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ memdelete(K.value);
}
playing_caches.clear();
@@ -796,7 +840,22 @@ void AnimationTree::_clear_caches() {
cache_valid = false;
}
-void AnimationTree::_process_graph(real_t p_delta) {
+static void _call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) {
+ // Separate function to use alloca() more efficiently
+ const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * p_params.size());
+ const Variant *args = p_params.ptr();
+ uint32_t argcount = p_params.size();
+ for (uint32_t i = 0; i < argcount; i++) {
+ argptrs[i] = &args[i];
+ }
+ if (p_deferred) {
+ MessageQueue::get_singleton()->push_callp(p_object, p_method, argptrs, argcount);
+ } else {
+ Callable::CallError ce;
+ p_object->callp(p_method, argptrs, argcount, ce);
+ }
+}
+void AnimationTree::_process_graph(double p_delta) {
_update_properties(); //if properties need updating, update them
//check all tracks, see if they need modification
@@ -927,24 +986,17 @@ void AnimationTree::_process_graph(real_t p_delta) {
real_t blend = (*as.track_blends)[blend_idx] * weight;
- if (blend < CMP_EPSILON) {
- continue; //nothing to blend
- }
-
switch (ttype) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
-
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = Vector3();
- t->rot = Quaternion();
- t->rot_blend_accum = 0;
- t->scale = Vector3(1, 1, 1);
- }
-
if (track->root_motion) {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3(0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
+ t->scale = Vector3(0, 0, 0);
+ }
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
@@ -1014,6 +1066,12 @@ void AnimationTree::_process_graph(real_t p_delta) {
prev_time = !backward ? 0 : (double)a->get_length();
} else {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = t->init_loc;
+ t->rot = t->init_rot;
+ t->scale = t->init_scale;
+ }
Vector3 loc;
Error err = a->position_track_interpolate(i, time, &loc);
@@ -1021,23 +1079,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
- t->loc = t->loc.lerp(loc, blend);
+ t->loc += (loc - t->init_loc) * blend;
}
#endif // _3D_DISABLED
} break;
case Animation::TYPE_ROTATION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
-
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = Vector3();
- t->rot = Quaternion();
- t->rot_blend_accum = 0;
- t->scale = Vector3(1, 1, 1);
- }
-
if (track->root_motion) {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3(0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
+ t->scale = Vector3(0, 0, 0);
+ }
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
@@ -1082,8 +1137,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
- Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
- t->rot = (t->rot * q).normalized();
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = 0;
}
} else {
@@ -1093,8 +1147,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
a->rotation_track_interpolate(i, 0, &rot[1]);
- Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
- t->rot = (t->rot * q).normalized();
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = 0;
}
}
@@ -1105,11 +1158,16 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->rotation_track_interpolate(i, time, &rot[1]);
- Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
- t->rot = (t->rot * q).normalized();
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = !backward ? 0 : (double)a->get_length();
} else {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = t->init_loc;
+ t->rot = t->init_rot;
+ t->scale = t->init_scale;
+ }
Quaternion rot;
Error err = a->rotation_track_interpolate(i, time, &rot);
@@ -1117,30 +1175,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
- if (t->rot_blend_accum == 0) {
- t->rot = rot;
- t->rot_blend_accum = blend;
- } else {
- real_t rot_total = t->rot_blend_accum + blend;
- t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
- t->rot_blend_accum = rot_total;
- }
+ t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized();
}
#endif // _3D_DISABLED
} break;
case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
-
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = Vector3();
- t->rot = Quaternion();
- t->rot_blend_accum = 0;
- t->scale = Vector3(1, 1, 1);
- }
-
if (track->root_motion) {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3(0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
+ t->scale = Vector3(0, 0, 0);
+ }
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
@@ -1210,6 +1258,12 @@ void AnimationTree::_process_graph(real_t p_delta) {
prev_time = !backward ? 0 : (double)a->get_length();
} else {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = t->init_loc;
+ t->rot = t->init_rot;
+ t->scale = t->init_scale;
+ }
Vector3 scale;
Error err = a->scale_track_interpolate(i, time, &scale);
@@ -1217,7 +1271,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
- t->scale = t->scale.lerp(scale, blend);
+ t->scale += (scale - t->init_scale) * blend;
}
#endif // _3D_DISABLED
} break;
@@ -1227,7 +1281,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
- t->value = 0;
+ t->value = t->init_value;
}
float value;
@@ -1239,8 +1293,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
- t->value = Math::lerp(t->value, value, blend);
-
+ t->value += (value - t->init_value) * blend;
#endif // _3D_DISABLED
} break;
case Animation::TYPE_VALUE: {
@@ -1257,13 +1310,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (t->process_pass != process_pass) {
- t->value = value;
t->process_pass = process_pass;
+ if (!t->init_value) {
+ t->init_value = value;
+ t->init_value.zero();
+ }
+ t->value = t->init_value;
}
- Variant::interpolate(t->value, value, blend, t->value);
-
+ Variant::sub(value, t->init_value, value);
+ Variant::blend(t->value, value, blend, t->value);
} else {
+ if (blend < CMP_EPSILON) {
+ continue; //nothing to blend
+ }
List<int> indices;
a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
@@ -1275,7 +1335,10 @@ void AnimationTree::_process_graph(real_t p_delta) {
} break;
case Animation::TYPE_METHOD: {
- if (delta == 0) {
+ if (blend < CMP_EPSILON) {
+ continue; //nothing to blend
+ }
+ if (!seeked && Math::is_zero_approx(delta)) {
continue;
}
TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
@@ -1287,25 +1350,10 @@ void AnimationTree::_process_graph(real_t p_delta) {
for (int &F : indices) {
StringName method = a->method_track_get_name(i, F);
Vector<Variant> params = a->method_track_get_params(i, F);
-
- int s = params.size();
-
- static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
- ERR_CONTINUE(s > VARIANT_ARG_MAX);
if (can_call) {
- t->object->call_deferred(
- method,
- s >= 1 ? params[0] : Variant(),
- s >= 2 ? params[1] : Variant(),
- s >= 3 ? params[2] : Variant(),
- s >= 4 ? params[3] : Variant(),
- s >= 5 ? params[4] : Variant(),
- s >= 6 ? params[5] : Variant(),
- s >= 7 ? params[6] : Variant(),
- s >= 8 ? params[7] : Variant());
+ _call_object(t->object, method, params, true);
}
}
-
} break;
case Animation::TYPE_BEZIER: {
TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
@@ -1313,14 +1361,16 @@ void AnimationTree::_process_graph(real_t p_delta) {
real_t bezier = a->bezier_track_interpolate(i, time);
if (t->process_pass != process_pass) {
- t->value = bezier;
t->process_pass = process_pass;
+ t->value = t->init_value;
}
- t->value = Math::lerp(t->value, bezier, blend);
-
+ t->value += (bezier - t->init_value) * blend;
} break;
case Animation::TYPE_AUDIO: {
+ if (blend < CMP_EPSILON) {
+ continue; //nothing to blend
+ }
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
if (seeked) {
@@ -1332,24 +1382,24 @@ void AnimationTree::_process_graph(real_t p_delta) {
Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
if (!stream.is_valid()) {
- t->object->call("stop");
+ t->object->call(SNAME("stop"));
t->playing = false;
playing_caches.erase(t);
} else {
- real_t start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ double start_ofs = a->audio_track_get_key_start_offset(i, idx);
start_ofs += time - a->track_get_key_time(i, idx);
- real_t end_ofs = a->audio_track_get_key_end_offset(i, idx);
- real_t len = stream->get_length();
+ double end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ double len = stream->get_length();
if (start_ofs > len - end_ofs) {
- t->object->call("stop");
+ t->object->call(SNAME("stop"));
t->playing = false;
playing_caches.erase(t);
continue;
}
- t->object->call("set_stream", stream);
- t->object->call("play", start_ofs);
+ t->object->call(SNAME("set_stream"), stream);
+ t->object->call(SNAME("play"), start_ofs);
t->playing = true;
playing_caches.insert(t);
@@ -1371,16 +1421,16 @@ void AnimationTree::_process_graph(real_t p_delta) {
Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
if (!stream.is_valid()) {
- t->object->call("stop");
+ t->object->call(SNAME("stop"));
t->playing = false;
playing_caches.erase(t);
} else {
- real_t start_ofs = a->audio_track_get_key_start_offset(i, idx);
- real_t end_ofs = a->audio_track_get_key_end_offset(i, idx);
- real_t len = stream->get_length();
+ double start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ double end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ double len = stream->get_length();
- t->object->call("set_stream", stream);
- t->object->call("play", start_ofs);
+ t->object->call(SNAME("set_stream"), stream);
+ t->object->call(SNAME("play"), start_ofs);
t->playing = true;
playing_caches.insert(t);
@@ -1408,7 +1458,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
}
} else if (t->len > 0) {
- real_t len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
+ double len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
if (len > t->len) {
stop = true;
@@ -1417,7 +1467,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
if (stop) {
//time to stop
- t->object->call("stop");
+ t->object->call(SNAME("stop"));
t->playing = false;
playing_caches.erase(t);
}
@@ -1425,13 +1475,16 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
real_t db = Math::linear2db(MAX(blend, 0.00001));
- if (t->object->has_method("set_unit_db")) {
- t->object->call("set_unit_db", db);
+ if (t->object->has_method(SNAME("set_unit_db"))) {
+ t->object->call(SNAME("set_unit_db"), db);
} else {
- t->object->call("set_volume_db", db);
+ t->object->call(SNAME("set_volume_db"), db);
}
} break;
case Animation::TYPE_ANIMATION: {
+ if (blend < CMP_EPSILON) {
+ continue; //nothing to blend
+ }
TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track);
AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object);
@@ -1456,7 +1509,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
Ref<Animation> anim = player2->get_animation(anim_name);
- real_t at_anim_pos = 0.0;
+ double at_anim_pos = 0.0;
switch (anim->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
@@ -1511,9 +1564,8 @@ void AnimationTree::_process_graph(real_t p_delta) {
{
// finally, set the tracks
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- TrackCache *track = track_cache[*K];
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ TrackCache *track = K.value;
if (track->process_pass != process_pass) {
continue; //not processed, ignore
}
@@ -1526,7 +1578,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
if (t->root_motion) {
Transform3D xform;
xform.origin = t->loc;
- xform.basis.set_quaternion_scale(t->rot, t->scale);
+ xform.basis.set_quaternion_scale(t->rot, Vector3(1, 1, 1) + t->scale);
root_motion_transform = xform;
@@ -1587,29 +1639,37 @@ void AnimationTree::advance(real_t p_time) {
}
void AnimationTree::_notification(int p_what) {
- if (active && p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS && process_callback == ANIMATION_PROCESS_PHYSICS) {
- _process_graph(get_physics_process_delta_time());
- }
-
- if (active && p_what == NOTIFICATION_INTERNAL_PROCESS && process_callback == ANIMATION_PROCESS_IDLE) {
- _process_graph(get_process_delta_time());
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (last_animation_player.is_valid()) {
+ Object *player = ObjectDB::get_instance(last_animation_player);
+ if (player) {
+ player->connect("caches_cleared", callable_mp(this, &AnimationTree::_clear_caches));
+ }
+ }
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ _clear_caches();
+ if (last_animation_player.is_valid()) {
+ Object *player = ObjectDB::get_instance(last_animation_player);
+ if (player) {
+ player->disconnect("caches_cleared", callable_mp(this, &AnimationTree::_clear_caches));
+ }
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- _clear_caches();
- if (last_animation_player.is_valid()) {
- Object *player = ObjectDB::get_instance(last_animation_player);
- if (player) {
- player->disconnect("caches_cleared", callable_mp(this, &AnimationTree::_clear_caches));
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (active && process_callback == ANIMATION_PROCESS_IDLE) {
+ _process_graph(get_process_delta_time());
}
- }
- } else if (p_what == NOTIFICATION_ENTER_TREE) {
- if (last_animation_player.is_valid()) {
- Object *player = ObjectDB::get_instance(last_animation_player);
- if (player) {
- player->connect("caches_cleared", callable_mp(this, &AnimationTree::_clear_caches));
+ } break;
+
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (active && process_callback == ANIMATION_PROCESS_PHYSICS) {
+ _process_graph(get_physics_process_delta_time());
}
- }
+ } break;
}
}
@@ -1638,18 +1698,18 @@ TypedArray<String> AnimationTree::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!root.is_valid()) {
- warnings.push_back(TTR("No root AnimationNode for the graph is set."));
+ warnings.push_back(RTR("No root AnimationNode for the graph is set."));
}
if (!has_node(animation_player)) {
- warnings.push_back(TTR("Path to an AnimationPlayer node containing animations is not set."));
+ warnings.push_back(RTR("Path to an AnimationPlayer node containing animations is not set."));
} else {
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));
if (!player) {
- warnings.push_back(TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."));
+ warnings.push_back(RTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."));
} else if (!player->has_node(player->get_root())) {
- warnings.push_back(TTR("The AnimationPlayer root node is not a valid node."));
+ warnings.push_back(RTR("The AnimationPlayer root node is not a valid node."));
}
}
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 6fc051fa41..d40d4ccbbd 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,6 +37,8 @@
#include "scene/resources/animation.h"
class AnimationNodeBlendTree;
+class AnimationNodeStartState;
+class AnimationNodeEndState;
class AnimationPlayer;
class AnimationTree;
@@ -83,7 +85,7 @@ public:
Vector<real_t> blends;
State *state = nullptr;
- real_t _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, real_t p_time, bool p_seek, const Vector<StringName> &p_connections);
+ double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections);
//all this is temporary
StringName base_path;
@@ -96,12 +98,12 @@ public:
Array _get_filters() const;
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
- real_t _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
+ double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
protected:
- void blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
- real_t blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
- real_t blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+ void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
+ double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+ double blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
void make_invalid(const String &p_reason);
@@ -164,6 +166,14 @@ public:
AnimationRootNode() {}
};
+class AnimationNodeStartState : public AnimationRootNode {
+ GDCLASS(AnimationNodeStartState, AnimationRootNode);
+};
+
+class AnimationNodeEndState : public AnimationRootNode {
+ GDCLASS(AnimationNodeEndState, AnimationRootNode);
+};
+
class AnimationTree : public Node {
GDCLASS(AnimationTree, Node);
@@ -197,9 +207,11 @@ 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;
Quaternion rot;
- real_t rot_blend_accum = 0.0;
Vector3 scale;
TrackCacheTransform() {
@@ -209,12 +221,14 @@ private:
struct TrackCacheBlendShape : public TrackCache {
MeshInstance3D *mesh_3d = nullptr;
+ float init_value = 0;
float value = 0;
int shape_index = -1;
TrackCacheBlendShape() { type = Animation::TYPE_BLEND_SHAPE; }
};
struct TrackCacheValue : public TrackCache {
+ Variant init_value;
Variant value;
Vector<StringName> subpath;
TrackCacheValue() { type = Animation::TYPE_VALUE; }
@@ -225,6 +239,7 @@ private:
};
struct TrackCacheBezier : public TrackCache {
+ real_t init_value = 0.0;
real_t value = 0.0;
Vector<StringName> subpath;
TrackCacheBezier() {
@@ -234,8 +249,8 @@ private:
struct TrackCacheAudio : public TrackCache {
bool playing = false;
- real_t start = 0.0;
- real_t len = 0.0;
+ double start = 0.0;
+ double len = 0.0;
TrackCacheAudio() {
type = Animation::TYPE_AUDIO;
@@ -251,7 +266,7 @@ private:
};
HashMap<NodePath, TrackCache *> track_cache;
- Set<TrackCache *> playing_caches;
+ RBSet<TrackCache *> playing_caches;
Ref<AnimationNode> root;
@@ -265,7 +280,7 @@ private:
void _clear_caches();
bool _update_caches(AnimationPlayer *player);
- void _process_graph(real_t p_delta);
+ void _process_graph(double p_delta);
uint64_t setup_pass = 1;
uint64_t process_pass = 1;
diff --git a/scene/animation/easing_equations.h b/scene/animation/easing_equations.h
index c38d083b7f..6d246c7a93 100644
--- a/scene/animation/easing_equations.h
+++ b/scene/animation/easing_equations.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp
index 770996820d..3192f5f7cd 100644
--- a/scene/animation/root_motion_view.cpp
+++ b/scene/animation/root_motion_view.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,8 +29,10 @@
/*************************************************************************/
#include "root_motion_view.h"
+
#include "scene/animation/animation_tree.h"
#include "scene/resources/material.h"
+
void RootMotionView::set_animation_path(const NodePath &p_path) {
path = p_path;
first = true;
@@ -76,84 +78,86 @@ bool RootMotionView::get_zero_y() const {
}
void RootMotionView::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- immediate_material = StandardMaterial3D::get_material_for_2d(false, true, false, false, false);
- first = true;
- }
-
- if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- Transform3D transform;
-
- if (has_node(path)) {
- Node *node = get_node(path);
-
- AnimationTree *tree = Object::cast_to<AnimationTree>(node);
- if (tree && tree->is_active() && tree->get_root_motion_track() != NodePath()) {
- if (is_processing_internal() && tree->get_process_callback() == AnimationTree::ANIMATION_PROCESS_PHYSICS) {
- set_process_internal(false);
- set_physics_process_internal(true);
- }
-
- if (is_physics_processing_internal() && tree->get_process_callback() == AnimationTree::ANIMATION_PROCESS_IDLE) {
- set_process_internal(true);
- set_physics_process_internal(false);
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ immediate_material = StandardMaterial3D::get_material_for_2d(false, true, false, false, false);
+ first = true;
+ } break;
+
+ case NOTIFICATION_INTERNAL_PROCESS:
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ Transform3D transform;
+
+ if (has_node(path)) {
+ Node *node = get_node(path);
+
+ AnimationTree *tree = Object::cast_to<AnimationTree>(node);
+ if (tree && tree->is_active() && tree->get_root_motion_track() != NodePath()) {
+ if (is_processing_internal() && tree->get_process_callback() == AnimationTree::ANIMATION_PROCESS_PHYSICS) {
+ set_process_internal(false);
+ set_physics_process_internal(true);
+ }
+
+ if (is_physics_processing_internal() && tree->get_process_callback() == AnimationTree::ANIMATION_PROCESS_IDLE) {
+ set_process_internal(true);
+ set_physics_process_internal(false);
+ }
+
+ transform = tree->get_root_motion_transform();
}
-
- transform = tree->get_root_motion_transform();
}
- }
- if (!first && transform == Transform3D()) {
- return;
- }
+ if (!first && transform == Transform3D()) {
+ return;
+ }
- first = false;
+ first = false;
- transform.orthonormalize(); //don't want scale, too imprecise
- transform.affine_invert();
+ transform.orthonormalize(); //don't want scale, too imprecise
- accumulated = transform * accumulated;
- accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
- if (zero_y) {
- accumulated.origin.y = 0;
- }
- accumulated.origin.z = Math::fposmod(accumulated.origin.z, cell_size);
+ accumulated = accumulated * transform;
+ accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
+ if (zero_y) {
+ accumulated.origin.y = 0;
+ }
+ accumulated.origin.z = Math::fposmod(accumulated.origin.z, cell_size);
- immediate->clear_surfaces();
+ immediate->clear_surfaces();
- int cells_in_radius = int((radius / cell_size) + 1.0);
+ int cells_in_radius = int((radius / cell_size) + 1.0);
- immediate->surface_begin(Mesh::PRIMITIVE_LINES, immediate_material);
+ immediate->surface_begin(Mesh::PRIMITIVE_LINES, immediate_material);
- for (int i = -cells_in_radius; i < cells_in_radius; i++) {
- for (int j = -cells_in_radius; j < cells_in_radius; j++) {
- Vector3 from(i * cell_size, 0, j * cell_size);
- Vector3 from_i((i + 1) * cell_size, 0, j * cell_size);
- Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size);
- from = accumulated.xform(from);
- from_i = accumulated.xform(from_i);
- from_j = accumulated.xform(from_j);
+ for (int i = -cells_in_radius; i < cells_in_radius; i++) {
+ for (int j = -cells_in_radius; j < cells_in_radius; j++) {
+ Vector3 from(i * cell_size, 0, j * cell_size);
+ Vector3 from_i((i + 1) * cell_size, 0, j * cell_size);
+ Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size);
+ from = accumulated.xform_inv(from);
+ from_i = accumulated.xform_inv(from_i);
+ from_j = accumulated.xform_inv(from_j);
- Color c = color, c_i = color, c_j = color;
- c.a *= MAX(0, 1.0 - from.length() / radius);
- c_i.a *= MAX(0, 1.0 - from_i.length() / radius);
- c_j.a *= MAX(0, 1.0 - from_j.length() / radius);
+ Color c = color, c_i = color, c_j = color;
+ c.a *= MAX(0, 1.0 - from.length() / radius);
+ c_i.a *= MAX(0, 1.0 - from_i.length() / radius);
+ c_j.a *= MAX(0, 1.0 - from_j.length() / radius);
- immediate->surface_set_color(c);
- immediate->surface_add_vertex(from);
+ immediate->surface_set_color(c);
+ immediate->surface_add_vertex(from);
- immediate->surface_set_color(c_i);
- immediate->surface_add_vertex(from_i);
+ immediate->surface_set_color(c_i);
+ immediate->surface_add_vertex(from_i);
- immediate->surface_set_color(c);
- immediate->surface_add_vertex(from);
+ immediate->surface_set_color(c);
+ immediate->surface_add_vertex(from);
- immediate->surface_set_color(c_j);
- immediate->surface_add_vertex(from_j);
+ immediate->surface_set_color(c_j);
+ immediate->surface_add_vertex(from_j);
+ }
}
- }
- immediate->surface_end();
+ immediate->surface_end();
+ } break;
}
}
@@ -161,10 +165,6 @@ AABB RootMotionView::get_aabb() const {
return AABB(Vector3(-radius, 0, -radius), Vector3(radius * 2, 0.001, radius * 2));
}
-Vector<Face3> RootMotionView::get_faces(uint32_t p_usage_flags) const {
- return Vector<Face3>();
-}
-
void RootMotionView::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation_path", "path"), &RootMotionView::set_animation_path);
ClassDB::bind_method(D_METHOD("get_animation_path"), &RootMotionView::get_animation_path);
diff --git a/scene/animation/root_motion_view.h b/scene/animation/root_motion_view.h
index d64c8bc675..8cb8ea8a9a 100644
--- a/scene/animation/root_motion_view.h
+++ b/scene/animation/root_motion_view.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -71,7 +71,6 @@ public:
bool get_zero_y() const;
virtual AABB get_aabb() const override;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
RootMotionView();
~RootMotionView();
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index da933ae02d..5457da472f 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -130,6 +130,7 @@ void Tween::stop() {
started = false;
running = false;
dead = false;
+ total_time = 0;
}
void Tween::pause() {
@@ -151,10 +152,6 @@ bool Tween::is_running() {
return running;
}
-void Tween::set_valid(bool p_valid) {
- valid = p_valid;
-}
-
bool Tween::is_valid() {
return valid;
}
@@ -249,8 +246,6 @@ bool Tween::custom_step(float p_delta) {
}
bool Tween::step(float p_delta) {
- ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners.");
-
if (dead) {
return false;
}
@@ -260,10 +255,8 @@ bool Tween::step(float p_delta) {
}
if (is_bound) {
- Object *bound_instance = ObjectDB::get_instance(bound_node);
- if (bound_instance) {
- Node *bound_node = Object::cast_to<Node>(bound_instance);
- // This can't by anything else than Node, so we can omit checking if casting succeeded.
+ Node *bound_node = get_bound_node();
+ if (bound_node) {
if (!bound_node->is_inside_tree()) {
return true;
}
@@ -273,14 +266,22 @@ bool Tween::step(float p_delta) {
}
if (!started) {
+ ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners.");
current_step = 0;
loops_done = 0;
+ total_time = 0;
start_tweeners();
started = true;
}
float rem_delta = p_delta * speed_scale;
bool step_active = false;
+ total_time += rem_delta;
+
+#ifdef DEBUG_ENABLED
+ float initial_delta = rem_delta;
+ bool potential_infinite = false;
+#endif
while (rem_delta > 0 && running) {
float step_delta = rem_delta;
@@ -310,6 +311,16 @@ bool Tween::step(float p_delta) {
emit_signal(SNAME("loop_finished"), loops_done);
current_step = 0;
start_tweeners();
+#ifdef DEBUG_ENABLED
+ if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) {
+ if (!potential_infinite) {
+ potential_infinite = true;
+ } else {
+ // Looped twice without using any time, this is 100% certain infinite loop.
+ ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
+ }
+ }
+#endif
}
} else {
start_tweeners();
@@ -320,16 +331,27 @@ bool Tween::step(float p_delta) {
return true;
}
-bool Tween::should_pause() {
+bool Tween::can_process(bool p_tree_paused) const {
if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) {
- Object *bound_instance = ObjectDB::get_instance(bound_node);
- if (bound_instance) {
- Node *bound_node = Object::cast_to<Node>(bound_instance);
- return !bound_node->can_process();
+ Node *bound_node = get_bound_node();
+ if (bound_node) {
+ return bound_node->is_inside_tree() && bound_node->can_process();
}
}
- return pause_mode != TWEEN_PAUSE_PROCESS;
+ return !p_tree_paused || pause_mode == TWEEN_PAUSE_PROCESS;
+}
+
+Node *Tween::get_bound_node() const {
+ if (is_bound) {
+ return Object::cast_to<Node>(ObjectDB::get_instance(bound_node));
+ } else {
+ return nullptr;
+ }
+}
+
+float Tween::get_total_time() const {
+ return total_time;
}
real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) {
@@ -434,12 +456,12 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Transform2D d = p_delta_val;
Transform2D r;
- APPLY_EQUATION(elements[0][0]);
- APPLY_EQUATION(elements[0][1]);
- APPLY_EQUATION(elements[1][0]);
- APPLY_EQUATION(elements[1][1]);
- APPLY_EQUATION(elements[2][0]);
- APPLY_EQUATION(elements[2][1]);
+ APPLY_EQUATION(columns[0][0]);
+ APPLY_EQUATION(columns[0][1]);
+ APPLY_EQUATION(columns[1][0]);
+ APPLY_EQUATION(columns[1][1]);
+ APPLY_EQUATION(columns[2][0]);
+ APPLY_EQUATION(columns[2][1]);
return r;
}
@@ -474,15 +496,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Basis d = p_delta_val;
Basis r;
- APPLY_EQUATION(elements[0][0]);
- APPLY_EQUATION(elements[0][1]);
- APPLY_EQUATION(elements[0][2]);
- APPLY_EQUATION(elements[1][0]);
- APPLY_EQUATION(elements[1][1]);
- APPLY_EQUATION(elements[1][2]);
- APPLY_EQUATION(elements[2][0]);
- APPLY_EQUATION(elements[2][1]);
- APPLY_EQUATION(elements[2][2]);
+ APPLY_EQUATION(rows[0][0]);
+ APPLY_EQUATION(rows[0][1]);
+ APPLY_EQUATION(rows[0][2]);
+ APPLY_EQUATION(rows[1][0]);
+ APPLY_EQUATION(rows[1][1]);
+ APPLY_EQUATION(rows[1][2]);
+ APPLY_EQUATION(rows[2][0]);
+ APPLY_EQUATION(rows[2][1]);
+ APPLY_EQUATION(rows[2][2]);
return r;
}
@@ -491,15 +513,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Transform3D d = p_delta_val;
Transform3D r;
- APPLY_EQUATION(basis.elements[0][0]);
- APPLY_EQUATION(basis.elements[0][1]);
- APPLY_EQUATION(basis.elements[0][2]);
- APPLY_EQUATION(basis.elements[1][0]);
- APPLY_EQUATION(basis.elements[1][1]);
- APPLY_EQUATION(basis.elements[1][2]);
- APPLY_EQUATION(basis.elements[2][0]);
- APPLY_EQUATION(basis.elements[2][1]);
- APPLY_EQUATION(basis.elements[2][2]);
+ APPLY_EQUATION(basis.rows[0][0]);
+ APPLY_EQUATION(basis.rows[0][1]);
+ APPLY_EQUATION(basis.rows[0][2]);
+ APPLY_EQUATION(basis.rows[1][0]);
+ APPLY_EQUATION(basis.rows[1][1]);
+ APPLY_EQUATION(basis.rows[1][2]);
+ APPLY_EQUATION(basis.rows[2][0]);
+ APPLY_EQUATION(basis.rows[2][1]);
+ APPLY_EQUATION(basis.rows[2][2]);
APPLY_EQUATION(origin.x);
APPLY_EQUATION(origin.y);
APPLY_EQUATION(origin.z);
@@ -548,12 +570,12 @@ Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val)
case Variant::TRANSFORM2D: {
Transform2D i = p_intial_val;
Transform2D f = p_final_val;
- return Transform2D(f.elements[0][0] - i.elements[0][0],
- f.elements[0][1] - i.elements[0][1],
- f.elements[1][0] - i.elements[1][0],
- f.elements[1][1] - i.elements[1][1],
- f.elements[2][0] - i.elements[2][0],
- f.elements[2][1] - i.elements[2][1]);
+ return Transform2D(f.columns[0][0] - i.columns[0][0],
+ f.columns[0][1] - i.columns[0][1],
+ f.columns[1][0] - i.columns[1][0],
+ f.columns[1][1] - i.columns[1][1],
+ f.columns[2][0] - i.columns[2][0],
+ f.columns[2][1] - i.columns[2][1]);
}
case Variant::AABB: {
@@ -565,29 +587,29 @@ Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val)
case Variant::BASIS: {
Basis i = p_intial_val;
Basis f = p_final_val;
- return Basis(f.elements[0][0] - i.elements[0][0],
- f.elements[0][1] - i.elements[0][1],
- f.elements[0][2] - i.elements[0][2],
- f.elements[1][0] - i.elements[1][0],
- f.elements[1][1] - i.elements[1][1],
- f.elements[1][2] - i.elements[1][2],
- f.elements[2][0] - i.elements[2][0],
- f.elements[2][1] - i.elements[2][1],
- f.elements[2][2] - i.elements[2][2]);
+ return Basis(f.rows[0][0] - i.rows[0][0],
+ f.rows[0][1] - i.rows[0][1],
+ f.rows[0][2] - i.rows[0][2],
+ f.rows[1][0] - i.rows[1][0],
+ f.rows[1][1] - i.rows[1][1],
+ f.rows[1][2] - i.rows[1][2],
+ f.rows[2][0] - i.rows[2][0],
+ f.rows[2][1] - i.rows[2][1],
+ f.rows[2][2] - i.rows[2][2]);
}
case Variant::TRANSFORM3D: {
Transform3D i = p_intial_val;
Transform3D f = p_final_val;
- return Transform3D(f.basis.elements[0][0] - i.basis.elements[0][0],
- f.basis.elements[0][1] - i.basis.elements[0][1],
- f.basis.elements[0][2] - i.basis.elements[0][2],
- f.basis.elements[1][0] - i.basis.elements[1][0],
- f.basis.elements[1][1] - i.basis.elements[1][1],
- f.basis.elements[1][2] - i.basis.elements[1][2],
- f.basis.elements[2][0] - i.basis.elements[2][0],
- f.basis.elements[2][1] - i.basis.elements[2][1],
- f.basis.elements[2][2] - i.basis.elements[2][2],
+ return Transform3D(f.basis.rows[0][0] - i.basis.rows[0][0],
+ f.basis.rows[0][1] - i.basis.rows[0][1],
+ f.basis.rows[0][2] - i.basis.rows[0][2],
+ f.basis.rows[1][0] - i.basis.rows[1][0],
+ f.basis.rows[1][1] - i.basis.rows[1][1],
+ f.basis.rows[1][2] - i.basis.rows[1][2],
+ f.basis.rows[2][0] - i.basis.rows[2][0],
+ f.basis.rows[2][1] - i.basis.rows[2][1],
+ f.basis.rows[2][2] - i.basis.rows[2][2],
f.origin.x - i.origin.x,
f.origin.y - i.origin.y,
f.origin.z - i.origin.z);
@@ -610,6 +632,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("pause"), &Tween::pause);
ClassDB::bind_method(D_METHOD("play"), &Tween::play);
ClassDB::bind_method(D_METHOD("kill"), &Tween::kill);
+ ClassDB::bind_method(D_METHOD("get_total_elapsed_time"), &Tween::get_total_time);
ClassDB::bind_method(D_METHOD("is_running"), &Tween::is_running);
ClassDB::bind_method(D_METHOD("is_valid"), &Tween::is_valid);
@@ -626,7 +649,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel);
ClassDB::bind_method(D_METHOD("chain"), &Tween::chain);
- ClassDB::bind_method(D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
+ ClassDB::bind_static_method("Tween", D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx")));
ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count")));
@@ -657,6 +680,14 @@ void Tween::_bind_methods() {
BIND_ENUM_CONSTANT(EASE_OUT_IN);
}
+Tween::Tween() {
+ ERR_FAIL_MSG("Tween can't be created directly. Use create_tween() method.");
+}
+
+Tween::Tween(bool p_valid) {
+ valid = p_valid;
+}
+
Ref<PropertyTweener> PropertyTweener::from(Variant p_value) {
initial_val = p_value;
do_continue = false;
@@ -727,12 +758,12 @@ bool PropertyTweener::step(float &r_delta) {
}
float time = MIN(elapsed_time - delay, duration);
- target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
-
if (time < duration) {
+ target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
r_delta = 0;
return true;
} else {
+ target_instance->set_indexed(property, final_val);
finished = true;
r_delta = elapsed_time - delay - duration;
emit_signal(SNAME("finished"));
@@ -824,7 +855,7 @@ bool CallbackTweener::step(float &r_delta) {
Callable::CallError ce;
callback.call(nullptr, 0, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_call_error_text(this, callback.get_method(), nullptr, 0, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce));
}
finished = true;
@@ -881,8 +912,13 @@ bool MethodTweener::step(float &r_delta) {
return true;
}
+ Variant current_val;
float time = MIN(elapsed_time - delay, duration);
- Variant current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type);
+ if (time < duration) {
+ current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type);
+ } else {
+ current_val = final_val;
+ }
const Variant **argptr = (const Variant **)alloca(sizeof(Variant *));
argptr[0] = &current_val;
@@ -890,7 +926,7 @@ bool MethodTweener::step(float &r_delta) {
Callable::CallError ce;
callback.call(argptr, 1, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_call_error_text(this, callback.get_method(), argptr, 1, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce));
}
if (time < duration) {
@@ -924,6 +960,7 @@ MethodTweener::MethodTweener(Callable p_callback, Variant p_from, Variant p_to,
callback = p_callback;
initial_val = p_from;
delta_val = tween->calculate_delta_value(p_from, p_to);
+ final_val = p_to;
duration = p_duration;
}
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 6a48d332b8..40268405cf 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -97,12 +97,13 @@ public:
private:
TweenProcessMode process_mode = TweenProcessMode::TWEEN_PROCESS_IDLE;
- TweenPauseMode pause_mode = TweenPauseMode::TWEEN_PAUSE_STOP;
+ TweenPauseMode pause_mode = TweenPauseMode::TWEEN_PAUSE_BOUND;
TransitionType default_transition = TransitionType::TRANS_LINEAR;
EaseType default_ease = EaseType::EASE_IN_OUT;
ObjectID bound_node;
Vector<List<Ref<Tweener>>> tweeners;
+ float total_time = 0;
int current_step = -1;
int loops = 1;
int loops_done = 0;
@@ -115,6 +116,9 @@ private:
bool valid = false;
bool default_parallel = false;
bool parallel_enabled = false;
+#ifdef DEBUG_ENABLED
+ bool is_infinite = false;
+#endif
typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
static interpolater interpolaters[TRANS_MAX][EASE_MAX];
@@ -138,7 +142,6 @@ public:
void kill();
bool is_running();
- void set_valid(bool p_valid);
bool is_valid();
void clear();
@@ -159,14 +162,17 @@ public:
Ref<Tween> parallel();
Ref<Tween> chain();
- real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
- Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease);
+ static real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
+ static Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease);
Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val);
bool step(float p_delta);
- bool should_pause();
+ bool can_process(bool p_tree_paused) const;
+ Node *get_bound_node() const;
+ float get_total_time() const;
- Tween() {}
+ Tween();
+ Tween(bool p_valid);
};
VARIANT_ENUM_CAST(Tween::TweenPauseMode);
@@ -273,6 +279,7 @@ private:
Ref<Tween> tween;
Variant initial_val;
Variant delta_val;
+ Variant final_val;
Callable callback;
};
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 43c4ce4c82..c8e8ff1cd1 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,49 +35,51 @@
#include "servers/audio_server.h"
void AudioStreamPlayer::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
- play();
- }
- }
-
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
- for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
- if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
- playbacks_to_remove.push_back(playback);
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
+ play();
}
- }
- // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
- for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
- stream_playbacks.erase(playback);
- }
- if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
- // This node is no longer actively playing audio.
- active.clear();
- set_process_internal(false);
- }
- if (!playbacks_to_remove.is_empty()) {
- emit_signal(SNAME("finished"));
- }
- }
+ } break;
+
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
+ playbacks_to_remove.push_back(playback);
+ }
+ }
+ // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
+ for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
+ stream_playbacks.erase(playback);
+ }
+ if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
+ // This node is no longer actively playing audio.
+ active.clear();
+ set_process_internal(false);
+ }
+ if (!playbacks_to_remove.is_empty()) {
+ emit_signal(SNAME("finished"));
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
- AudioServer::get_singleton()->stop_playback_stream(playback);
- }
- stream_playbacks.clear();
- }
+ case NOTIFICATION_EXIT_TREE: {
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->stop_playback_stream(playback);
+ }
+ stream_playbacks.clear();
+ } break;
- if (p_what == NOTIFICATION_PAUSED) {
- if (!can_process()) {
- // Node can't process so we start fading out to silence
- set_stream_paused(true);
- }
- }
+ case NOTIFICATION_PAUSED: {
+ if (!can_process()) {
+ // Node can't process so we start fading out to silence
+ set_stream_paused(true);
+ }
+ } break;
- if (p_what == NOTIFICATION_UNPAUSED) {
- set_stream_paused(false);
+ case NOTIFICATION_UNPAUSED: {
+ set_stream_paused(false);
+ } break;
}
}
@@ -143,7 +145,7 @@ void AudioStreamPlayer::play(float p_from_pos) {
set_process_internal(true);
while (stream_playbacks.size() > max_polyphony) {
AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
- stream_playbacks.remove(0);
+ stream_playbacks.remove_at(0);
}
}
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index 7205fce204..67e616312a 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index 3c8949ddfb..b792b11dbc 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,30 +31,122 @@
#include "scene_debugger.h"
#include "core/debugger/engine_debugger.h"
+#include "core/debugger/engine_profiler.h"
#include "core/io/marshalls.h"
#include "core/object/script_language.h"
+#include "core/templates/local_vector.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
-void SceneDebugger::initialize() {
+Array SceneDebugger::RPCProfilerFrame::serialize() {
+ Array arr;
+ arr.push_back(infos.size() * 4);
+ for (int i = 0; i < infos.size(); ++i) {
+ arr.push_back(uint64_t(infos[i].node));
+ arr.push_back(infos[i].node_path);
+ arr.push_back(infos[i].incoming_rpc);
+ arr.push_back(infos[i].outgoing_rpc);
+ }
+ return arr;
+}
+
+bool SceneDebugger::RPCProfilerFrame::deserialize(const Array &p_arr) {
+ ERR_FAIL_COND_V(p_arr.size() < 1, false);
+ uint32_t size = p_arr[0];
+ ERR_FAIL_COND_V(size % 4, false);
+ ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
+ infos.resize(size / 4);
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 4; ++i) {
+ infos.write[i].node = uint64_t(p_arr[idx]);
+ infos.write[i].node_path = p_arr[idx + 1];
+ infos.write[i].incoming_rpc = p_arr[idx + 2];
+ infos.write[i].outgoing_rpc = p_arr[idx + 3];
+ }
+ return true;
+}
+
+class SceneDebugger::RPCProfiler : public EngineProfiler {
+ HashMap<ObjectID, RPCNodeInfo> rpc_node_data;
+ uint64_t last_profile_time = 0;
+
+ void init_node(const ObjectID p_node) {
+ if (rpc_node_data.has(p_node)) {
+ return;
+ }
+ rpc_node_data.insert(p_node, RPCNodeInfo());
+ rpc_node_data[p_node].node = p_node;
+ rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
+ rpc_node_data[p_node].incoming_rpc = 0;
+ rpc_node_data[p_node].outgoing_rpc = 0;
+ }
+
+public:
+ void toggle(bool p_enable, const Array &p_opts) {
+ rpc_node_data.clear();
+ }
+
+ void add(const Array &p_data) {
+ ERR_FAIL_COND(p_data.size() < 2);
+ const ObjectID id = p_data[0];
+ const String what = p_data[1];
+ init_node(id);
+ RPCNodeInfo &info = rpc_node_data[id];
+ if (what == "rpc_in") {
+ info.incoming_rpc++;
+ } else if (what == "rpc_out") {
+ info.outgoing_rpc++;
+ }
+ }
+
+ void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_profile_time > 100) {
+ last_profile_time = pt;
+ RPCProfilerFrame frame;
+ for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {
+ frame.infos.push_back(E.value);
+ }
+ rpc_node_data.clear();
+ EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());
+ }
+ }
+};
+
+SceneDebugger *SceneDebugger::singleton = nullptr;
+
+SceneDebugger::SceneDebugger() {
+ singleton = this;
+ rpc_profiler.instantiate();
+ rpc_profiler->bind("rpc");
#ifdef DEBUG_ENABLED
LiveEditor::singleton = memnew(LiveEditor);
EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message));
#endif
}
-void SceneDebugger::deinitialize() {
+SceneDebugger::~SceneDebugger() {
#ifdef DEBUG_ENABLED
if (LiveEditor::singleton) {
- // Should be removed automatically when deiniting debugger, but just in case
- if (EngineDebugger::has_capture("scene")) {
- EngineDebugger::unregister_message_capture("scene");
- }
+ EngineDebugger::unregister_message_capture("scene");
memdelete(LiveEditor::singleton);
LiveEditor::singleton = nullptr;
}
#endif
+ singleton = nullptr;
+}
+
+void SceneDebugger::initialize() {
+ if (EngineDebugger::is_active()) {
+ memnew(SceneDebugger);
+ }
+}
+
+void SceneDebugger::deinitialize() {
+ if (singleton) {
+ memdelete(singleton);
+ }
}
#ifdef DEBUG_ENABLED
@@ -145,12 +237,28 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra
live_editor->_res_set_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_node_call") {
- ERR_FAIL_COND_V(p_args.size() < 10, ERR_INVALID_DATA);
- live_editor->_node_call_func(p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5], p_args[6], p_args[7], p_args[8], p_args[9]);
+ LocalVector<Variant> args;
+ LocalVector<Variant *> argptrs;
+ args.resize(p_args.size() - 2);
+ argptrs.resize(args.size());
+ for (uint32_t i = 0; i < args.size(); i++) {
+ args[i] = p_args[i + 2];
+ argptrs[i] = &args[i];
+ }
+ live_editor->_node_call_func(p_args[0], p_args[1], (const Variant **)argptrs.ptr(), argptrs.size());
} else if (p_msg == "live_res_call") {
ERR_FAIL_COND_V(p_args.size() < 10, ERR_INVALID_DATA);
- live_editor->_res_call_func(p_args[0], p_args[1], p_args[2], p_args[3], p_args[4], p_args[5], p_args[6], p_args[7], p_args[8], p_args[9]);
+
+ LocalVector<Variant> args;
+ LocalVector<Variant *> argptrs;
+ args.resize(p_args.size() - 2);
+ argptrs.resize(args.size());
+ for (uint32_t i = 0; i < args.size(); i++) {
+ args[i] = p_args[i + 2];
+ argptrs[i] = &args[i];
+ }
+ live_editor->_res_call_func(p_args[0], p_args[1], (const Variant **)argptrs.ptr(), argptrs.size());
} else if (p_msg == "live_create_node") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
@@ -226,7 +334,7 @@ void SceneDebugger::add_to_cache(const String &p_filename, Node *p_node) {
return;
}
- if (EngineDebugger::get_script_debugger() && p_filename != String()) {
+ if (EngineDebugger::get_script_debugger() && !p_filename.is_empty()) {
debugger->live_scene_edit_cache[p_filename].insert(p_node);
}
}
@@ -237,22 +345,22 @@ void SceneDebugger::remove_from_cache(const String &p_filename, Node *p_node) {
return;
}
- Map<String, Set<Node *>> &edit_cache = debugger->live_scene_edit_cache;
- Map<String, Set<Node *>>::Element *E = edit_cache.find(p_filename);
+ HashMap<String, RBSet<Node *>> &edit_cache = debugger->live_scene_edit_cache;
+ HashMap<String, RBSet<Node *>>::Iterator E = edit_cache.find(p_filename);
if (E) {
- E->get().erase(p_node);
- if (E->get().size() == 0) {
- edit_cache.erase(E);
+ E->value.erase(p_node);
+ if (E->value.size() == 0) {
+ edit_cache.remove(E);
}
}
- Map<Node *, Map<ObjectID, Node *>> &remove_list = debugger->live_edit_remove_list;
- Map<Node *, Map<ObjectID, Node *>>::Element *F = remove_list.find(p_node);
+ HashMap<Node *, HashMap<ObjectID, Node *>> &remove_list = debugger->live_edit_remove_list;
+ HashMap<Node *, HashMap<ObjectID, Node *>>::Iterator F = remove_list.find(p_node);
if (F) {
- for (const KeyValue<ObjectID, Node *> &G : F->get()) {
+ for (const KeyValue<ObjectID, Node *> &G : F->value) {
memdelete(G.value);
}
- remove_list.erase(F);
+ remove_list.remove(F);
}
}
@@ -300,47 +408,47 @@ SceneDebuggerObject::SceneDebuggerObject(ObjectID p_id) {
}
void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInstance *p_instance) {
- typedef Map<const Script *, Set<StringName>> ScriptMemberMap;
- typedef Map<const Script *, Map<StringName, Variant>> ScriptConstantsMap;
+ typedef HashMap<const Script *, RBSet<StringName>> ScriptMemberMap;
+ typedef HashMap<const Script *, HashMap<StringName, Variant>> ScriptConstantsMap;
ScriptMemberMap members;
if (p_instance) {
- members[p_script] = Set<StringName>();
+ members[p_script] = RBSet<StringName>();
p_script->get_members(&(members[p_script]));
}
ScriptConstantsMap constants;
- constants[p_script] = Map<StringName, Variant>();
+ constants[p_script] = HashMap<StringName, Variant>();
p_script->get_constants(&(constants[p_script]));
Ref<Script> base = p_script->get_base_script();
while (base.is_valid()) {
if (p_instance) {
- members[base.ptr()] = Set<StringName>();
+ members[base.ptr()] = RBSet<StringName>();
base->get_members(&(members[base.ptr()]));
}
- constants[base.ptr()] = Map<StringName, Variant>();
+ constants[base.ptr()] = HashMap<StringName, Variant>();
base->get_constants(&(constants[base.ptr()]));
base = base->get_base_script();
}
// Members
- for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) {
- for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) {
+ for (KeyValue<const Script *, RBSet<StringName>> sm : members) {
+ for (RBSet<StringName>::Element *E = sm.value.front(); E; E = E->next()) {
Variant m;
if (p_instance->get(E->get(), m)) {
- String script_path = sm->key() == p_script ? "" : sm->key()->get_path().get_file() + "/";
+ String script_path = sm.key == p_script ? "" : sm.key->get_path().get_file() + "/";
PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get());
properties.push_back(SceneDebuggerProperty(pi, m));
}
}
}
// Constants
- for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) {
- for (const KeyValue<StringName, Variant> &E : sc->get()) {
- String script_path = sc->key() == p_script ? "" : sc->key()->get_path().get_file() + "/";
+ for (KeyValue<const Script *, HashMap<StringName, Variant>> &sc : constants) {
+ for (const KeyValue<StringName, Variant> &E : sc.value) {
+ String script_path = sc.key == p_script ? "" : sc.key->get_path().get_file() + "/";
if (E.value.get_type() == Variant::OBJECT) {
Variant id = ((Object *)E.value)->get_instance_id();
PropertyInfo pi(id.get_type(), "Constants/" + E.key, PROPERTY_HINT_OBJECT_ID, "Object");
@@ -359,7 +467,7 @@ void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) {
const PropertyInfo &pi = properties[i].first;
Variant &var = properties[i].second;
- RES res = var;
+ Ref<Resource> res = var;
Array prop;
prop.push_back(pi.name);
@@ -367,7 +475,7 @@ void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) {
PropertyHint hint = pi.hint;
String hint_string = pi.hint_string;
- if (!res.is_null()) {
+ if (!res.is_null() && !res->get_path().is_empty()) {
var = res->get_path();
} else { //only send information that can be sent..
int len = 0; //test how big is this to encode
@@ -421,7 +529,7 @@ void SceneDebuggerObject::deserialize(const Array &p_arr) {
if (pinfo.type == Variant::OBJECT) {
if (var.is_zero()) {
- var = RES();
+ var = Ref<Resource>();
} else if (var.get_type() == Variant::OBJECT) {
if (((Object *)var)->is_class("EncodedObjectAsID")) {
var = Object::cast_to<EncodedObjectAsID>(var)->get_object_id();
@@ -516,12 +624,12 @@ void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Varian
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+ for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_ancestor_of(n)) {
@@ -538,14 +646,14 @@ void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Varian
}
void LiveEditor::_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
- RES r = ResourceLoader::load(p_value);
+ Ref<Resource> r = ResourceLoader::load(p_value);
if (!r.is_valid()) {
return;
}
_node_set_func(p_id, p_prop, r);
}
-void LiveEditor::_node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
+void LiveEditor::_node_call_func(int p_id, const StringName &p_method, const Variant **p_args, int p_argcount) {
SceneTree *scene_tree = SceneTree::get_singleton();
if (!scene_tree) {
return;
@@ -560,12 +668,12 @@ void LiveEditor::_node_call_func(int p_id, const StringName &p_method, VARIANT_A
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+ for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_ancestor_of(n)) {
@@ -577,7 +685,8 @@ void LiveEditor::_node_call_func(int p_id, const StringName &p_method, VARIANT_A
}
Node *n2 = n->get_node(np);
- n2->call(p_method, VARIANT_ARG_PASS);
+ Callable::CallError ce;
+ n2->callp(p_method, p_args, p_argcount, ce);
}
}
@@ -592,7 +701,7 @@ void LiveEditor::_res_set_func(int p_id, const StringName &p_prop, const Variant
return;
}
- RES r = ResourceCache::get(resp);
+ Ref<Resource> r = ResourceCache::get(resp);
if (!r.is_valid()) {
return;
}
@@ -601,14 +710,14 @@ void LiveEditor::_res_set_func(int p_id, const StringName &p_prop, const Variant
}
void LiveEditor::_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value) {
- RES r = ResourceLoader::load(p_value);
+ Ref<Resource> r = ResourceLoader::load(p_value);
if (!r.is_valid()) {
return;
}
_res_set_func(p_id, p_prop, r);
}
-void LiveEditor::_res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
+void LiveEditor::_res_call_func(int p_id, const StringName &p_method, const Variant **p_args, int p_argcount) {
if (!live_edit_resource_cache.has(p_id)) {
return;
}
@@ -619,12 +728,13 @@ void LiveEditor::_res_call_func(int p_id, const StringName &p_method, VARIANT_AR
return;
}
- RES r = ResourceCache::get(resp);
+ Ref<Resource> r = ResourceCache::get(resp);
if (!r.is_valid()) {
return;
}
- r->call(p_method, VARIANT_ARG_PASS);
+ Callable::CallError ce;
+ r->callp(p_method, p_args, p_argcount, ce);
}
void LiveEditor::_root_func(const NodePath &p_scene_path, const String &p_scene_from) {
@@ -643,12 +753,12 @@ void LiveEditor::_create_node_func(const NodePath &p_parent, const String &p_typ
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+ for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_ancestor_of(n)) {
@@ -687,12 +797,12 @@ void LiveEditor::_instance_node_func(const NodePath &p_parent, const String &p_p
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+ for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_ancestor_of(n)) {
@@ -725,13 +835,13 @@ void LiveEditor::_remove_node_func(const NodePath &p_at) {
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F;) {
- Set<Node *>::Element *N = F->next();
+ for (RBSet<Node *>::Element *F = E->value.front(); F;) {
+ RBSet<Node *>::Element *N = F->next();
Node *n = F->get();
@@ -761,13 +871,13 @@ void LiveEditor::_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_kee
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F;) {
- Set<Node *>::Element *N = F->next();
+ for (RBSet<Node *>::Element *F = E->value.front(); F;) {
+ RBSet<Node *>::Element *N = F->next();
Node *n = F->get();
@@ -800,13 +910,13 @@ void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_a
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F;) {
- Set<Node *>::Element *N = F->next();
+ for (RBSet<Node *>::Element *F = E->value.front(); F;) {
+ RBSet<Node *>::Element *N = F->next();
Node *n = F->get();
@@ -819,23 +929,23 @@ void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_a
}
Node *n2 = n->get_node(p_at);
- Map<Node *, Map<ObjectID, Node *>>::Element *EN = live_edit_remove_list.find(n);
+ HashMap<Node *, HashMap<ObjectID, Node *>>::Iterator EN = live_edit_remove_list.find(n);
if (!EN) {
continue;
}
- Map<ObjectID, Node *>::Element *FN = EN->get().find(p_id);
+ HashMap<ObjectID, Node *>::Iterator FN = EN->value.find(p_id);
if (!FN) {
continue;
}
- n2->add_child(FN->get());
+ n2->add_child(FN->value);
- EN->get().erase(FN);
+ EN->value.remove(FN);
- if (EN->get().size() == 0) {
- live_edit_remove_list.erase(EN);
+ if (EN->value.size() == 0) {
+ live_edit_remove_list.remove(EN);
}
F = N;
@@ -853,12 +963,12 @@ void LiveEditor::_duplicate_node_func(const NodePath &p_at, const String &p_new_
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+ for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_ancestor_of(n)) {
@@ -892,12 +1002,12 @@ void LiveEditor::_reparent_node_func(const NodePath &p_at, const NodePath &p_new
base = scene_tree->root->get_node(live_edit_root);
}
- Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene);
+ HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene);
if (!E) {
return; //scene not editable
}
- for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) {
+ for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) {
Node *n = F->get();
if (base && !base->is_ancestor_of(n)) {
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
index 9d54556187..0daefa9609 100644
--- a/scene/debugger/scene_debugger.h
+++ b/scene/debugger/scene_debugger.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,17 +32,46 @@
#define SCENE_DEBUGGER_H
#include "core/object/class_db.h"
+#include "core/object/ref_counted.h"
#include "core/string/ustring.h"
#include "core/templates/pair.h"
#include "core/variant/array.h"
class Script;
+class Node;
class SceneDebugger {
public:
+ // RPC profiler
+ struct RPCNodeInfo {
+ ObjectID node;
+ String node_path;
+ int incoming_rpc = 0;
+ int outgoing_rpc = 0;
+ };
+
+ struct RPCProfilerFrame {
+ Vector<RPCNodeInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+private:
+ class RPCProfiler;
+
+ static SceneDebugger *singleton;
+
+ Ref<RPCProfiler> rpc_profiler;
+
+ SceneDebugger();
+
+public:
static void initialize();
static void deinitialize();
+ ~SceneDebugger();
+
#ifdef DEBUG_ENABLED
private:
static void _save_node(ObjectID id, const String &p_path);
@@ -103,14 +132,14 @@ public:
class LiveEditor {
private:
friend class SceneDebugger;
- Map<int, NodePath> live_edit_node_path_cache;
- Map<int, String> live_edit_resource_cache;
+ HashMap<int, NodePath> live_edit_node_path_cache;
+ HashMap<int, String> live_edit_resource_cache;
NodePath live_edit_root;
String live_edit_scene;
- Map<String, Set<Node *>> live_scene_edit_cache;
- Map<Node *, Map<ObjectID, Node *>> live_edit_remove_list;
+ HashMap<String, RBSet<Node *>> live_scene_edit_cache;
+ HashMap<Node *, HashMap<ObjectID, Node *>> live_edit_remove_list;
void _send_tree();
@@ -119,10 +148,10 @@ private:
void _node_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
void _node_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
- void _node_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
+ void _node_call_func(int p_id, const StringName &p_method, const Variant **p_args, int p_argcount);
void _res_set_func(int p_id, const StringName &p_prop, const Variant &p_value);
void _res_set_res_func(int p_id, const StringName &p_prop, const String &p_value);
- void _res_call_func(int p_id, const StringName &p_method, VARIANT_ARG_DECLARE);
+ void _res_call_func(int p_id, const StringName &p_method, const Variant **p_args, int p_argcount);
void _root_func(const NodePath &p_scene_path, const String &p_scene_from);
void _create_node_func(const NodePath &p_parent, const String &p_type, const String &p_name);
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp
index fb6fa9dec9..75f19ac452 100644
--- a/scene/gui/aspect_ratio_container.cpp
+++ b/scene/gui/aspect_ratio_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -60,16 +60,34 @@ void AspectRatioContainer::set_stretch_mode(StretchMode p_mode) {
queue_sort();
}
-void AspectRatioContainer::set_alignment_horizontal(AlignMode p_alignment_horizontal) {
+void AspectRatioContainer::set_alignment_horizontal(AlignmentMode p_alignment_horizontal) {
alignment_horizontal = p_alignment_horizontal;
queue_sort();
}
-void AspectRatioContainer::set_alignment_vertical(AlignMode p_alignment_vertical) {
+void AspectRatioContainer::set_alignment_vertical(AlignmentMode p_alignment_vertical) {
alignment_vertical = p_alignment_vertical;
queue_sort();
}
+Vector<int> AspectRatioContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> AspectRatioContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void AspectRatioContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {
@@ -107,25 +125,25 @@ void AspectRatioContainer::_notification(int p_what) {
float align_x = 0.5;
switch (alignment_horizontal) {
- case ALIGN_BEGIN: {
+ case ALIGNMENT_BEGIN: {
align_x = 0.0;
} break;
- case ALIGN_CENTER: {
+ case ALIGNMENT_CENTER: {
align_x = 0.5;
} break;
- case ALIGN_END: {
+ case ALIGNMENT_END: {
align_x = 1.0;
} break;
}
float align_y = 0.5;
switch (alignment_vertical) {
- case ALIGN_BEGIN: {
+ case ALIGNMENT_BEGIN: {
align_y = 0.0;
} break;
- case ALIGN_CENTER: {
+ case ALIGNMENT_CENTER: {
align_y = 0.5;
} break;
- case ALIGN_END: {
+ case ALIGNMENT_END: {
align_y = 1.0;
} break;
}
@@ -154,7 +172,7 @@ void AspectRatioContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_alignment_vertical", "alignment_vertical"), &AspectRatioContainer::set_alignment_vertical);
ClassDB::bind_method(D_METHOD("get_alignment_vertical"), &AspectRatioContainer::get_alignment_vertical);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ratio"), "set_ratio", "get_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ratio", PROPERTY_HINT_RANGE, "0.001,10.0,0.0001,or_greater"), "set_ratio", "get_ratio");
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Width Controls Height,Height Controls Width,Fit,Cover"), "set_stretch_mode", "get_stretch_mode");
ADD_GROUP("Alignment", "alignment_");
@@ -166,7 +184,7 @@ void AspectRatioContainer::_bind_methods() {
BIND_ENUM_CONSTANT(STRETCH_FIT);
BIND_ENUM_CONSTANT(STRETCH_COVER);
- BIND_ENUM_CONSTANT(ALIGN_BEGIN);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_END);
+ BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
+ BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
+ BIND_ENUM_CONSTANT(ALIGNMENT_END);
}
diff --git a/scene/gui/aspect_ratio_container.h b/scene/gui/aspect_ratio_container.h
index c95c6a7274..6740e2f329 100644
--- a/scene/gui/aspect_ratio_container.h
+++ b/scene/gui/aspect_ratio_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -48,17 +48,17 @@ public:
STRETCH_FIT,
STRETCH_COVER,
};
- enum AlignMode {
- ALIGN_BEGIN,
- ALIGN_CENTER,
- ALIGN_END,
+ enum AlignmentMode {
+ ALIGNMENT_BEGIN,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_END,
};
private:
float ratio = 1.0;
StretchMode stretch_mode = STRETCH_FIT;
- AlignMode alignment_horizontal = ALIGN_CENTER;
- AlignMode alignment_vertical = ALIGN_CENTER;
+ AlignmentMode alignment_horizontal = ALIGNMENT_CENTER;
+ AlignmentMode alignment_vertical = ALIGNMENT_CENTER;
public:
void set_ratio(float p_ratio);
@@ -67,14 +67,17 @@ public:
void set_stretch_mode(StretchMode p_mode);
StretchMode get_stretch_mode() const { return stretch_mode; }
- void set_alignment_horizontal(AlignMode p_alignment_horizontal);
- AlignMode get_alignment_horizontal() const { return alignment_horizontal; }
+ void set_alignment_horizontal(AlignmentMode p_alignment_horizontal);
+ AlignmentMode get_alignment_horizontal() const { return alignment_horizontal; }
- void set_alignment_vertical(AlignMode p_alignment_vertical);
- AlignMode get_alignment_vertical() const { return alignment_vertical; }
+ void set_alignment_vertical(AlignmentMode p_alignment_vertical);
+ AlignmentMode get_alignment_vertical() const { return alignment_vertical; }
+
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
};
VARIANT_ENUM_CAST(AspectRatioContainer::StretchMode);
-VARIANT_ENUM_CAST(AspectRatioContainer::AlignMode);
+VARIANT_ENUM_CAST(AspectRatioContainer::AlignmentMode);
#endif // ASPECT_RATIO_CONTAINER_H
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 9f712ed478..595f0cbea7 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,7 +43,7 @@ void BaseButton::_unpress_group() {
status.pressed = true;
}
- for (Set<BaseButton *>::Element *E = button_group->buttons.front(); E; E = E->next()) {
+ for (RBSet<BaseButton *>::Element *E = button_group->buttons.front(); E; E = E->next()) {
if (E->get() == this) {
continue;
}
@@ -81,42 +81,50 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) {
}
void BaseButton::_notification(int p_what) {
- if (p_what == NOTIFICATION_MOUSE_ENTER) {
- status.hovering = true;
- update();
- }
+ switch (p_what) {
+ case NOTIFICATION_MOUSE_ENTER: {
+ status.hovering = true;
+ update();
+ } break;
- if (p_what == NOTIFICATION_MOUSE_EXIT) {
- status.hovering = false;
- update();
- }
- if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) {
- if (status.press_attempt) {
- status.press_attempt = false;
+ case NOTIFICATION_MOUSE_EXIT: {
+ status.hovering = false;
update();
- }
- }
+ } break;
- if (p_what == NOTIFICATION_FOCUS_ENTER) {
- update();
- }
+ case NOTIFICATION_DRAG_BEGIN:
+ case NOTIFICATION_SCROLL_BEGIN: {
+ if (status.press_attempt) {
+ status.press_attempt = false;
+ update();
+ }
+ } break;
- if (p_what == NOTIFICATION_FOCUS_EXIT) {
- if (status.press_attempt) {
- status.press_attempt = false;
+ case NOTIFICATION_FOCUS_ENTER: {
update();
- } else if (status.hovering) {
- update();
- }
- }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE || (p_what == NOTIFICATION_VISIBILITY_CHANGED && !is_visible_in_tree())) {
- if (!toggle_mode) {
- status.pressed = false;
- }
- status.hovering = false;
- status.press_attempt = false;
- status.pressing_inside = false;
+ case NOTIFICATION_FOCUS_EXIT: {
+ if (status.press_attempt) {
+ status.press_attempt = false;
+ update();
+ } else if (status.hovering) {
+ update();
+ }
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED:
+ case NOTIFICATION_EXIT_TREE: {
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED && is_visible_in_tree()) {
+ break;
+ }
+ if (!toggle_mode) {
+ status.pressed = false;
+ }
+ status.hovering = false;
+ status.press_attempt = false;
+ status.pressing_inside = false;
+ } break;
}
}
@@ -331,17 +339,17 @@ bool BaseButton::is_keep_pressed_outside() const {
void BaseButton::set_shortcut(const Ref<Shortcut> &p_shortcut) {
shortcut = p_shortcut;
- set_process_unhandled_key_input(shortcut.is_valid());
+ set_process_shortcut_input(shortcut.is_valid());
}
Ref<Shortcut> BaseButton::get_shortcut() const {
return shortcut;
}
-void BaseButton::unhandled_key_input(const Ref<InputEvent> &p_event) {
+void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (!_is_focus_owner_in_shorcut_context()) {
+ if (!_is_focus_owner_in_shortcut_context()) {
return;
}
@@ -355,8 +363,8 @@ String BaseButton::get_tooltip(const Point2 &p_pos) const {
String tooltip = Control::get_tooltip(p_pos);
if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->has_valid_event()) {
String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")";
- if (tooltip != String() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
- text += "\n" + tooltip;
+ if (!tooltip.is_empty() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
+ text += "\n" + atr(tooltip);
}
tooltip = text;
}
@@ -382,8 +390,11 @@ Ref<ButtonGroup> BaseButton::get_button_group() const {
}
void BaseButton::set_shortcut_context(Node *p_node) {
- ERR_FAIL_NULL_MSG(p_node, "Shortcut context node can't be null.");
- shortcut_context = p_node->get_instance_id();
+ if (p_node != nullptr) {
+ shortcut_context = p_node->get_instance_id();
+ } else {
+ shortcut_context = ObjectID();
+ }
}
Node *BaseButton::get_shortcut_context() const {
@@ -393,14 +404,14 @@ Node *BaseButton::get_shortcut_context() const {
return ctx_node;
}
-bool BaseButton::_is_focus_owner_in_shorcut_context() const {
+bool BaseButton::_is_focus_owner_in_shortcut_context() const {
if (shortcut_context == ObjectID()) {
// No context, therefore global - always "in" context.
return true;
}
Node *ctx_node = get_shortcut_context();
- Control *vp_focus = get_focus_owner();
+ Control *vp_focus = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr;
// If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it.
return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus));
@@ -441,10 +452,11 @@ void BaseButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("button_up"));
ADD_SIGNAL(MethodInfo("button_down"));
ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed")));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "button_pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside");
@@ -473,14 +485,14 @@ BaseButton::~BaseButton() {
}
void ButtonGroup::get_buttons(List<BaseButton *> *r_buttons) {
- for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
+ for (RBSet<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
r_buttons->push_back(E->get());
}
}
Array ButtonGroup::_get_buttons() {
Array btns;
- for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
+ for (RBSet<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
btns.push_back(E->get());
}
@@ -488,7 +500,7 @@ Array ButtonGroup::_get_buttons() {
}
BaseButton *ButtonGroup::get_pressed_button() {
- for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
+ for (RBSet<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
if (E->get()->is_pressed()) {
return E->get();
}
@@ -500,7 +512,8 @@ BaseButton *ButtonGroup::get_pressed_button() {
void ButtonGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button);
ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons);
- ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button")));
+
+ ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button", PROPERTY_HINT_RESOURCE_TYPE, "BaseButton")));
}
ButtonGroup::ButtonGroup() {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 3ea59c3ff9..0b70d285ee 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,7 @@
#ifndef BASE_BUTTON_H
#define BASE_BUTTON_H
+#include "core/input/shortcut.h"
#include "scene/gui/control.h"
class ButtonGroup;
@@ -76,10 +77,10 @@ protected:
virtual void toggled(bool p_pressed);
static void _bind_methods();
virtual void gui_input(const Ref<InputEvent> &p_event) override;
- virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
+ virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
void _notification(int p_what);
- bool _is_focus_owner_in_shorcut_context() const;
+ bool _is_focus_owner_in_shortcut_context() const;
GDVIRTUAL0(_pressed)
GDVIRTUAL1(_toggled, bool)
@@ -142,7 +143,7 @@ VARIANT_ENUM_CAST(BaseButton::ActionMode)
class ButtonGroup : public Resource {
GDCLASS(ButtonGroup, Resource);
friend class BaseButton;
- Set<BaseButton *> buttons;
+ RBSet<BaseButton *> buttons;
protected:
static void _bind_methods();
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index cb9f13e970..df695feba8 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "box_container.h"
+
#include "label.h"
#include "margin_container.h"
@@ -51,7 +52,7 @@ void BoxContainer::_resort() {
int stretch_min = 0;
int stretch_avail = 0;
float stretch_ratio_total = 0.0;
- Map<Control *, _MinSizeCache> min_size_cache;
+ HashMap<Control *, _MinSizeCache> min_size_cache;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
@@ -154,29 +155,29 @@ void BoxContainer::_resort() {
int ofs = 0;
if (!has_stretched) {
if (!vertical) {
- switch (align) {
- case ALIGN_BEGIN:
+ switch (alignment) {
+ case ALIGNMENT_BEGIN:
if (rtl) {
ofs = stretch_diff;
}
break;
- case ALIGN_CENTER:
+ case ALIGNMENT_CENTER:
ofs = stretch_diff / 2;
break;
- case ALIGN_END:
+ case ALIGNMENT_END:
if (!rtl) {
ofs = stretch_diff;
}
break;
}
} else {
- switch (align) {
- case ALIGN_BEGIN:
+ switch (alignment) {
+ case ALIGNMENT_BEGIN:
break;
- case ALIGN_CENTER:
+ case ALIGNMENT_CENTER:
ofs = stretch_diff / 2;
break;
- case ALIGN_END:
+ case ALIGNMENT_END:
ofs = stretch_diff;
break;
}
@@ -294,9 +295,11 @@ void BoxContainer::_notification(int p_what) {
case NOTIFICATION_SORT_CHILDREN: {
_resort();
} break;
+
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
queue_sort();
@@ -304,13 +307,13 @@ void BoxContainer::_notification(int p_what) {
}
}
-void BoxContainer::set_alignment(AlignMode p_align) {
- align = p_align;
+void BoxContainer::set_alignment(AlignmentMode p_alignment) {
+ alignment = p_alignment;
_resort();
}
-BoxContainer::AlignMode BoxContainer::get_alignment() const {
- return align;
+BoxContainer::AlignmentMode BoxContainer::get_alignment() const {
+ return alignment;
}
Control *BoxContainer::add_spacer(bool p_begin) {
@@ -331,6 +334,30 @@ Control *BoxContainer::add_spacer(bool p_begin) {
return c;
}
+Vector<int> BoxContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (!vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> BoxContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
BoxContainer::BoxContainer(bool p_vertical) {
vertical = p_vertical;
}
@@ -340,9 +367,9 @@ void BoxContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_alignment"), &BoxContainer::get_alignment);
ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &BoxContainer::set_alignment);
- BIND_ENUM_CONSTANT(ALIGN_BEGIN);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_END);
+ BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
+ BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
+ BIND_ENUM_CONSTANT(ALIGNMENT_END);
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment");
}
@@ -351,11 +378,11 @@ MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control
Label *l = memnew(Label);
l->set_theme_type_variation("HeaderSmall");
l->set_text(p_label);
- add_child(l, false, INTERNAL_MODE_FRONT);
+ add_child(l);
MarginContainer *mc = memnew(MarginContainer);
mc->add_theme_constant_override("margin_left", 0);
mc->add_child(p_control, true);
- add_child(mc, false, INTERNAL_MODE_FRONT);
+ add_child(mc);
if (p_expand) {
mc->set_v_size_flags(SIZE_EXPAND_FILL);
}
diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h
index 23feea565c..3043c3ea45 100644
--- a/scene/gui/box_container.h
+++ b/scene/gui/box_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,15 +37,15 @@ class BoxContainer : public Container {
GDCLASS(BoxContainer, Container);
public:
- enum AlignMode {
- ALIGN_BEGIN,
- ALIGN_CENTER,
- ALIGN_END
+ enum AlignmentMode {
+ ALIGNMENT_BEGIN,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_END
};
private:
bool vertical = false;
- AlignMode align = ALIGN_BEGIN;
+ AlignmentMode alignment = ALIGNMENT_BEGIN;
void _resort();
@@ -57,11 +57,14 @@ protected:
public:
Control *add_spacer(bool p_begin = false);
- void set_alignment(AlignMode p_align);
- AlignMode get_alignment() const;
+ void set_alignment(AlignmentMode p_alignment);
+ AlignmentMode get_alignment() const;
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
BoxContainer(bool p_vertical = false);
};
@@ -84,6 +87,6 @@ public:
BoxContainer(true) {}
};
-VARIANT_ENUM_CAST(BoxContainer::AlignMode);
+VARIANT_ENUM_CAST(BoxContainer::AlignmentMode);
#endif // BOX_CONTAINER_H
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 9818c8f0cc..ff194f979d 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -50,10 +50,10 @@ Size2 Button::get_minimum_size() const {
if (!_icon.is_null()) {
minsize.height = MAX(minsize.height, _icon->get_height());
- if (icon_align != ALIGN_CENTER) {
+ if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) {
minsize.width += _icon->get_width();
- if (xl_text != "") {
- minsize.width += get_theme_constant(SNAME("hseparation"));
+ if (!xl_text.is_empty()) {
+ minsize.width += get_theme_constant(SNAME("h_separation"));
}
} else {
minsize.width = MAX(minsize.width, _icon->get_width());
@@ -78,19 +78,22 @@ void Button::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
update();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED: {
xl_text = atr(text);
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
+
case NOTIFICATION_THEME_CHANGED: {
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
+
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
Size2 size = get_size();
@@ -126,7 +129,8 @@ void Button::_notification(int p_what) {
}
} break;
case DRAW_HOVER_PRESSED: {
- if (has_theme_stylebox(SNAME("hover_pressed")) && has_theme_stylebox_override("hover_pressed")) {
+ // Edge case for CheckButton and CheckBox.
+ if (has_theme_stylebox("hover_pressed")) {
if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) {
style = get_theme_stylebox(SNAME("hover_pressed_mirrored"));
} else {
@@ -138,8 +142,6 @@ void Button::_notification(int p_what) {
}
if (has_theme_color(SNAME("font_hover_pressed_color"))) {
color = get_theme_color(SNAME("font_hover_pressed_color"));
- } else {
- color = get_theme_color(SNAME("font_color"));
}
if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
color_icon = get_theme_color(SNAME("icon_hover_pressed_color"));
@@ -198,6 +200,8 @@ void Button::_notification(int p_what) {
color = get_theme_color(SNAME("font_disabled_color"));
if (has_theme_color(SNAME("icon_disabled_color"))) {
color_icon = get_theme_color(SNAME("icon_disabled_color"));
+ } else {
+ color_icon.a = 0.4;
}
} break;
@@ -216,49 +220,47 @@ void Button::_notification(int p_what) {
}
Rect2 icon_region = Rect2();
- TextAlign icon_align_rtl_checked = icon_align;
- TextAlign align_rtl_checked = align;
+ HorizontalAlignment icon_align_rtl_checked = icon_alignment;
+ HorizontalAlignment align_rtl_checked = alignment;
// Swap icon and text alignment sides if right-to-left layout is set.
if (rtl) {
- if (icon_align == ALIGN_RIGHT) {
- icon_align_rtl_checked = ALIGN_LEFT;
- } else if (icon_align == ALIGN_LEFT) {
- icon_align_rtl_checked = ALIGN_RIGHT;
+ if (icon_alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
+ icon_align_rtl_checked = HORIZONTAL_ALIGNMENT_LEFT;
+ } else if (icon_alignment == HORIZONTAL_ALIGNMENT_LEFT) {
+ icon_align_rtl_checked = HORIZONTAL_ALIGNMENT_RIGHT;
}
- if (align == ALIGN_RIGHT) {
- align_rtl_checked = ALIGN_LEFT;
- } else if (align == ALIGN_LEFT) {
- align_rtl_checked = ALIGN_RIGHT;
+ if (alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
+ align_rtl_checked = HORIZONTAL_ALIGNMENT_LEFT;
+ } else if (alignment == HORIZONTAL_ALIGNMENT_LEFT) {
+ align_rtl_checked = HORIZONTAL_ALIGNMENT_RIGHT;
}
}
if (!_icon.is_null()) {
int valign = size.height - style->get_minimum_size().y;
- if (is_disabled()) {
- color_icon.a = 0.4;
- }
float icon_ofs_region = 0.0;
Point2 style_offset;
Size2 icon_size = _icon->get_size();
- if (icon_align_rtl_checked == ALIGN_LEFT) {
+ if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
style_offset.x = style->get_margin(SIDE_LEFT);
if (_internal_margin[SIDE_LEFT] > 0) {
- icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("hseparation"));
+ icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation"));
}
- } else if (icon_align_rtl_checked == ALIGN_CENTER) {
+ } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
style_offset.x = 0.0;
- } else if (icon_align_rtl_checked == ALIGN_RIGHT) {
+ } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) {
style_offset.x = -style->get_margin(SIDE_RIGHT);
if (_internal_margin[SIDE_RIGHT] > 0) {
- icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("hseparation"));
+ icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation"));
}
}
style_offset.y = style->get_margin(SIDE_TOP);
if (expand_icon) {
Size2 _size = get_size() - style->get_offset() * 2;
- _size.width -= get_theme_constant(SNAME("hseparation")) + icon_ofs_region;
- if (!clip_text && icon_align_rtl_checked != ALIGN_CENTER) {
+ int icon_text_separation = text.is_empty() ? 0 : get_theme_constant(SNAME("h_separation"));
+ _size.width -= icon_text_separation + icon_ofs_region;
+ if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
_size.width -= text_buf->get_size().width;
}
float icon_width = _icon->get_width() * _size.height / _icon->get_height();
@@ -272,9 +274,9 @@ void Button::_notification(int p_what) {
icon_size = Size2(icon_width, icon_height);
}
- if (icon_align_rtl_checked == ALIGN_LEFT) {
+ if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
icon_region = Rect2(style_offset + Point2(icon_ofs_region, Math::floor((valign - icon_size.y) * 0.5)), icon_size);
- } else if (icon_align_rtl_checked == ALIGN_CENTER) {
+ } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
icon_region = Rect2(style_offset + Point2(icon_ofs_region + Math::floor((size.x - icon_size.x) * 0.5), Math::floor((valign - icon_size.y) * 0.5)), icon_size);
} else {
icon_region = Rect2(style_offset + Point2(icon_ofs_region + size.x - icon_size.x, Math::floor((valign - icon_size.y) * 0.5)), icon_size);
@@ -285,53 +287,56 @@ void Button::_notification(int p_what) {
}
}
- Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant(SNAME("hseparation")), 0) : Point2();
- if (align_rtl_checked == ALIGN_CENTER && icon_align_rtl_checked == ALIGN_CENTER) {
+ Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant(SNAME("h_separation")), 0) : Point2();
+ if (align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER && icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
icon_ofs.x = 0.0;
}
int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
text_buf->set_width(clip_text ? text_clip : -1);
- int text_width = clip_text ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x;
+ int text_width = MAX(1, clip_text ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
if (_internal_margin[SIDE_LEFT] > 0) {
- text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("hseparation"));
+ text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation"));
}
if (_internal_margin[SIDE_RIGHT] > 0) {
- text_clip -= _internal_margin[SIDE_RIGHT] + get_theme_constant(SNAME("hseparation"));
+ text_clip -= _internal_margin[SIDE_RIGHT] + get_theme_constant(SNAME("h_separation"));
}
Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0;
+ text_buf->set_alignment(align_rtl_checked);
+ text_buf->set_width(text_width);
switch (align_rtl_checked) {
- case ALIGN_LEFT: {
- if (icon_align_rtl_checked != ALIGN_LEFT) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
+ if (icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_LEFT) {
icon_ofs.x = 0.0;
}
if (_internal_margin[SIDE_LEFT] > 0) {
- text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("hseparation"));
+ text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation"));
} else {
text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x;
}
text_ofs.y += style->get_offset().y;
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (text_ofs.x < 0) {
text_ofs.x = 0;
}
- if (icon_align_rtl_checked == ALIGN_LEFT) {
+ if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
text_ofs += icon_ofs;
}
text_ofs += style->get_offset();
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (_internal_margin[SIDE_RIGHT] > 0) {
- text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("hseparation"));
+ text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation"));
} else {
text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width;
}
text_ofs.y += style->get_offset().y;
- if (icon_align_rtl_checked == ALIGN_RIGHT) {
+ if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) {
text_ofs.x -= icon_ofs.x;
}
} break;
@@ -358,7 +363,7 @@ void Button::_shape() {
} else {
text_buf->set_direction((TextServer::Direction)text_direction);
}
- text_buf->add_string(xl_text, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text_buf->add_string(xl_text, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
}
void Button::set_text(const String &p_text) {
@@ -368,7 +373,7 @@ void Button::set_text(const String &p_text) {
_shape();
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -428,7 +433,7 @@ void Button::set_icon(const Ref<Texture2D> &p_icon) {
if (icon != p_icon) {
icon = p_icon;
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -440,7 +445,7 @@ void Button::set_expand_icon(bool p_enabled) {
if (expand_icon != p_enabled) {
expand_icon = p_enabled;
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -463,7 +468,7 @@ void Button::set_clip_text(bool p_enabled) {
if (clip_text != p_enabled) {
clip_text = p_enabled;
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -471,25 +476,25 @@ bool Button::get_clip_text() const {
return clip_text;
}
-void Button::set_text_align(TextAlign p_align) {
- if (align != p_align) {
- align = p_align;
+void Button::set_text_alignment(HorizontalAlignment p_alignment) {
+ if (alignment != p_alignment) {
+ alignment = p_alignment;
update();
}
}
-Button::TextAlign Button::get_text_align() const {
- return align;
+HorizontalAlignment Button::get_text_alignment() const {
+ return alignment;
}
-void Button::set_icon_align(TextAlign p_align) {
- icon_align = p_align;
- minimum_size_changed();
+void Button::set_icon_alignment(HorizontalAlignment p_alignment) {
+ icon_alignment = p_alignment;
+ update_minimum_size();
update();
}
-Button::TextAlign Button::get_icon_align() const {
- return icon_align;
+HorizontalAlignment Button::get_icon_alignment() const {
+ return icon_alignment;
}
bool Button::_set(const StringName &p_name, const Variant &p_value) {
@@ -497,7 +502,7 @@ bool Button::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -505,7 +510,7 @@ bool Button::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@@ -537,7 +542,7 @@ bool Button::_get(const StringName &p_name, Variant &r_ret) const {
void Button::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
@@ -558,33 +563,29 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat);
ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text);
ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text);
- ClassDB::bind_method(D_METHOD("set_text_align", "align"), &Button::set_text_align);
- ClassDB::bind_method(D_METHOD("get_text_align"), &Button::get_text_align);
- ClassDB::bind_method(D_METHOD("set_icon_align", "icon_align"), &Button::set_icon_align);
- ClassDB::bind_method(D_METHOD("get_icon_align"), &Button::get_icon_align);
+ ClassDB::bind_method(D_METHOD("set_text_alignment", "alignment"), &Button::set_text_alignment);
+ ClassDB::bind_method(D_METHOD("get_text_alignment"), &Button::get_text_alignment);
+ ClassDB::bind_method(D_METHOD("set_icon_alignment", "icon_alignment"), &Button::set_icon_alignment);
+ ClassDB::bind_method(D_METHOD("get_icon_alignment"), &Button::get_icon_alignment);
ClassDB::bind_method(D_METHOD("set_expand_icon", "enabled"), &Button::set_expand_icon);
ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon);
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
-
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_align", "get_icon_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon");
}
Button::Button(const String &p_text) {
text_buf.instantiate();
text_buf->set_flags(TextServer::BREAK_MANDATORY);
-
set_mouse_filter(MOUSE_FILTER_STOP);
+
set_text(p_text);
}
diff --git a/scene/gui/button.h b/scene/gui/button.h
index fd36cb77af..1abf86c986 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,13 +37,6 @@
class Button : public BaseButton {
GDCLASS(Button, BaseButton);
-public:
- enum TextAlign {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT
- };
-
private:
bool flat = false;
String text;
@@ -57,8 +50,8 @@ private:
Ref<Texture2D> icon;
bool expand_icon = false;
bool clip_text = false;
- TextAlign align = ALIGN_CENTER;
- TextAlign icon_align = ALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_CENTER;
+ HorizontalAlignment icon_alignment = HORIZONTAL_ALIGNMENT_LEFT;
float _internal_margin[4] = {};
void _shape();
@@ -100,16 +93,14 @@ public:
void set_clip_text(bool p_enabled);
bool get_clip_text() const;
- void set_text_align(TextAlign p_align);
- TextAlign get_text_align() const;
+ void set_text_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_text_alignment() const;
- void set_icon_align(TextAlign p_align);
- TextAlign get_icon_align() const;
+ void set_icon_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_icon_alignment() const;
Button(const String &p_text = String());
~Button();
};
-VARIANT_ENUM_CAST(Button::TextAlign);
-
#endif
diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp
index 909516e7ef..33ec4006ae 100644
--- a/scene/gui/center_container.cpp
+++ b/scene/gui/center_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -61,7 +61,7 @@ void CenterContainer::set_use_top_left(bool p_enable) {
use_top_left = p_enable;
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
@@ -69,22 +69,32 @@ bool CenterContainer::is_using_top_left() const {
return use_top_left;
}
+Vector<int> CenterContainer::get_allowed_size_flags_horizontal() const {
+ return Vector<int>();
+}
+
+Vector<int> CenterContainer::get_allowed_size_flags_vertical() const {
+ return Vector<int>();
+}
+
void CenterContainer::_notification(int p_what) {
- if (p_what == NOTIFICATION_SORT_CHILDREN) {
- Size2 size = get_size();
- for (int i = 0; i < get_child_count(); i++) {
- Control *c = Object::cast_to<Control>(get_child(i));
- if (!c) {
- continue;
- }
- if (c->is_set_as_top_level()) {
- continue;
- }
+ switch (p_what) {
+ case NOTIFICATION_SORT_CHILDREN: {
+ Size2 size = get_size();
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
- Size2 minsize = c->get_combined_minimum_size();
- Point2 ofs = use_top_left ? (-minsize * 0.5).floor() : ((size - minsize) / 2.0).floor();
- fit_child_in_rect(c, Rect2(ofs, minsize));
- }
+ Size2 minsize = c->get_combined_minimum_size();
+ Point2 ofs = use_top_left ? (-minsize * 0.5).floor() : ((size - minsize) / 2.0).floor();
+ fit_child_in_rect(c, Rect2(ofs, minsize));
+ }
+ } break;
}
}
diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h
index 0944f200fc..c35e0c4e29 100644
--- a/scene/gui/center_container.h
+++ b/scene/gui/center_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -48,6 +48,9 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
CenterContainer();
};
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index 411fb2e1f0..cb80f5b5ef 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -75,7 +75,7 @@ Size2 CheckBox::get_minimum_size() const {
Size2 tex_size = get_icon_size();
minsize.width += tex_size.width;
if (get_text().length() > 0) {
- minsize.width += get_theme_constant(SNAME("hseparation"));
+ minsize.width += get_theme_constant(SNAME("h_separation"));
}
Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM));
@@ -84,34 +84,40 @@ Size2 CheckBox::get_minimum_size() const {
}
void CheckBox::_notification(int p_what) {
- if ((p_what == NOTIFICATION_THEME_CHANGED) || (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || (p_what == NOTIFICATION_TRANSLATION_CHANGED))) {
- if (is_layout_rtl()) {
- _set_internal_margin(SIDE_LEFT, 0.f);
- _set_internal_margin(SIDE_RIGHT, get_icon_size().width);
- } else {
- _set_internal_margin(SIDE_LEFT, get_icon_size().width);
- _set_internal_margin(SIDE_RIGHT, 0.f);
- }
- } else if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
-
- Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : ""));
- Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : ""));
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
-
- Vector2 ofs;
- if (is_layout_rtl()) {
- ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width;
- } else {
- ofs.x = sb->get_margin(SIDE_LEFT);
- }
- ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_vadjust"));
-
- if (is_pressed()) {
- on->draw(ci, ofs);
- } else {
- off->draw(ci, ofs);
- }
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ if (is_layout_rtl()) {
+ _set_internal_margin(SIDE_LEFT, 0.f);
+ _set_internal_margin(SIDE_RIGHT, get_icon_size().width);
+ } else {
+ _set_internal_margin(SIDE_LEFT, get_icon_size().width);
+ _set_internal_margin(SIDE_RIGHT, 0.f);
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
+
+ Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : ""));
+ Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : ""));
+ Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
+
+ Vector2 ofs;
+ if (is_layout_rtl()) {
+ ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width;
+ } else {
+ ofs.x = sb->get_margin(SIDE_LEFT);
+ }
+ ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_v_adjust"));
+
+ if (is_pressed()) {
+ on->draw(ci, ofs);
+ } else {
+ off->draw(ci, ofs);
+ }
+ } break;
}
}
@@ -123,7 +129,7 @@ CheckBox::CheckBox(const String &p_text) :
Button(p_text) {
set_toggle_mode(true);
- set_text_align(ALIGN_LEFT);
+ set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
if (is_layout_rtl()) {
_set_internal_margin(SIDE_RIGHT, get_icon_size().width);
diff --git a/scene/gui/check_box.h b/scene/gui/check_box.h
index 9fb0aea218..fcdb2ce08c 100644
--- a/scene/gui/check_box.h
+++ b/scene/gui/check_box.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,9 +32,7 @@
#define CHECK_BOX_H
#include "scene/gui/button.h"
-/**
-@author Mariano Suligoy <marianognu.esyrpg@gmail.com>
-*/
+
class CheckBox : public Button {
GDCLASS(CheckBox, Button);
@@ -50,4 +48,4 @@ public:
~CheckBox();
};
-#endif
+#endif // CHECK_BOX_H
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index 162a256d23..a09873ea4f 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -52,7 +52,7 @@ Size2 CheckButton::get_minimum_size() const {
Size2 tex_size = get_icon_size();
minsize.width += tex_size.width;
if (get_text().length() > 0) {
- minsize.width += get_theme_constant(SNAME("hseparation"));
+ minsize.width += get_theme_constant(SNAME("h_separation"));
}
Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM));
@@ -61,53 +61,62 @@ Size2 CheckButton::get_minimum_size() const {
}
void CheckButton::_notification(int p_what) {
- if ((p_what == NOTIFICATION_THEME_CHANGED) || (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) || (p_what == NOTIFICATION_TRANSLATION_CHANGED)) {
- if (is_layout_rtl()) {
- _set_internal_margin(SIDE_LEFT, get_icon_size().width);
- _set_internal_margin(SIDE_RIGHT, 0.f);
- } else {
- _set_internal_margin(SIDE_LEFT, 0.f);
- _set_internal_margin(SIDE_RIGHT, get_icon_size().width);
- }
- } else if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
- bool rtl = is_layout_rtl();
-
- Ref<Texture2D> on;
- if (rtl) {
- on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored");
- } else {
- on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on");
- }
- Ref<Texture2D> off;
- if (rtl) {
- off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored");
- } else {
- off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off");
- }
-
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
- Vector2 ofs;
- Size2 tex_size = get_icon_size();
-
- if (rtl) {
- ofs.x = sb->get_margin(SIDE_LEFT);
- } else {
- ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT));
- }
- ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_vadjust"));
-
- if (is_pressed()) {
- on->draw(ci, ofs);
- } else {
- off->draw(ci, ofs);
- }
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ if (is_layout_rtl()) {
+ _set_internal_margin(SIDE_LEFT, get_icon_size().width);
+ _set_internal_margin(SIDE_RIGHT, 0.f);
+ } else {
+ _set_internal_margin(SIDE_LEFT, 0.f);
+ _set_internal_margin(SIDE_RIGHT, get_icon_size().width);
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
+ bool rtl = is_layout_rtl();
+
+ Ref<Texture2D> on;
+ if (rtl) {
+ on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored");
+ } else {
+ on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on");
+ }
+ Ref<Texture2D> off;
+ if (rtl) {
+ off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored");
+ } else {
+ off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off");
+ }
+
+ Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
+ Vector2 ofs;
+ Size2 tex_size = get_icon_size();
+
+ if (rtl) {
+ ofs.x = sb->get_margin(SIDE_LEFT);
+ } else {
+ ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT));
+ }
+ ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_v_adjust"));
+
+ if (is_pressed()) {
+ on->draw(ci, ofs);
+ } else {
+ off->draw(ci, ofs);
+ }
+ } break;
}
}
-CheckButton::CheckButton() {
+CheckButton::CheckButton(const String &p_text) :
+ Button(p_text) {
set_toggle_mode(true);
- set_text_align(ALIGN_LEFT);
+
+ set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
+
if (is_layout_rtl()) {
_set_internal_margin(SIDE_LEFT, get_icon_size().width);
} else {
diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h
index 29c557ce89..7d4bb8bdfc 100644
--- a/scene/gui/check_button.h
+++ b/scene/gui/check_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,9 +32,7 @@
#define CHECK_BUTTON_H
#include "scene/gui/button.h"
-/**
-@author Juan Linietsky <reduzio@gmail.com>
-*/
+
class CheckButton : public Button {
GDCLASS(CheckButton, Button);
@@ -44,8 +42,8 @@ protected:
void _notification(int p_what);
public:
- CheckButton();
+ CheckButton(const String &p_text = String());
~CheckButton();
};
-#endif
+#endif // CHECK_BUTTON_H
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 7fba94da4c..197c9005c3 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,14 +34,6 @@
#include "core/string/string_builder.h"
#include "core/string/ustring.h"
-static bool _is_whitespace(char32_t c) {
- return c == '\t' || c == ' ';
-}
-
-static bool _is_char(char32_t c) {
- return !is_symbol(c);
-}
-
void CodeEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
@@ -82,6 +74,7 @@ void CodeEdit::_notification(int p_what) {
line_length_guideline_color = get_theme_color(SNAME("line_length_guideline_color"));
} break;
+
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
const Size2 size = get_size();
@@ -92,7 +85,7 @@ void CodeEdit::_notification(int p_what) {
if (line_length_guideline_columns.size() > 0) {
const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width();
const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0);
- const int char_size = (int)font->get_char_size('0', 0, font_size).width;
+ const float char_size = font->get_char_size('0', 0, font_size).width;
for (int i = 0; i < line_length_guideline_columns.size(); i++) {
const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll();
@@ -113,7 +106,7 @@ void CodeEdit::_notification(int p_what) {
const int code_completion_options_count = code_completion_options.size();
const int lines = MIN(code_completion_options_count, code_completion_max_lines);
- const int icon_hsep = get_theme_constant(SNAME("hseparation"), SNAME("ItemList"));
+ const int icon_hsep = get_theme_constant(SNAME("h_separation"), SNAME("ItemList"));
const Size2 icon_area_size(row_height, row_height);
code_completion_rect.size.width = code_completion_longest_line + icon_hsep + icon_area_size.width + 2;
@@ -143,7 +136,6 @@ void CodeEdit::_notification(int p_what) {
code_completion_line_ofs = CLAMP(code_completion_current_selected - lines / 2, 0, code_completion_options_count - lines);
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), code_completion_selected_color);
- draw_rect(Rect2(code_completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(MIN(code_completion_base_width, code_completion_rect.size.width - (icon_area_size.x + icon_hsep)), code_completion_rect.size.height)), code_completion_existing_color);
for (int i = 0; i < lines; i++) {
int l = code_completion_line_ofs + i;
@@ -170,13 +162,24 @@ void CodeEdit::_notification(int p_what) {
if (code_completion_options[l].default_value.get_type() == Variant::COLOR) {
draw_rect(Rect2(Point2(code_completion_rect.position.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value);
}
- tl->set_align(HALIGN_RIGHT);
+ tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
} else {
if (code_completion_options[l].default_value.get_type() == Variant::COLOR) {
draw_rect(Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value);
}
- tl->set_align(HALIGN_LEFT);
+ tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
}
+
+ Point2 match_pos = Point2(code_completion_rect.position.x + icon_area_size.x + icon_hsep, code_completion_rect.position.y + i * row_height);
+
+ for (int j = 0; j < code_completion_options[l].matches.size(); j++) {
+ Pair<int, int> match = code_completion_options[l].matches[j];
+ int match_offset = font->get_string_size(code_completion_options[l].display.substr(0, match.first), font_size).width;
+ int match_len = font->get_string_size(code_completion_options[l].display.substr(match.first, match.second), font_size).width;
+
+ draw_rect(Rect2(match_pos + Point2(match_offset, 0), Size2(match_len, row_height)), code_completion_existing_color);
+ }
+
tl->draw(ci, title_pos, code_completion_options[l].font_color);
}
@@ -189,7 +192,7 @@ void CodeEdit::_notification(int p_what) {
}
/* Code hint */
- if (caret_visible && code_hint != "" && (!code_completion_active || (code_completion_below != code_hint_draw_below))) {
+ if (caret_visible && !code_hint.is_empty() && (!code_completion_active || (code_completion_below != code_hint_draw_below))) {
const int font_height = font->get_height(font_size);
Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel"));
Color font_color = get_theme_color(SNAME("font_color"), SNAME("TooltipLabel"));
@@ -222,14 +225,14 @@ void CodeEdit::_notification(int p_what) {
int begin = 0;
int end = 0;
- if (line.find(String::chr(0xFFFF)) != -1) {
+ if (line.contains(String::chr(0xFFFF))) {
begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), font_size).x;
end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), font_size).x;
}
Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent(font_size) + font_height * i + yofs);
round_ofs = round_ofs.round();
- draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, font_size, font_color);
+ draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
if (end > 0) {
// Draw an underline for the currently edited function parameter.
const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + yofs);
@@ -315,7 +318,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
} else {
if (mb->get_button_index() == MouseButton::LEFT) {
- if (mb->is_command_pressed() && symbol_lookup_word != String()) {
+ if (mb->is_command_pressed() && !symbol_lookup_word.is_empty()) {
Vector2i mpos = mb->get_position();
if (is_layout_rtl()) {
mpos.x = get_size().x - mpos.x;
@@ -354,6 +357,11 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
Ref<InputEventKey> k = p_gui_input;
+ if (TextEdit::alt_input(p_gui_input)) {
+ accept_event();
+ return;
+ }
+
bool update_code_completion = false;
if (!k.is_valid()) {
TextEdit::gui_input(p_gui_input);
@@ -530,7 +538,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
/* General overrides */
Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
- if (symbol_lookup_word != String()) {
+ if (!symbol_lookup_word.is_empty()) {
return CURSOR_POINTING_HAND;
}
@@ -561,6 +569,8 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
// Overridable actions
void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
bool had_selection = has_selection();
+ String selection_text = (had_selection ? get_selected_text() : "");
+
if (had_selection) {
begin_complex_operation();
delete_selection();
@@ -581,27 +591,38 @@ void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
if (auto_brace_completion_enabled) {
int cl = get_caret_line();
int cc = get_caret_column();
- int caret_move_offset = 1;
- int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
-
- if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
- insert_text_at_caret(chr);
- } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) {
- insert_text_at_caret(chr);
- } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
- caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
- } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ if (had_selection) {
insert_text_at_caret(chr);
+
+ String close_key = get_auto_brace_completion_close_key(chr);
+ if (!close_key.is_empty()) {
+ insert_text_at_caret(selection_text + close_key);
+ set_caret_column(get_caret_column() - 1);
+ }
} else {
- insert_text_at_caret(chr);
+ int caret_move_offset = 1;
+
+ int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
+
+ if (has_string_delimiter(chr) && cc > 0 && !is_symbol(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
+ insert_text_at_caret(chr);
+ } else if (cc < get_line(cl).length() && !is_symbol(get_line(cl)[cc])) {
+ insert_text_at_caret(chr);
+ } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
+ caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
+ } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ insert_text_at_caret(chr);
+ } else {
+ insert_text_at_caret(chr);
- int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
- if (pre_brace_pair != -1) {
- insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
+ if (pre_brace_pair != -1) {
+ insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ }
}
+ set_caret_column(cc + caret_move_offset);
}
- set_caret_column(cc + caret_move_offset);
} else {
insert_text_at_caret(chr);
}
@@ -719,7 +740,7 @@ void CodeEdit::set_auto_indent_prefixes(const TypedArray<String> &p_prefixes) {
TypedArray<String> CodeEdit::get_auto_indent_prefixes() const {
TypedArray<String> prefixes;
- for (const Set<char32_t>::Element *E = auto_indent_prefixes.front(); E; E = E->next()) {
+ for (const RBSet<char32_t>::Element *E = auto_indent_prefixes.front(); E; E = E->next()) {
prefixes.push_back(String::chr(E->get()));
}
return prefixes;
@@ -927,8 +948,10 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
return;
}
- const int cc = get_caret_column();
+ /* When not splitting the line, we need to factor in indentation from the end of the current line. */
+ const int cc = p_split_current_line ? get_caret_column() : get_line(get_caret_line()).length();
const int cl = get_caret_line();
+
const String line = get_line(cl);
String ins = "\n";
@@ -976,7 +999,8 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
}
/* Make sure this is the last char, trailing whitespace or comments are okay. */
- if (should_indent && (!_is_whitespace(c) && is_in_comment(cl, cc) == -1)) {
+ /* Increment column for comments because the delimiter (#) should be ignored. */
+ if (should_indent && (!is_whitespace(c) && is_in_comment(cl, line_col + 1) == -1)) {
should_indent = false;
}
}
@@ -1002,6 +1026,8 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
bool first_line = false;
if (!p_split_current_line) {
+ deselect();
+
if (p_above) {
if (cl > 0) {
set_caret_line(cl - 1, false);
@@ -1419,40 +1445,23 @@ void CodeEdit::fold_line(int p_line) {
/* End line is the same therefore we have a block of single line delimiters. */
if (end_line == p_line) {
for (int i = p_line + 1; i <= line_count; i++) {
- if (i == line_count) {
- end_line = line_count;
- break;
- }
-
if ((in_string != -1 && is_in_string(i) == -1) || (in_comment != -1 && is_in_comment(i) == -1)) {
- end_line = i - 1;
break;
}
+ end_line = i;
}
}
} else {
int start_indent = get_indent_level(p_line);
for (int i = p_line + 1; i <= line_count; i++) {
- if (get_line(p_line).strip_edges().size() == 0 || is_in_string(i) != -1 || is_in_comment(i) != -1) {
- end_line = i;
+ if (get_line(i).strip_edges().size() == 0) {
continue;
}
-
- if (i == line_count) {
- /* Do not fold empty last line of script if any */
+ if (get_indent_level(i) > start_indent) {
end_line = i;
- if (get_line(i).strip_edges().size() == 0) {
- end_line--;
- }
- break;
+ continue;
}
-
- if ((get_indent_level(i) <= start_indent && get_line(i).strip_edges().size() != 0)) {
- /* Keep an empty line unfolded if any */
- end_line = i - 1;
- if (get_line(i - 1).strip_edges().size() == 0 && i - 2 > p_line) {
- end_line = i - 2;
- }
+ if (is_in_string(i) == -1 && is_in_comment(i) == -1) {
break;
}
}
@@ -1619,7 +1628,7 @@ Point2 CodeEdit::get_delimiter_start_position(int p_line, int p_column) const {
start_position.y = -1;
start_position.x = -1;
- bool in_region = ((p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value()) != -1;
+ bool in_region = ((p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->get()) != -1;
/* Check the keys for this line. */
for (const KeyValue<int, int> &E : delimiter_cache[p_line]) {
@@ -1743,7 +1752,7 @@ void CodeEdit::set_code_completion_prefixes(const TypedArray<String> &p_prefixes
TypedArray<String> CodeEdit::get_code_completion_prefixes() const {
TypedArray<String> prefixes;
- for (const Set<char32_t>::Element *E = code_completion_prefixes.front(); E; E = E->next()) {
+ for (const RBSet<char32_t>::Element *E = code_completion_prefixes.front(); E; E = E->next()) {
prefixes.push_back(String::chr(E->get()));
}
return prefixes;
@@ -1779,10 +1788,10 @@ void CodeEdit::request_code_completion(bool p_force) {
/* Don't re-query if all existing options are quoted types, eg path, signal. */
bool ignored = code_completion_active && !code_completion_options.is_empty();
if (ignored) {
- ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_PLAIN_TEXT;
- const ScriptCodeCompletionOption *previous_option = nullptr;
+ ScriptLanguage::CodeCompletionKind kind = ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT;
+ const ScriptLanguage::CodeCompletionOption *previous_option = nullptr;
for (int i = 0; i < code_completion_options.size(); i++) {
- const ScriptCodeCompletionOption &current_option = code_completion_options[i];
+ const ScriptLanguage::CodeCompletionOption &current_option = code_completion_options[i];
if (!previous_option) {
previous_option = &current_option;
kind = current_option.kind;
@@ -1792,7 +1801,7 @@ void CodeEdit::request_code_completion(bool p_force) {
break;
}
}
- ignored = ignored && (kind == ScriptCodeCompletionOption::KIND_FILE_PATH || kind == ScriptCodeCompletionOption::KIND_NODE_PATH || kind == ScriptCodeCompletionOption::KIND_SIGNAL);
+ ignored = ignored && (kind == ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH || kind == ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH || kind == ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL);
}
if (ignored) {
@@ -1800,23 +1809,23 @@ void CodeEdit::request_code_completion(bool p_force) {
}
if (p_force) {
- emit_signal(SNAME("request_code_completion"));
+ emit_signal(SNAME("code_completion_requested"));
return;
}
String line = get_line(get_caret_line());
int ofs = CLAMP(get_caret_column(), 0, line.length());
- if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
- emit_signal(SNAME("request_code_completion"));
+ if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || !is_symbol(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
+ emit_signal(SNAME("code_completion_requested"));
} else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(line[ofs - 2])) {
- emit_signal(SNAME("request_code_completion"));
+ emit_signal(SNAME("code_completion_requested"));
}
}
-void CodeEdit::add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color, const RES &p_icon, const Variant &p_value) {
- ScriptCodeCompletionOption completion_option;
- completion_option.kind = (ScriptCodeCompletionOption::Kind)p_type;
+void CodeEdit::add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color, const Ref<Resource> &p_icon, const Variant &p_value) {
+ ScriptLanguage::CodeCompletionOption completion_option;
+ completion_option.kind = (ScriptLanguage::CodeCompletionKind)p_type;
completion_option.display = p_display_text;
completion_option.insert_text = p_insert_text;
completion_option.font_color = p_text_color;
@@ -1916,7 +1925,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
if (merge_text) {
for (; caret_col < line.length(); caret_col++) {
- if (!_is_char(line[caret_col])) {
+ if (is_symbol(line[caret_col])) {
break;
}
}
@@ -2092,8 +2101,6 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auto_brace_completion_close_key", "open_key"), &CodeEdit::get_auto_brace_completion_close_key);
/* Main Gutter */
- ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_breakpoints_gutter", "enable"), &CodeEdit::set_draw_breakpoints_gutter);
ClassDB::bind_method(D_METHOD("is_drawing_breakpoints_gutter"), &CodeEdit::is_drawing_breakpoints_gutter);
@@ -2122,16 +2129,12 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_executing_lines"), &CodeEdit::get_executing_lines);
/* Line numbers */
- ClassDB::bind_method(D_METHOD("_line_number_draw_callback"), &CodeEdit::_line_number_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_line_numbers", "enable"), &CodeEdit::set_draw_line_numbers);
ClassDB::bind_method(D_METHOD("is_draw_line_numbers_enabled"), &CodeEdit::is_draw_line_numbers_enabled);
ClassDB::bind_method(D_METHOD("set_line_numbers_zero_padded", "enable"), &CodeEdit::set_line_numbers_zero_padded);
ClassDB::bind_method(D_METHOD("is_line_numbers_zero_padded"), &CodeEdit::is_line_numbers_zero_padded);
/* Fold Gutter */
- ClassDB::bind_method(D_METHOD("_fold_gutter_draw_callback"), &CodeEdit::_fold_gutter_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_fold_gutter", "enable"), &CodeEdit::set_draw_fold_gutter);
ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &CodeEdit::is_drawing_fold_gutter);
@@ -2198,7 +2201,7 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text_for_code_completion"), &CodeEdit::get_text_for_code_completion);
ClassDB::bind_method(D_METHOD("request_code_completion", "force"), &CodeEdit::request_code_completion, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value"), &CodeEdit::add_code_completion_option, DEFVAL(Color(1, 1, 1)), DEFVAL(RES()), DEFVAL(Variant::NIL));
+ ClassDB::bind_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value"), &CodeEdit::add_code_completion_option, DEFVAL(Color(1, 1, 1)), DEFVAL(Ref<Resource>()), DEFVAL(Variant::NIL));
ClassDB::bind_method(D_METHOD("update_code_completion_options", "force"), &CodeEdit::update_code_completion_options);
ClassDB::bind_method(D_METHOD("get_code_completion_options"), &CodeEdit::get_code_completion_options);
ClassDB::bind_method(D_METHOD("get_code_completion_option", "index"), &CodeEdit::get_code_completion_option);
@@ -2264,7 +2267,7 @@ void CodeEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_automatic"), "set_auto_indent_enabled", "is_auto_indent_enabled");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "indent_automatic_prefixes"), "set_auto_indent_prefixes", "get_auto_indent_prefixes");
- ADD_GROUP("Auto brace completion", "auto_brace_completion_");
+ ADD_GROUP("Auto Brace Completion", "auto_brace_completion_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_brace_completion_enabled"), "set_auto_brace_completion_enabled", "is_auto_brace_completion_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_brace_completion_highlight_matching"), "set_highlight_matching_braces_enabled", "is_highlight_matching_braces_enabled");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "auto_brace_completion_pairs"), "set_auto_brace_completion_pairs", "get_auto_brace_completion_pairs");
@@ -2274,7 +2277,7 @@ void CodeEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line")));
/* Code Completion */
- ADD_SIGNAL(MethodInfo("request_code_completion"));
+ ADD_SIGNAL(MethodInfo("code_completion_requested"));
/* Symbol lookup */
ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "column")));
@@ -2395,11 +2398,11 @@ void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) {
if (start_line != end_line) {
if (p_to_line < p_from_line) {
for (int i = end_line; i > start_line; i--) {
- delimiter_cache.remove(i);
+ delimiter_cache.remove_at(i);
}
} else {
for (int i = start_line; i < end_line; i++) {
- delimiter_cache.insert(i, Map<int, int>());
+ delimiter_cache.insert(i, RBMap<int, int>());
}
}
}
@@ -2536,7 +2539,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
int region = (p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value();
bool in_region = region != -1 && delimiters[region].type == p_type;
- for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) {
+ for (RBMap<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) {
/* If column is specified, loop until the key is larger then the column. */
if (p_column != -1) {
if (E->key() > p_column) {
@@ -2558,7 +2561,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
region = E->value();
in_region = true;
for (int i = E->key() - 2; i >= 0; i--) {
- if (!_is_whitespace(line[i])) {
+ if (!is_whitespace(line[i])) {
return -1;
}
}
@@ -2577,7 +2580,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
}
for (int i = end_col; i < line.length(); i++) {
- if (!_is_whitespace(line[i])) {
+ if (!is_whitespace(line[i])) {
return -1;
}
}
@@ -2614,7 +2617,7 @@ void CodeEdit::_add_delimiter(const String &p_start_key, const String &p_end_key
delimiter.type = p_type;
delimiter.start_key = p_start_key;
delimiter.end_key = p_end_key;
- delimiter.line_only = p_line_only || p_end_key == "";
+ delimiter.line_only = p_line_only || p_end_key.is_empty();
delimiters.insert(at, delimiter);
if (!setting_delimiters) {
delimiter_cache.clear();
@@ -2632,7 +2635,7 @@ void CodeEdit::_remove_delimiter(const String &p_start_key, DelimiterType p_type
break;
}
- delimiters.remove(i);
+ delimiters.remove_at(i);
if (!setting_delimiters) {
delimiter_cache.clear();
_update_delimiter_cache();
@@ -2664,7 +2667,7 @@ void CodeEdit::_set_delimiters(const TypedArray<String> &p_delimiters, Delimiter
const String start_key = key.get_slice(" ", 0);
const String end_key = key.get_slice_count(" ") > 1 ? key.get_slice(" ", 1) : String();
- _add_delimiter(start_key, end_key, end_key == "", p_type);
+ _add_delimiter(start_key, end_key, end_key.is_empty(), p_type);
}
setting_delimiters = false;
_update_delimiter_cache();
@@ -2673,7 +2676,7 @@ void CodeEdit::_set_delimiters(const TypedArray<String> &p_delimiters, Delimiter
void CodeEdit::_clear_delimiters(DelimiterType p_type) {
for (int i = delimiters.size() - 1; i >= 0; i--) {
if (delimiters[i].type == p_type) {
- delimiters.remove(i);
+ delimiters.remove_at(i);
}
}
delimiter_cache.clear();
@@ -2705,7 +2708,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
TypedArray<Dictionary> completion_options_sources;
completion_options_sources.resize(code_completion_option_sources.size());
int i = 0;
- for (const ScriptCodeCompletionOption &E : code_completion_option_sources) {
+ for (const ScriptLanguage::CodeCompletionOption &E : code_completion_option_sources) {
Dictionary option;
option["kind"] = E.kind;
option["display_text"] = E.display;
@@ -2730,8 +2733,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
/* Convert back into options. */
int max_width = 0;
for (i = 0; i < completion_options.size(); i++) {
- ScriptCodeCompletionOption option;
- option.kind = (ScriptCodeCompletionOption::Kind)(int)completion_options[i].get("kind");
+ ScriptLanguage::CodeCompletionOption option;
+ option.kind = (ScriptLanguage::CodeCompletionKind)(int)completion_options[i].get("kind");
option.display = completion_options[i].get("display_text");
option.insert_text = completion_options[i].get("insert_text");
option.font_color = completion_options[i].get("font_color");
@@ -2793,11 +2796,11 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
while (ofs > 0 && line[ofs] == ' ') {
ofs--;
}
- prev_is_word = _is_char(line[ofs]);
+ prev_is_word = !is_symbol(line[ofs]);
/* Otherwise get current word and set cofs to the start. */
} else {
int start_cofs = cofs;
- while (cofs > 0 && line[cofs - 1] > 32 && (line[cofs - 1] == '/' || _is_char(line[cofs - 1]))) {
+ while (cofs > 0 && line[cofs - 1] > 32 && (line[cofs - 1] == '/' || !is_symbol(line[cofs - 1]))) {
cofs--;
}
string_to_complete = line.substr(cofs, start_cofs - cofs);
@@ -2824,13 +2827,15 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
code_completion_options.clear();
code_completion_base = string_to_complete;
- Vector<ScriptCodeCompletionOption> completion_options_casei;
- Vector<ScriptCodeCompletionOption> completion_options_subseq;
- Vector<ScriptCodeCompletionOption> completion_options_subseq_casei;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_casei;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_substr;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_substr_casei;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_subseq;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_subseq_casei;
int max_width = 0;
String string_to_complete_lower = string_to_complete.to_lower();
- for (ScriptCodeCompletionOption &option : code_completion_option_sources) {
+ for (ScriptLanguage::CodeCompletionOption &option : code_completion_option_sources) {
if (single_quote && option.display.is_quoted()) {
option.display = option.display.unquote().quote("'");
}
@@ -2864,7 +2869,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
completion_options_casei.push_back(option);
} else if (s.is_subsequence_of(option.display)) {
completion_options_subseq.push_back(option);
- } else if (s.is_subsequence_ofi(option.display)) {
+ } else if (s.is_subsequence_ofn(option.display)) {
completion_options_subseq_casei.push_back(option);
}
@@ -2879,35 +2884,100 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
const char32_t *tgt = &option.display[0];
const char32_t *tgt_lower = &display_lower[0];
- const char32_t *ssq_last_tgt = nullptr;
- const char32_t *ssq_lower_last_tgt = nullptr;
+ const char32_t *sst = &string_to_complete[0];
+ const char32_t *sst_lower = &display_lower[0];
+
+ Vector<Pair<int, int>> ssq_matches;
+ int ssq_match_start = 0;
+ int ssq_match_len = 0;
+
+ Vector<Pair<int, int>> ssq_lower_matches;
+ int ssq_lower_match_start = 0;
+ int ssq_lower_match_len = 0;
+
+ int sst_start = -1;
+ int sst_lower_start = -1;
- for (; *tgt; tgt++, tgt_lower++) {
+ for (int i = 0; *tgt; tgt++, tgt_lower++, i++) {
+ // Check substring.
+ if (*sst == *tgt) {
+ sst++;
+ if (sst_start == -1) {
+ sst_start = i;
+ }
+ } else if (sst_start != -1 && *sst) {
+ sst = &string_to_complete[0];
+ sst_start = -1;
+ }
+
+ // Check subsequence.
if (*ssq == *tgt) {
ssq++;
- ssq_last_tgt = tgt;
+ if (ssq_match_len == 0) {
+ ssq_match_start = i;
+ }
+ ssq_match_len++;
+ } else if (ssq_match_len > 0) {
+ ssq_matches.push_back(Pair<int, int>(ssq_match_start, ssq_match_len));
+ ssq_match_len = 0;
+ }
+
+ // Check lower substring.
+ if (*sst_lower == *tgt) {
+ sst_lower++;
+ if (sst_lower_start == -1) {
+ sst_lower_start = i;
+ }
+ } else if (sst_lower_start != -1 && *sst_lower) {
+ sst_lower = &string_to_complete[0];
+ sst_lower_start = -1;
}
+
+ // Check lower subsequence.
if (*ssq_lower == *tgt_lower) {
ssq_lower++;
- ssq_lower_last_tgt = tgt;
+ if (ssq_lower_match_len == 0) {
+ ssq_lower_match_start = i;
+ }
+ ssq_lower_match_len++;
+ } else if (ssq_lower_match_len > 0) {
+ ssq_lower_matches.push_back(Pair<int, int>(ssq_lower_match_start, ssq_lower_match_len));
+ ssq_lower_match_len = 0;
}
}
/* Matched the whole subsequence in s. */
- if (!*ssq) {
- /* Finished matching in the first s.length() characters. */
- if (ssq_last_tgt == &option.display[string_to_complete.length() - 1]) {
+ if (!*ssq) { // Matched the whole subsequence in s.
+ option.matches.clear();
+
+ if (sst_start == 0) { // Matched substring in beginning of s.
+ option.matches.push_back(Pair<int, int>(sst_start, string_to_complete.length()));
code_completion_options.push_back(option);
+ } else if (sst_start > 0) { // Matched substring in s.
+ option.matches.push_back(Pair<int, int>(sst_start, string_to_complete.length()));
+ completion_options_substr.push_back(option);
} else {
+ if (ssq_match_len > 0) {
+ ssq_matches.push_back(Pair<int, int>(ssq_match_start, ssq_match_len));
+ }
+ option.matches.append_array(ssq_matches);
completion_options_subseq.push_back(option);
}
max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset);
- /* Matched the whole subsequence in s_lower. */
- } else if (!*ssq_lower) {
- /* Finished matching in the first s.length() characters. */
- if (ssq_lower_last_tgt == &option.display[string_to_complete.length() - 1]) {
+ } else if (!*ssq_lower) { // Matched the whole subsequence in s_lower.
+ option.matches.clear();
+
+ if (sst_lower_start == 0) { // Matched substring in beginning of s_lower.
+ option.matches.push_back(Pair<int, int>(sst_lower_start, string_to_complete.length()));
completion_options_casei.push_back(option);
+ } else if (sst_lower_start > 0) { // Matched substring in s_lower.
+ option.matches.push_back(Pair<int, int>(sst_lower_start, string_to_complete.length()));
+ completion_options_substr_casei.push_back(option);
} else {
+ if (ssq_lower_match_len > 0) {
+ ssq_lower_matches.push_back(Pair<int, int>(ssq_lower_match_start, ssq_lower_match_len));
+ }
+ option.matches.append_array(ssq_lower_matches);
completion_options_subseq_casei.push_back(option);
}
max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset);
@@ -2971,7 +3041,9 @@ void CodeEdit::_text_changed() {
lc = get_line_count();
List<int> breakpoints;
- breakpointed_lines.get_key_list(&breakpoints);
+ for (const KeyValue<int, bool> &E : breakpointed_lines) {
+ breakpoints.push_back(E.key);
+ }
for (const int &line : breakpoints) {
if (line < lines_edited_from || (line < lc && is_line_breakpointed(line))) {
continue;
@@ -3007,7 +3079,7 @@ CodeEdit::CodeEdit() {
add_auto_brace_completion_pair("\"", "\"");
add_auto_brace_completion_pair("\'", "\'");
- /* Delimiter traking */
+ /* Delimiter tracking */
add_string_delimiter("\"", "\"", false);
add_string_delimiter("\'", "\'", false);
@@ -3024,7 +3096,7 @@ CodeEdit::CodeEdit() {
set_gutter_draw(gutter_idx, false);
set_gutter_overwritable(gutter_idx, true);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_main_gutter_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_main_gutter_draw_callback));
gutter_idx++;
/* Line numbers */
@@ -3032,7 +3104,7 @@ CodeEdit::CodeEdit() {
set_gutter_name(gutter_idx, "line_numbers");
set_gutter_draw(gutter_idx, false);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_line_number_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_line_number_draw_callback));
gutter_idx++;
/* Fold Gutter */
@@ -3040,7 +3112,7 @@ CodeEdit::CodeEdit() {
set_gutter_name(gutter_idx, "fold_gutter");
set_gutter_draw(gutter_idx, false);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_fold_gutter_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_fold_gutter_draw_callback));
gutter_idx++;
connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from));
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index f0d971dd35..0b00735f46 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,7 +38,7 @@ class CodeEdit : public TextEdit {
public:
/* Keep enum in sync with: */
- /* /core/object/script_language.h - ScriptCodeCompletionOption::Kind */
+ /* /core/object/script_language.h - ScriptLanguage::CodeCompletionKind */
enum CodeCompletionKind {
KIND_CLASS,
KIND_FUNCTION,
@@ -58,7 +58,7 @@ private:
String indent_text = "\t";
bool auto_indent = false;
- Set<char32_t> auto_indent_prefixes;
+ RBSet<char32_t> auto_indent_prefixes;
bool indent_using_spaces = false;
int _calculate_spaces_till_next_left_indent(int p_column) const;
@@ -176,7 +176,7 @@ private:
* ]
* ]
*/
- Vector<Map<int, int>> delimiter_cache;
+ Vector<RBMap<int, int>> delimiter_cache;
void _update_delimiter_cache(int p_from_line = 0, int p_to_line = -1);
int _is_in_delimiter(int p_line, int p_column, DelimiterType p_type) const;
@@ -208,15 +208,15 @@ private:
Color code_completion_existing_color = Color(0, 0, 0, 0);
bool code_completion_active = false;
- Vector<ScriptCodeCompletionOption> code_completion_options;
+ Vector<ScriptLanguage::CodeCompletionOption> code_completion_options;
int code_completion_line_ofs = 0;
int code_completion_current_selected = 0;
int code_completion_longest_line = 0;
Rect2i code_completion_rect;
- Set<char32_t> code_completion_prefixes;
- List<ScriptCodeCompletionOption> code_completion_option_submitted;
- List<ScriptCodeCompletionOption> code_completion_option_sources;
+ RBSet<char32_t> code_completion_prefixes;
+ List<ScriptLanguage::CodeCompletionOption> code_completion_option_submitted;
+ List<ScriptLanguage::CodeCompletionOption> code_completion_option_sources;
String code_completion_base;
void _filter_code_completion_candidates_impl();
@@ -398,7 +398,7 @@ public:
void request_code_completion(bool p_force = false);
- void add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color = Color(1, 1, 1), const RES &p_icon = RES(), const Variant &p_value = Variant::NIL);
+ void add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color = Color(1, 1, 1), const Ref<Resource> &p_icon = Ref<Resource>(), const Variant &p_value = Variant::NIL);
void update_code_completion_options(bool p_forced = false);
TypedArray<Dictionary> get_code_completion_options() const;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 5a378554c9..6f7ad94139 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,11 +33,11 @@
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
+#include "scene/main/window.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif
-#include "scene/main/window.h"
List<Color> ColorPicker::preset_cache;
@@ -45,7 +45,6 @@ void ColorPicker::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_update_color();
-
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
if (preset_cache.is_empty()) {
@@ -437,7 +436,7 @@ ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const {
}
inline int ColorPicker::_get_preset_size() {
- return (int(get_minimum_size().width) - (preset_container->get_theme_constant(SNAME("hseparation")) * (preset_column_count - 1))) / preset_column_count;
+ return (int(get_minimum_size().width) - (preset_container->get_theme_constant(SNAME("h_separation")) * (preset_column_count - 1))) / preset_column_count;
}
void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
@@ -1222,7 +1221,7 @@ ColorPicker::ColorPicker() :
hhb->add_child(text_type);
text_type->set_text("#");
- text_type->set_tooltip(TTR("Switch between hexadecimal and code values."));
+ text_type->set_tooltip(RTR("Switch between hexadecimal and code values."));
if (Engine::get_singleton()->is_editor_hint()) {
text_type->connect("pressed", callable_mp(this, &ColorPicker::_text_type_toggled));
} else {
@@ -1274,7 +1273,7 @@ ColorPicker::ColorPicker() :
preset_container->set_columns(preset_column_count);
add_child(preset_container, false, INTERNAL_MODE_FRONT);
- btn_add_preset->set_icon_align(Button::ALIGN_CENTER);
+ btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
btn_add_preset->set_tooltip(RTR("Add current color as a preset."));
btn_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed));
preset_container->add_child(btn_add_preset);
@@ -1305,7 +1304,7 @@ void ColorPickerButton::pressed() {
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
- popup->set_as_minsize();
+ popup->reset_size();
picker->_update_presets();
Rect2i usable_rect = popup->get_usable_parent_rect();
@@ -1347,17 +1346,18 @@ void ColorPickerButton::_notification(int p_what) {
draw_texture(Control::get_theme_icon(SNAME("overbright_indicator"), SNAME("ColorPicker")), normal->get_offset());
}
} break;
+
case NOTIFICATION_WM_CLOSE_REQUEST: {
if (popup) {
popup->hide();
}
} break;
- }
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- if (popup && !is_visible_in_tree()) {
- popup->hide();
- }
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (popup && !is_visible_in_tree()) {
+ popup->hide();
+ }
+ } break;
}
}
@@ -1429,7 +1429,8 @@ void ColorPickerButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
}
-ColorPickerButton::ColorPickerButton() {
+ColorPickerButton::ColorPickerButton(const String &p_text) :
+ Button(p_text) {
set_toggle_mode(true);
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index ad4f5ad5b1..6f3e16009c 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -231,7 +231,7 @@ public:
ColorPicker *get_picker();
PopupPanel *get_popup();
- ColorPickerButton();
+ ColorPickerButton(const String &p_text = String());
};
VARIANT_ENUM_CAST(ColorPicker::PickerShapeType);
diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp
index e35d37d520..2955f74a0c 100644
--- a/scene/gui/color_rect.cpp
+++ b/scene/gui/color_rect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,8 +40,10 @@ Color ColorRect::get_color() const {
}
void ColorRect::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- draw_rect(Rect2(Point2(), get_size()), color);
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ draw_rect(Rect2(Point2(), get_size()), color);
+ } break;
}
}
diff --git a/scene/gui/color_rect.h b/scene/gui/color_rect.h
index 5c650f9f01..35c8ebcaf8 100644
--- a/scene/gui/color_rect.h
+++ b/scene/gui/color_rect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index a1bd82f6f7..5512c0f1fd 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,13 +29,14 @@
/*************************************************************************/
#include "container.h"
+
#include "core/object/message_queue.h"
#include "scene/scene_string_names.h"
void Container::_child_minsize_changed() {
//Size2 ms = get_combined_minimum_size();
//if (ms.width > get_size().width || ms.height > get_size().height) {
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
@@ -51,7 +52,7 @@ void Container::add_child_notify(Node *p_child) {
control->connect(SNAME("minimum_size_changed"), callable_mp(this, &Container::_child_minsize_changed));
control->connect(SNAME("visibility_changed"), callable_mp(this, &Container::_child_minsize_changed));
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
@@ -62,7 +63,7 @@ void Container::move_child_notify(Node *p_child) {
return;
}
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
@@ -78,7 +79,7 @@ void Container::remove_child_notify(Node *p_child) {
control->disconnect("minimum_size_changed", callable_mp(this, &Container::_child_minsize_changed));
control->disconnect("visibility_changed", callable_mp(this, &Container::_child_minsize_changed));
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
@@ -143,18 +144,46 @@ void Container::queue_sort() {
pending_sort = true;
}
+Vector<int> Container::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ if (GDVIRTUAL_CALL(_get_allowed_size_flags_horizontal, flags)) {
+ return flags;
+ }
+
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_EXPAND);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> Container::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ if (GDVIRTUAL_CALL(_get_allowed_size_flags_vertical, flags)) {
+ return flags;
+ }
+
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_EXPAND);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void Container::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
pending_sort = false;
queue_sort();
} break;
- case NOTIFICATION_RESIZED: {
- queue_sort();
- } break;
+
+ case NOTIFICATION_RESIZED:
case NOTIFICATION_THEME_CHANGED: {
queue_sort();
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible_in_tree()) {
queue_sort();
@@ -167,7 +196,7 @@ TypedArray<String> Container::get_configuration_warnings() const {
TypedArray<String> warnings = Control::get_configuration_warnings();
if (get_class() == "Container" && get_script().is_null()) {
- warnings.push_back(TTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead."));
+ warnings.push_back(RTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead."));
}
return warnings;
@@ -177,6 +206,9 @@ void Container::_bind_methods() {
ClassDB::bind_method(D_METHOD("queue_sort"), &Container::queue_sort);
ClassDB::bind_method(D_METHOD("fit_child_in_rect", "child", "rect"), &Container::fit_child_in_rect);
+ GDVIRTUAL_BIND(_get_allowed_size_flags_horizontal);
+ GDVIRTUAL_BIND(_get_allowed_size_flags_vertical);
+
BIND_CONSTANT(NOTIFICATION_PRE_SORT_CHILDREN);
BIND_CONSTANT(NOTIFICATION_SORT_CHILDREN);
diff --git a/scene/gui/container.h b/scene/gui/container.h
index f3ae948556..9ec4ad3200 100644
--- a/scene/gui/container.h
+++ b/scene/gui/container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,6 +46,9 @@ protected:
virtual void move_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
+ GDVIRTUAL0RC(Vector<int>, _get_allowed_size_flags_horizontal)
+ GDVIRTUAL0RC(Vector<int>, _get_allowed_size_flags_vertical)
+
void _notification(int p_what);
static void _bind_methods();
@@ -57,6 +60,9 @@ public:
void fit_child_in_rect(Control *p_child, const Rect2 &p_rect);
+ virtual Vector<int> get_allowed_size_flags_horizontal() const;
+ virtual Vector<int> get_allowed_size_flags_vertical() const;
+
TypedArray<String> get_configuration_warnings() const override;
Container();
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 582c8e5860..a0104387c9 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,7 +47,7 @@
#include "servers/text_server.h"
#ifdef TOOLS_ENABLED
-#include "editor/plugins/canvas_item_editor_plugin.h"
+#include "editor/plugins/control_editor_plugin.h"
#endif
#ifdef TOOLS_ENABLED
@@ -56,51 +56,71 @@ Dictionary Control::_edit_get_state() const {
s["rotation"] = get_rotation();
s["scale"] = get_scale();
s["pivot"] = get_pivot_offset();
+
Array anchors;
anchors.push_back(get_anchor(SIDE_LEFT));
anchors.push_back(get_anchor(SIDE_TOP));
anchors.push_back(get_anchor(SIDE_RIGHT));
anchors.push_back(get_anchor(SIDE_BOTTOM));
s["anchors"] = anchors;
+
Array offsets;
offsets.push_back(get_offset(SIDE_LEFT));
offsets.push_back(get_offset(SIDE_TOP));
offsets.push_back(get_offset(SIDE_RIGHT));
offsets.push_back(get_offset(SIDE_BOTTOM));
s["offsets"] = offsets;
+
+ s["layout_mode"] = _get_layout_mode();
+ s["anchors_layout_preset"] = _get_anchors_layout_preset();
+
return s;
}
void Control::_edit_set_state(const Dictionary &p_state) {
ERR_FAIL_COND((p_state.size() <= 0) ||
!p_state.has("rotation") || !p_state.has("scale") ||
- !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets"));
+ !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets") ||
+ !p_state.has("layout_mode") || !p_state.has("anchors_layout_preset"));
Dictionary state = p_state;
set_rotation(state["rotation"]);
set_scale(state["scale"]);
set_pivot_offset(state["pivot"]);
+
Array anchors = state["anchors"];
+
+ // If anchors are not in their default position, force the anchor layout mode in place of position.
+ LayoutMode _layout = (LayoutMode)(int)state["layout_mode"];
+ if (_layout == LayoutMode::LAYOUT_MODE_POSITION) {
+ bool anchors_mode = ((real_t)anchors[0] != 0.0 || (real_t)anchors[1] != 0.0 || (real_t)anchors[2] != 0.0 || (real_t)anchors[3] != 0.0);
+ if (anchors_mode) {
+ _layout = LayoutMode::LAYOUT_MODE_ANCHORS;
+ }
+ }
+
+ _set_layout_mode(_layout);
+ if (_layout == LayoutMode::LAYOUT_MODE_ANCHORS) {
+ _set_anchors_layout_preset((int)state["anchors_layout_preset"]);
+ }
+
data.anchor[SIDE_LEFT] = anchors[0];
data.anchor[SIDE_TOP] = anchors[1];
data.anchor[SIDE_RIGHT] = anchors[2];
data.anchor[SIDE_BOTTOM] = anchors[3];
+
Array offsets = state["offsets"];
data.offset[SIDE_LEFT] = offsets[0];
data.offset[SIDE_TOP] = offsets[1];
data.offset[SIDE_RIGHT] = offsets[2];
data.offset[SIDE_BOTTOM] = offsets[3];
+
_size_changed();
}
void Control::_edit_set_position(const Point2 &p_position) {
-#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
- set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
-#else
- // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
- set_position(p_position);
-#endif
+ set_position(p_position, ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
};
Point2 Control::_edit_get_position() const {
@@ -116,15 +136,9 @@ Size2 Control::_edit_get_scale() const {
}
void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
-#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
- set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
- set_size(p_edit_rect.size.snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
-#else
- // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
- set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)));
- set_size(p_edit_rect.size.snapped(Vector2(1, 1)));
-#endif
+ set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled());
+ set_size(p_edit_rect.size.snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled());
}
Rect2 Control::_edit_get_rect() const {
@@ -176,9 +190,10 @@ String Control::properties_managed_by_container[] = {
"anchor_top",
"anchor_right",
"anchor_bottom",
- "rect_position",
- "rect_scale",
- "rect_size"
+ "position",
+ "rotation",
+ "scale",
+ "size"
};
void Control::accept_event() {
@@ -192,7 +207,7 @@ void Control::set_custom_minimum_size(const Size2 &p_custom) {
return;
}
data.custom_minimum_size = p_custom;
- minimum_size_changed();
+ update_minimum_size();
}
Size2 Control::get_custom_minimum_size() const {
@@ -213,7 +228,7 @@ void Control::_update_minimum_size_cache() {
data.minimum_size_valid = true;
if (size_changed) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -235,42 +250,41 @@ Transform2D Control::_get_internal_transform() const {
bool Control::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
- // Prefixes "custom_*" are supported for compatibility with 3.x.
- if (!name.begins_with("theme_override") && !name.begins_with("custom")) {
+ if (!name.begins_with("theme_override")) {
return false;
}
- if (p_value.get_type() == Variant::NIL) {
- if (name.begins_with("theme_override_icons/") || name.begins_with("custom_icons/")) {
+ if (p_value.get_type() == Variant::NIL || (p_value.get_type() == Variant::OBJECT && (Object *)p_value == nullptr)) {
+ 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.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_styles/") || name.begins_with("custom_styles/")) {
+ } 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.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_fonts/") || name.begins_with("custom_fonts/")) {
+ } 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.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_font_sizes/") || name.begins_with("custom_font_sizes/")) {
+ } else if (name.begins_with("theme_override_font_sizes/")) {
String dname = name.get_slicec('/', 1);
data.font_size_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_colors/") || name.begins_with("custom_colors/")) {
+ } else if (name.begins_with("theme_override_colors/")) {
String dname = name.get_slicec('/', 1);
data.color_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_constants/") || name.begins_with("custom_constants/")) {
+ } else if (name.begins_with("theme_override_constants/")) {
String dname = name.get_slicec('/', 1);
data.constant_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
@@ -279,22 +293,22 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
}
} else {
- if (name.begins_with("theme_override_icons/") || name.begins_with("custom_icons/")) {
+ if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
add_theme_icon_override(dname, p_value);
- } else if (name.begins_with("theme_override_styles/") || name.begins_with("custom_styles/")) {
+ } else if (name.begins_with("theme_override_styles/")) {
String dname = name.get_slicec('/', 1);
add_theme_style_override(dname, p_value);
- } else if (name.begins_with("theme_override_fonts/") || name.begins_with("custom_fonts/")) {
+ } else if (name.begins_with("theme_override_fonts/")) {
String dname = name.get_slicec('/', 1);
add_theme_font_override(dname, p_value);
- } else if (name.begins_with("theme_override_font_sizes/") || name.begins_with("custom_font_sizes/")) {
+ } else if (name.begins_with("theme_override_font_sizes/")) {
String dname = name.get_slicec('/', 1);
add_theme_font_size_override(dname, p_value);
- } else if (name.begins_with("theme_override_colors/") || name.begins_with("custom_colors/")) {
+ } else if (name.begins_with("theme_override_colors/")) {
String dname = name.get_slicec('/', 1);
add_theme_color_override(dname, p_value);
- } else if (name.begins_with("theme_override_constants/") || name.begins_with("custom_constants/")) {
+ } else if (name.begins_with("theme_override_constants/")) {
String dname = name.get_slicec('/', 1);
add_theme_constant_override(dname, p_value);
} else {
@@ -353,7 +367,7 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const {
void Control::_get_property_list(List<PropertyInfo> *p_list) const {
Ref<Theme> theme = Theme::get_default();
- p_list->push_back(PropertyInfo(Variant::NIL, "Theme Overrides", PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP));
+ p_list->push_back(PropertyInfo(Variant::NIL, TTRC("Theme Overrides"), PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP));
{
List<StringName> names;
@@ -430,6 +444,7 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const {
}
void Control::_validate_property(PropertyInfo &property) const {
+ // Update theme type variation options.
if (property.name == "theme_type_variation") {
List<StringName> names;
@@ -455,10 +470,99 @@ void Control::_validate_property(PropertyInfo &property) const {
property.hint_string = hint_string;
}
- if (!Object::cast_to<Container>(get_parent())) {
- return;
+
+ // Validate which positioning properties should be displayed depending on the parent and the layout mode.
+ Node *parent_node = get_parent_control();
+ if (!parent_node) {
+ // If there is no parent, display both anchor and container options.
+
+ // Set the layout mode to be disabled with the proper value.
+ if (property.name == "layout_mode") {
+ property.hint_string = "Position,Anchors,Container,Uncontrolled";
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+
+ // Use the layout mode to display or hide advanced anchoring properties.
+ bool use_custom_anchors = _get_anchors_layout_preset() == -1; // Custom "preset".
+ if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+ }
+ } else if (Object::cast_to<Container>(parent_node)) {
+ // If the parent is a container, display only container-related properties.
+ if (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_") || property.name == "anchors_preset" ||
+ property.name == "position" || property.name == "rotation" || property.name == "scale" || property.name == "size" || property.name == "pivot_offset") {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+
+ } else if (property.name == "layout_mode") {
+ // Set the layout mode to be disabled with the proper value.
+ property.hint_string = "Position,Anchors,Container,Uncontrolled";
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ } else if (property.name == "size_flags_horizontal" || property.name == "size_flags_vertical") {
+ // Filter allowed size flags based on the parent container configuration.
+ Container *parent_container = Object::cast_to<Container>(parent_node);
+ Vector<int> size_flags;
+ if (property.name == "size_flags_horizontal") {
+ size_flags = parent_container->get_allowed_size_flags_horizontal();
+ } else if (property.name == "size_flags_vertical") {
+ size_flags = parent_container->get_allowed_size_flags_vertical();
+ }
+
+ // Enforce the order of the options, regardless of what the container provided.
+ String hint_string;
+ if (size_flags.has(SIZE_FILL)) {
+ hint_string += "Fill:1";
+ }
+ if (size_flags.has(SIZE_EXPAND)) {
+ if (!hint_string.is_empty()) {
+ hint_string += ",";
+ }
+ hint_string += "Expand:2";
+ }
+ if (size_flags.has(SIZE_SHRINK_CENTER)) {
+ if (!hint_string.is_empty()) {
+ hint_string += ",";
+ }
+ hint_string += "Shrink Center:4";
+ }
+ if (size_flags.has(SIZE_SHRINK_END)) {
+ if (!hint_string.is_empty()) {
+ hint_string += ",";
+ }
+ hint_string += "Shrink End:8";
+ }
+
+ if (hint_string.is_empty()) {
+ property.hint_string = "";
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ } else {
+ property.hint_string = hint_string;
+ }
+ }
+ } else {
+ // If the parent is NOT a container or not a control at all, display only anchoring-related properties.
+ if (property.name.begins_with("size_flags_")) {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+
+ } else if (property.name == "layout_mode") {
+ // Set the layout mode to be enabled with proper options.
+ property.hint_string = "Position,Anchors";
+ }
+
+ // Use the layout mode to display or hide advanced anchoring properties.
+ bool use_anchors = _get_layout_mode() == LayoutMode::LAYOUT_MODE_ANCHORS;
+ if (!use_anchors && property.name == "anchors_preset") {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+ }
+ bool use_custom_anchors = use_anchors && _get_anchors_layout_preset() == -1; // Custom "preset".
+ if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+ }
}
+
// Disable the property if it's managed by the parent container.
+ if (!Object::cast_to<Container>(parent_node)) {
+ return;
+ }
bool property_is_managed_by_container = false;
for (unsigned i = 0; i < properties_managed_by_container_count; i++) {
property_is_managed_by_container = properties_managed_by_container[i] == property.name;
@@ -576,21 +680,27 @@ void Control::_update_canvas_item_transform() {
Transform2D xform = _get_internal_transform();
xform[2] += get_position();
+ // We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
+ if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
+ xform[2] = xform[2].round();
+ }
+
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), xform);
}
void Control::_notification(int p_notification) {
switch (p_notification) {
- case NOTIFICATION_ENTER_TREE: {
- } break;
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);
@@ -602,35 +712,25 @@ void Control::_notification(int p_notification) {
data.parent_window = Object::cast_to<Window>(get_parent());
data.is_rtl_dirty = true;
- Node *parent = this; //meh
+ CanvasItem *node = this;
Control *parent_control = nullptr;
- bool subwindow = false;
-
- while (parent) {
- parent = parent->get_parent();
+ while (!node->is_set_as_top_level()) {
+ CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent());
if (!parent) {
break;
}
- CanvasItem *ci = Object::cast_to<CanvasItem>(parent);
- if (ci && ci->is_set_as_top_level()) {
- subwindow = true;
- break;
- }
-
parent_control = Object::cast_to<Control>(parent);
-
if (parent_control) {
break;
- } else if (ci) {
- } else {
- break;
}
+
+ node = parent;
}
- if (parent_control && !subwindow) {
- //do nothing, has a parent control and not top_level
+ if (parent_control) {
+ // Do nothing, has a parent control.
if (data.theme.is_null() && parent_control->data.theme_owner) {
data.theme_owner = parent_control->data.theme_owner;
notification(NOTIFICATION_THEME_CHANGED);
@@ -653,6 +753,7 @@ void Control::_notification(int p_notification) {
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));
@@ -673,8 +774,8 @@ void Control::_notification(int p_notification) {
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
@@ -686,54 +787,58 @@ void Control::_notification(int p_notification) {
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);
- //emit_signal(SceneStringNames::get_singleton()->draw);
-
} 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: {
- minimum_size_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);
}
-
- //remove key focus
-
} else {
data.minimum_size_valid = false;
_size_changed();
}
-
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- data.is_rtl_dirty = true;
- _size_changed();
+ if (is_inside_tree()) {
+ data.is_rtl_dirty = true;
+ _size_changed();
+ }
} break;
}
}
@@ -810,6 +915,10 @@ void Control::set_drag_preview(Control *p_control) {
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()) {
@@ -1144,12 +1253,12 @@ float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_theme_base_scale()) {
- return theme_owner->data.theme->get_default_theme_base_scale();
+ if (theme_owner && 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_theme_base_scale()) {
- return theme_owner_window->theme->get_default_theme_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();
@@ -1171,13 +1280,16 @@ float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_
// Secondly, check the project-defined Theme resource.
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_theme_base_scale()) {
- return Theme::get_project_default()->get_default_theme_base_scale();
+ 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.
- return Theme::get_default()->get_default_theme_base_scale();
+ if (Theme::get_default()->has_default_base_scale()) {
+ return Theme::get_default()->get_default_base_scale();
+ }
+ return Theme::get_fallback_base_scale();
}
float Control::get_theme_default_base_scale() const {
@@ -1192,12 +1304,12 @@ Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_th
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_theme_font()) {
- return theme_owner->data.theme->get_default_theme_font();
+ if (theme_owner && 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_theme_font()) {
- return theme_owner_window->theme->get_default_theme_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();
@@ -1219,13 +1331,16 @@ Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_th
// Secondly, check the project-defined Theme resource.
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_theme_font()) {
- return Theme::get_project_default()->get_default_theme_font();
+ if (Theme::get_project_default()->has_default_font()) {
+ return Theme::get_project_default()->get_default_font();
}
}
// Lastly, fall back on the default Theme.
- return Theme::get_default()->get_default_theme_font();
+ if (Theme::get_default()->has_default_font()) {
+ return Theme::get_default()->get_default_font();
+ }
+ return Theme::get_fallback_font();
}
Ref<Font> Control::get_theme_default_font() const {
@@ -1240,12 +1355,12 @@ int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_the
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_theme_font_size()) {
- return theme_owner->data.theme->get_default_theme_font_size();
+ if (theme_owner && 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_theme_font_size()) {
- return theme_owner_window->theme->get_default_theme_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();
@@ -1267,13 +1382,16 @@ int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_the
// Secondly, check the project-defined Theme resource.
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_theme_font_size()) {
- return Theme::get_project_default()->get_default_theme_font_size();
+ 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.
- return Theme::get_default()->get_default_theme_font_size();
+ if (Theme::get_default()->has_default_font_size()) {
+ return Theme::get_default()->get_default_font_size();
+ }
+ return Theme::get_fallback_font_size();
}
int Control::get_theme_default_font_size() const {
@@ -1292,7 +1410,7 @@ Rect2 Control::get_parent_anchorable_rect() const {
#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/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ 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();
}
@@ -1369,6 +1487,55 @@ void Control::_size_changed() {
}
}
+void Control::_set_layout_mode(LayoutMode p_mode) {
+ bool list_changed = false;
+
+ if (p_mode == LayoutMode::LAYOUT_MODE_POSITION || p_mode == LayoutMode::LAYOUT_MODE_ANCHORS) {
+ if ((int)get_meta("_edit_layout_mode", p_mode) != (int)p_mode) {
+ list_changed = true;
+ }
+
+ set_meta("_edit_layout_mode", (int)p_mode);
+
+ if (p_mode == LayoutMode::LAYOUT_MODE_POSITION) {
+ remove_meta("_edit_layout_mode");
+ remove_meta("_edit_use_custom_anchors");
+ set_anchors_and_offsets_preset(LayoutPreset::PRESET_TOP_LEFT, LayoutPresetMode::PRESET_MODE_KEEP_SIZE);
+ set_grow_direction_preset(LayoutPreset::PRESET_TOP_LEFT);
+ }
+ } else {
+ if (has_meta("_edit_layout_mode")) {
+ remove_meta("_edit_layout_mode");
+ list_changed = true;
+ }
+ }
+
+ if (list_changed) {
+ notify_property_list_changed();
+ }
+}
+
+Control::LayoutMode Control::_get_layout_mode() const {
+ Node *parent_node = get_parent_control();
+ // In these modes the property is read-only.
+ if (!parent_node) {
+ return LayoutMode::LAYOUT_MODE_UNCONTROLLED;
+ } else if (Object::cast_to<Container>(parent_node)) {
+ return LayoutMode::LAYOUT_MODE_CONTAINER;
+ }
+
+ // If anchors are not in the top-left position, this is definitely in anchors mode.
+ if (_get_anchors_layout_preset() != (int)LayoutPreset::PRESET_TOP_LEFT) {
+ return LayoutMode::LAYOUT_MODE_ANCHORS;
+ }
+ // Otherwise check what was saved.
+ if (has_meta("_edit_layout_mode")) {
+ return (LayoutMode)(int)get_meta("_edit_layout_mode");
+ }
+ // Or fallback on default.
+ 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);
@@ -1410,6 +1577,133 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos,
set_offset(p_side, p_pos);
}
+void Control::_set_anchors_layout_preset(int p_preset) {
+ bool list_changed = false;
+
+ if (get_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS).operator int() != LayoutMode::LAYOUT_MODE_ANCHORS) {
+ list_changed = true;
+ set_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS);
+ }
+
+ if (p_preset == -1) {
+ if (!get_meta("_edit_use_custom_anchors", false)) {
+ set_meta("_edit_use_custom_anchors", true);
+ notify_property_list_changed();
+ }
+ return; // Keep settings as is.
+ }
+
+ if (get_meta("_edit_use_custom_anchors", true)) {
+ list_changed = true;
+ remove_meta("_edit_use_custom_anchors");
+ }
+
+ LayoutPreset preset = (LayoutPreset)p_preset;
+ // Set correct anchors.
+ set_anchors_preset(preset);
+
+ // Select correct preset mode.
+ switch (preset) {
+ case PRESET_TOP_LEFT:
+ case PRESET_TOP_RIGHT:
+ case PRESET_BOTTOM_LEFT:
+ case PRESET_BOTTOM_RIGHT:
+ case PRESET_CENTER_LEFT:
+ case PRESET_CENTER_TOP:
+ case PRESET_CENTER_RIGHT:
+ case PRESET_CENTER_BOTTOM:
+ case PRESET_CENTER:
+ set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_KEEP_SIZE);
+ break;
+ case PRESET_LEFT_WIDE:
+ case PRESET_TOP_WIDE:
+ case PRESET_RIGHT_WIDE:
+ case PRESET_BOTTOM_WIDE:
+ case PRESET_VCENTER_WIDE:
+ case PRESET_HCENTER_WIDE:
+ case PRESET_WIDE:
+ set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_MINSIZE);
+ break;
+ }
+
+ // Select correct grow directions.
+ set_grow_direction_preset(preset);
+
+ if (list_changed) {
+ notify_property_list_changed();
+ }
+}
+
+int Control::_get_anchors_layout_preset() const {
+ // If the custom preset was selected by user, use it.
+ if ((bool)get_meta("_edit_use_custom_anchors", false)) {
+ return -1;
+ }
+
+ // Check anchors to determine if the current state matches a preset, or not.
+
+ float left = get_anchor(SIDE_LEFT);
+ float right = get_anchor(SIDE_RIGHT);
+ float top = get_anchor(SIDE_TOP);
+ float bottom = get_anchor(SIDE_BOTTOM);
+
+ if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
+ return (int)LayoutPreset::PRESET_TOP_LEFT;
+ }
+ if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
+ return (int)LayoutPreset::PRESET_TOP_RIGHT;
+ }
+ if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_END && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_BOTTOM_LEFT;
+ }
+ if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_END && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_BOTTOM_RIGHT;
+ }
+
+ if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == 0.5 && bottom == 0.5) {
+ return (int)LayoutPreset::PRESET_CENTER_LEFT;
+ }
+ if (left == ANCHOR_END && right == ANCHOR_END && top == 0.5 && bottom == 0.5) {
+ return (int)LayoutPreset::PRESET_CENTER_RIGHT;
+ }
+ if (left == 0.5 && right == 0.5 && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
+ return (int)LayoutPreset::PRESET_CENTER_TOP;
+ }
+ if (left == 0.5 && right == 0.5 && top == ANCHOR_END && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_CENTER_BOTTOM;
+ }
+ if (left == 0.5 && right == 0.5 && top == 0.5 && bottom == 0.5) {
+ return (int)LayoutPreset::PRESET_CENTER;
+ }
+
+ if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_LEFT_WIDE;
+ }
+ if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_RIGHT_WIDE;
+ }
+ if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
+ return (int)LayoutPreset::PRESET_TOP_WIDE;
+ }
+ if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_END && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_BOTTOM_WIDE;
+ }
+
+ if (left == 0.5 && right == 0.5 && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_VCENTER_WIDE;
+ }
+ if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == 0.5 && bottom == 0.5) {
+ return (int)LayoutPreset::PRESET_HCENTER_WIDE;
+ }
+
+ if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_WIDE;
+ }
+
+ // Does not match any preset, return "Custom".
+ return -1;
+}
+
void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) {
ERR_FAIL_INDEX((int)p_preset, 16);
@@ -1666,6 +1960,62 @@ void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPreset
set_offsets_preset(p_preset, p_resize_mode, p_margin);
}
+void Control::set_grow_direction_preset(LayoutPreset p_preset) {
+ // Select correct horizontal grow direction.
+ switch (p_preset) {
+ case PRESET_TOP_LEFT:
+ case PRESET_BOTTOM_LEFT:
+ case PRESET_CENTER_LEFT:
+ case PRESET_LEFT_WIDE:
+ set_h_grow_direction(GrowDirection::GROW_DIRECTION_END);
+ break;
+ case PRESET_TOP_RIGHT:
+ case PRESET_BOTTOM_RIGHT:
+ case PRESET_CENTER_RIGHT:
+ case PRESET_RIGHT_WIDE:
+ set_h_grow_direction(GrowDirection::GROW_DIRECTION_BEGIN);
+ break;
+ case PRESET_CENTER_TOP:
+ case PRESET_CENTER_BOTTOM:
+ case PRESET_CENTER:
+ case PRESET_TOP_WIDE:
+ case PRESET_BOTTOM_WIDE:
+ case PRESET_VCENTER_WIDE:
+ case PRESET_HCENTER_WIDE:
+ case PRESET_WIDE:
+ set_h_grow_direction(GrowDirection::GROW_DIRECTION_BOTH);
+ break;
+ }
+
+ // Select correct vertical grow direction.
+ switch (p_preset) {
+ case PRESET_TOP_LEFT:
+ case PRESET_TOP_RIGHT:
+ case PRESET_CENTER_TOP:
+ case PRESET_TOP_WIDE:
+ set_v_grow_direction(GrowDirection::GROW_DIRECTION_END);
+ break;
+
+ case PRESET_BOTTOM_LEFT:
+ case PRESET_BOTTOM_RIGHT:
+ case PRESET_CENTER_BOTTOM:
+ case PRESET_BOTTOM_WIDE:
+ set_v_grow_direction(GrowDirection::GROW_DIRECTION_BEGIN);
+ break;
+
+ case PRESET_CENTER_LEFT:
+ case PRESET_CENTER_RIGHT:
+ case PRESET_CENTER:
+ case PRESET_LEFT_WIDE:
+ case PRESET_RIGHT_WIDE:
+ case PRESET_VCENTER_WIDE:
+ case PRESET_HCENTER_WIDE:
+ case PRESET_WIDE:
+ set_v_grow_direction(GrowDirection::GROW_DIRECTION_BOTH);
+ break;
+ }
+}
+
real_t Control::get_anchor(Side p_side) const {
ERR_FAIL_INDEX_V(int(p_side), 4, 0.0);
@@ -1711,7 +2061,7 @@ Point2 Control::get_global_position() const {
Point2 Control::get_screen_position() const {
ERR_FAIL_COND_V(!is_inside_tree(), Point2());
- Point2 global_pos = get_viewport()->get_canvas_transform().xform(get_global_position());
+ Point2 global_pos = get_global_transform_with_canvas().get_origin();
Window *w = Object::cast_to<Window>(get_viewport());
if (w && !w->is_embedding_subwindows()) {
global_pos += w->get_position();
@@ -1821,6 +2171,10 @@ Size2 Control::get_size() const {
return data.size_cache;
}
+void Control::reset_size() {
+ set_size(Size2());
+}
+
Rect2 Control::get_global_rect() const {
return Rect2(get_global_position(), get_size());
}
@@ -2171,8 +2525,7 @@ void Control::release_focus() {
return;
}
- get_viewport()->_gui_remove_focus();
- update();
+ get_viewport()->gui_release_focus();
}
bool Control::is_top_level_control() const {
@@ -2526,7 +2879,7 @@ void Control::grab_click_focus() {
get_viewport()->_gui_grab_click_focus(this);
}
-void Control::minimum_size_changed() {
+void Control::update_minimum_size() {
if (!is_inside_tree() || data.block_minimum_size_adjust) {
return;
}
@@ -2575,104 +2928,26 @@ Control::MouseFilter Control::get_mouse_filter() const {
return data.mouse_filter;
}
-Control *Control::get_focus_owner() const {
- ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
- return get_viewport()->_gui_get_focus_owner();
-}
-
-void Control::warp_mouse(const Point2 &p_to_pos) {
+void Control::warp_mouse(const Point2 &p_position) {
ERR_FAIL_COND(!is_inside_tree());
- get_viewport()->warp_mouse(get_global_transform().xform(p_to_pos));
+ get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position));
}
bool Control::is_text_field() const {
return false;
}
-Array Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const {
- Array ret;
- switch (p_theme_type) {
- case STRUCTURED_TEXT_URI: {
- int prev = 0;
- for (int i = 0; i < p_text.length(); i++) {
- if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == '.') || (p_text[i] == ':') || (p_text[i] == '&') || (p_text[i] == '=') || (p_text[i] == '@') || (p_text[i] == '?') || (p_text[i] == '#')) {
- if (prev != i) {
- ret.push_back(Vector2i(prev, i));
- }
- ret.push_back(Vector2i(i, i + 1));
- prev = i + 1;
- }
- }
- if (prev != p_text.length()) {
- ret.push_back(Vector2i(prev, p_text.length()));
- }
- } break;
- case STRUCTURED_TEXT_FILE: {
- int prev = 0;
- for (int i = 0; i < p_text.length(); i++) {
- if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == ':')) {
- if (prev != i) {
- ret.push_back(Vector2i(prev, i));
- }
- ret.push_back(Vector2i(i, i + 1));
- prev = i + 1;
- }
- }
- if (prev != p_text.length()) {
- ret.push_back(Vector2i(prev, p_text.length()));
- }
- } break;
- case STRUCTURED_TEXT_EMAIL: {
- bool local = true;
- int prev = 0;
- for (int i = 0; i < p_text.length(); i++) {
- if ((p_text[i] == '@') && local) { // Add full "local" as single context.
- local = false;
- ret.push_back(Vector2i(prev, i));
- ret.push_back(Vector2i(i, i + 1));
- prev = i + 1;
- } else if (!local & (p_text[i] == '.')) { // Add each dot separated "domain" part as context.
- if (prev != i) {
- ret.push_back(Vector2i(prev, i));
- }
- ret.push_back(Vector2i(i, i + 1));
- prev = i + 1;
- }
- }
- if (prev != p_text.length()) {
- ret.push_back(Vector2i(prev, p_text.length()));
- }
- } break;
- case STRUCTURED_TEXT_LIST: {
- if (p_args.size() == 1 && p_args[0].get_type() == Variant::STRING) {
- Vector<String> tags = p_text.split(String(p_args[0]));
- int prev = 0;
- for (int i = 0; i < tags.size(); i++) {
- if (prev != i) {
- ret.push_back(Vector2i(prev, prev + tags[i].length()));
- }
- ret.push_back(Vector2i(prev + tags[i].length(), prev + tags[i].length() + 1));
- prev = prev + tags[i].length() + 1;
- }
- }
- } break;
- case STRUCTURED_TEXT_CUSTOM: {
- Array r;
- if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, r)) {
- for (int i = 0; i < r.size(); i++) {
- if (r[i].get_type() == Variant::VECTOR2I) {
- ret.push_back(Vector2i(r[i]));
- }
- }
- }
- } break;
- case STRUCTURED_TEXT_NONE:
- case STRUCTURED_TEXT_DEFAULT:
- default: {
- ret.push_back(Vector2i(0, p_text.length()));
+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 ret;
}
void Control::set_rotation(real_t p_radians) {
@@ -2688,7 +2963,7 @@ real_t Control::get_rotation() const {
void Control::_override_changed() {
notification(NOTIFICATION_THEME_CHANGED);
emit_signal(SceneStringNames::get_singleton()->theme_changed);
- minimum_size_changed(); // overrides are likely to affect minimum size
+ update_minimum_size(); // Overrides are likely to affect minimum size.
}
void Control::set_pivot_offset(const Vector2 &p_pivot) {
@@ -2783,8 +3058,8 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
TypedArray<String> Control::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
- if (data.mouse_filter == MOUSE_FILTER_IGNORE && data.tooltip != "") {
- warnings.push_back(TTR("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\"."));
+ 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;
@@ -2828,19 +3103,28 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event);
ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size);
ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size);
+
+ ClassDB::bind_method(D_METHOD("_set_layout_mode", "mode"), &Control::_set_layout_mode);
+ ClassDB::bind_method(D_METHOD("_get_layout_mode"), &Control::_get_layout_mode);
+ ClassDB::bind_method(D_METHOD("_set_anchors_layout_preset", "preset"), &Control::_set_anchors_layout_preset);
+ ClassDB::bind_method(D_METHOD("_get_anchors_layout_preset"), &Control::_get_anchors_layout_preset);
ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_offsets"), &Control::set_anchors_preset, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_anchors_and_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
+
ClassDB::bind_method(D_METHOD("_set_anchor", "side", "anchor"), &Control::_set_anchor);
ClassDB::bind_method(D_METHOD("set_anchor", "side", "anchor", "keep_offset", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_anchor", "side"), &Control::get_anchor);
ClassDB::bind_method(D_METHOD("set_offset", "side", "offset"), &Control::set_offset);
+ ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset);
ClassDB::bind_method(D_METHOD("set_anchor_and_offset", "side", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_offset, DEFVAL(false));
+
ClassDB::bind_method(D_METHOD("set_begin", "position"), &Control::set_begin);
ClassDB::bind_method(D_METHOD("set_end", "position"), &Control::set_end);
ClassDB::bind_method(D_METHOD("set_position", "position", "keep_offsets"), &Control::set_position, DEFVAL(false));
ClassDB::bind_method(D_METHOD("_set_position", "position"), &Control::_set_position);
ClassDB::bind_method(D_METHOD("set_size", "size", "keep_offsets"), &Control::set_size, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("reset_size"), &Control::reset_size);
ClassDB::bind_method(D_METHOD("_set_size", "size"), &Control::_set_size);
ClassDB::bind_method(D_METHOD("set_custom_minimum_size", "size"), &Control::set_custom_minimum_size);
ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_offsets"), &Control::set_global_position, DEFVAL(false));
@@ -2848,7 +3132,6 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale);
ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset);
- ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset);
ClassDB::bind_method(D_METHOD("get_begin"), &Control::get_begin);
ClassDB::bind_method(D_METHOD("get_end"), &Control::get_end);
ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position);
@@ -2859,6 +3142,7 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_custom_minimum_size"), &Control::get_custom_minimum_size);
ClassDB::bind_method(D_METHOD("get_parent_area_size"), &Control::get_parent_area_size);
ClassDB::bind_method(D_METHOD("get_global_position"), &Control::get_global_position);
+ ClassDB::bind_method(D_METHOD("get_screen_position"), &Control::get_screen_position);
ClassDB::bind_method(D_METHOD("get_rect"), &Control::get_rect);
ClassDB::bind_method(D_METHOD("get_global_rect"), &Control::get_global_rect);
ClassDB::bind_method(D_METHOD("set_focus_mode", "mode"), &Control::set_focus_mode);
@@ -2868,7 +3152,6 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("release_focus"), &Control::release_focus);
ClassDB::bind_method(D_METHOD("find_prev_valid_focus"), &Control::find_prev_valid_focus);
ClassDB::bind_method(D_METHOD("find_next_valid_focus"), &Control::find_next_valid_focus);
- ClassDB::bind_method(D_METHOD("get_focus_owner"), &Control::get_focus_owner);
ClassDB::bind_method(D_METHOD("set_h_size_flags", "flags"), &Control::set_h_size_flags);
ClassDB::bind_method(D_METHOD("get_h_size_flags"), &Control::get_h_size_flags);
@@ -2964,10 +3247,11 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_drag_forwarding", "target"), &Control::set_drag_forwarding);
ClassDB::bind_method(D_METHOD("set_drag_preview", "control"), &Control::set_drag_preview);
+ ClassDB::bind_method(D_METHOD("is_drag_successful"), &Control::is_drag_successful);
- ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Control::warp_mouse);
+ ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Control::warp_mouse);
- ClassDB::bind_method(D_METHOD("minimum_size_changed"), &Control::minimum_size_changed);
+ ClassDB::bind_method(D_METHOD("update_minimum_size"), &Control::update_minimum_size);
ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Control::set_layout_direction);
ClassDB::bind_method(D_METHOD("get_layout_direction"), &Control::get_layout_direction);
@@ -2976,38 +3260,53 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate);
ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating);
- ADD_GROUP("Anchor", "anchor_");
+ ADD_GROUP("Layout", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimum_size"), "set_custom_minimum_size", "get_custom_minimum_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
+ ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
+
+ const String anchors_presets_options = "Custom:-1,PresetWide:15,"
+ "PresetTopLeft:0,PresetTopRight:1,PresetBottomRight:3,PresetBottomLeft:2,"
+ "PresetCenterLeft:4,PresetCenterTop:5,PresetCenterRight:6,PresetCenterBottom:7,PresetCenter:8,"
+ "PresetLeftWide:9,PresetTopWide:10,PresetRightWide:11,PresetBottomWide:12,PresetVCenterWide:13,PresetHCenterWide:14";
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "anchors_preset", PROPERTY_HINT_ENUM, anchors_presets_options, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_anchors_layout_preset", "_get_anchors_layout_preset");
+ ADD_PROPERTY_DEFAULT("anchors_preset", -1);
+
+ ADD_SUBGROUP_INDENT("Anchor Points", "anchor_", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_BOTTOM);
- ADD_GROUP("Offset", "offset_");
+ ADD_SUBGROUP_INDENT("Anchor Offsets", "offset_", 1);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_LEFT);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_top", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_right", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_BOTTOM);
- ADD_GROUP("Grow Direction", "grow_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
-
- ADD_GROUP("Layout Direction", "layout_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
+ ADD_SUBGROUP_INDENT("Grow Direction", "grow_", 1);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Left,Right,Both"), "set_h_grow_direction", "get_h_grow_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Top,Bottom,Both"), "set_v_grow_direction", "get_v_grow_direction");
+
+ ADD_SUBGROUP("Transform", "");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset"), "set_pivot_offset", "get_pivot_offset");
+
+ ADD_SUBGROUP("Container Sizing", "size_flags_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_h_size_flags", "get_h_size_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_v_size_flags", "get_v_size_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio");
ADD_GROUP("Auto Translate", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
- ADD_GROUP("Rect", "rect_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents");
-
ADD_GROUP("Hint", "hint_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
@@ -3024,11 +3323,6 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape");
- ADD_GROUP("Size Flags", "size_flags_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio");
-
ADD_GROUP("Theme", "theme_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation");
@@ -3087,6 +3381,7 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_HEIGHT);
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_SIZE);
+ BIND_ENUM_CONSTANT(SIZE_SHRINK_BEGIN);
BIND_ENUM_CONSTANT(SIZE_FILL);
BIND_ENUM_CONSTANT(SIZE_EXPAND);
BIND_ENUM_CONSTANT(SIZE_EXPAND_FILL);
@@ -3114,14 +3409,6 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(TEXT_DIRECTION_LTR);
BIND_ENUM_CONSTANT(TEXT_DIRECTION_RTL);
- BIND_ENUM_CONSTANT(STRUCTURED_TEXT_DEFAULT);
- BIND_ENUM_CONSTANT(STRUCTURED_TEXT_URI);
- BIND_ENUM_CONSTANT(STRUCTURED_TEXT_FILE);
- BIND_ENUM_CONSTANT(STRUCTURED_TEXT_EMAIL);
- BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST);
- BIND_ENUM_CONSTANT(STRUCTURED_TEXT_NONE);
- BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM);
-
ADD_SIGNAL(MethodInfo("resized"));
ADD_SIGNAL(MethodInfo("gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
ADD_SIGNAL(MethodInfo("mouse_entered"));
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 02ab336ef0..65b71d74f8 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,12 +31,10 @@
#ifndef CONTROL_H
#define CONTROL_H
-#include "core/input/shortcut.h"
#include "core/math/transform_2d.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/templates/rid.h"
#include "scene/main/canvas_item.h"
-#include "scene/main/node.h"
#include "scene/main/timer.h"
#include "scene/resources/theme.h"
@@ -46,7 +44,6 @@ class Panel;
class Control : public CanvasItem {
GDCLASS(Control, CanvasItem);
- OBJ_CATEGORY("GUI Nodes");
public:
enum Anchor {
@@ -67,12 +64,13 @@ public:
};
enum SizeFlags {
+ SIZE_SHRINK_BEGIN = 0,
SIZE_FILL = 1,
SIZE_EXPAND = 2,
- SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL,
- SIZE_SHRINK_CENTER = 4, //ignored by expand or fill
- SIZE_SHRINK_END = 8, //ignored by expand or fill
+ SIZE_SHRINK_CENTER = 4,
+ SIZE_SHRINK_END = 8,
+ SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL,
};
enum MouseFilter {
@@ -128,6 +126,13 @@ public:
PRESET_MODE_KEEP_SIZE
};
+ enum LayoutMode {
+ LAYOUT_MODE_POSITION,
+ LAYOUT_MODE_ANCHORS,
+ LAYOUT_MODE_CONTAINER,
+ LAYOUT_MODE_UNCONTROLLED,
+ };
+
enum LayoutDirection {
LAYOUT_DIRECTION_INHERITED,
LAYOUT_DIRECTION_LOCALE,
@@ -142,16 +147,6 @@ public:
TEXT_DIRECTION_INHERITED,
};
- enum StructuredTextParser {
- STRUCTURED_TEXT_DEFAULT,
- STRUCTURED_TEXT_URI,
- STRUCTURED_TEXT_FILE,
- STRUCTURED_TEXT_EMAIL,
- STRUCTURED_TEXT_LIST,
- STRUCTURED_TEXT_NONE,
- STRUCTURED_TEXT_CUSTOM
- };
-
private:
struct CComparator {
bool operator()(const Control *p_a, const Control *p_b) const {
@@ -230,7 +225,7 @@ private:
} data;
- static constexpr unsigned properties_managed_by_container_count = 11;
+ 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);
@@ -241,6 +236,12 @@ private:
void _set_global_position(const Point2 &p_point);
void _set_size(const Size2 &p_size);
+ 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();
@@ -279,15 +280,16 @@ protected:
//virtual void _window_gui_input(InputEvent p_event);
- virtual Array structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const;
+ virtual Array structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
+
void _notification(int p_notification);
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const override;
//bind helpers
@@ -357,6 +359,7 @@ public:
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;
void set_custom_minimum_size(const Size2 &p_custom);
Size2 get_custom_minimum_size() const;
@@ -377,6 +380,7 @@ public:
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_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;
@@ -400,6 +404,7 @@ public:
void set_size(const Size2 &p_size, bool p_keep_offsets = false);
Size2 get_size() const;
+ void reset_size();
Rect2 get_rect() const;
Rect2 get_global_rect() const;
@@ -439,7 +444,7 @@ public:
void set_stretch_ratio(real_t p_ratio);
real_t get_stretch_ratio() const;
- void minimum_size_changed();
+ void update_minimum_size();
/* FOCUS */
@@ -460,8 +465,6 @@ public:
void set_focus_previous(const NodePath &p_prev);
NodePath get_focus_previous() const;
- Control *get_focus_owner() const;
-
void set_mouse_filter(MouseFilter p_filter);
MouseFilter get_mouse_filter() const;
@@ -534,7 +537,7 @@ public:
void grab_click_focus();
- void warp_mouse(const Point2 &p_to_pos);
+ void warp_mouse(const Point2 &p_position);
virtual bool is_text_field() const;
@@ -563,8 +566,8 @@ VARIANT_ENUM_CAST(Control::LayoutPresetMode);
VARIANT_ENUM_CAST(Control::MouseFilter);
VARIANT_ENUM_CAST(Control::GrowDirection);
VARIANT_ENUM_CAST(Control::Anchor);
+VARIANT_ENUM_CAST(Control::LayoutMode);
VARIANT_ENUM_CAST(Control::LayoutDirection);
VARIANT_ENUM_CAST(Control::TextDirection);
-VARIANT_ENUM_CAST(Control::StructuredTextParser);
#endif
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index f66d3f6835..0bb96a18a5 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,24 +33,19 @@
#include "core/os/keyboard.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
-#include "line_edit.h"
-
-#ifdef TOOLS_ENABLED
-#include "editor/editor_node.h"
-#include "scene/main/window.h" // Only used to check for more modals when dimming the editor.
-#endif
+#include "scene/gui/line_edit.h"
// AcceptDialog
void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
+ if (close_on_escape && key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
_cancel_pressed();
}
}
void AcceptDialog::_parent_focused() {
- if (!is_exclusive()) {
+ if (close_on_escape && !is_exclusive()) {
_cancel_pressed();
}
}
@@ -72,21 +67,25 @@ void AcceptDialog::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_THEME_CHANGED: {
bg->add_theme_style_override("panel", bg->get_theme_stylebox(SNAME("panel"), SNAME("AcceptDialog")));
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (parent_visible) {
parent_visible->disconnect("focus_entered", callable_mp(this, &AcceptDialog::_parent_focused));
parent_visible = nullptr;
}
} break;
+
case NOTIFICATION_READY:
case NOTIFICATION_WM_SIZE_CHANGED: {
if (is_visible()) {
_update_child_rects();
}
} break;
+
case NOTIFICATION_WM_CLOSE_REQUEST: {
_cancel_pressed();
} break;
@@ -94,6 +93,9 @@ void AcceptDialog::_notification(int p_what) {
}
void AcceptDialog::_text_submitted(const String &p_text) {
+ if (get_ok_button() && get_ok_button()->is_disabled()) {
+ return; // Do not allow submission if OK button is disabled.
+ }
_ok_pressed();
}
@@ -143,6 +145,14 @@ bool AcceptDialog::get_hide_on_ok() const {
return hide_on_ok;
}
+void AcceptDialog::set_close_on_escape(bool p_hide) {
+ close_on_escape = p_hide;
+}
+
+bool AcceptDialog::get_close_on_escape() const {
+ return close_on_escape;
+}
+
void AcceptDialog::set_autowrap(bool p_autowrap) {
label->set_autowrap_mode(p_autowrap ? Label::AUTOWRAP_WORD : Label::AUTOWRAP_OFF);
}
@@ -242,7 +252,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin
hbc->add_spacer(true);
}
- if (p_action != "") {
+ if (!p_action.is_empty()) {
button->connect("pressed", callable_mp(this, &AcceptDialog::_custom_action), varray(p_action));
}
@@ -251,7 +261,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin
Button *AcceptDialog::add_cancel_button(const String &p_cancel) {
String c = p_cancel;
- if (p_cancel == "") {
+ if (p_cancel.is_empty()) {
c = TTRC("Cancel");
}
Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c);
@@ -286,6 +296,8 @@ void AcceptDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_label"), &AcceptDialog::get_label);
ClassDB::bind_method(D_METHOD("set_hide_on_ok", "enabled"), &AcceptDialog::set_hide_on_ok);
ClassDB::bind_method(D_METHOD("get_hide_on_ok"), &AcceptDialog::get_hide_on_ok);
+ ClassDB::bind_method(D_METHOD("set_close_on_escape", "enabled"), &AcceptDialog::set_close_on_escape);
+ ClassDB::bind_method(D_METHOD("get_close_on_escape"), &AcceptDialog::get_close_on_escape);
ClassDB::bind_method(D_METHOD("add_button", "text", "right", "action"), &AcceptDialog::add_button, DEFVAL(false), DEFVAL(""));
ClassDB::bind_method(D_METHOD("add_cancel_button", "name"), &AcceptDialog::add_cancel_button);
ClassDB::bind_method(D_METHOD("remove_button", "button"), &AcceptDialog::remove_button);
@@ -302,6 +314,7 @@ void AcceptDialog::_bind_methods() {
ADD_GROUP("Dialog", "dialog");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_close_on_escape"), "set_close_on_escape", "get_close_on_escape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap"), "set_autowrap", "has_autowrap");
}
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index 8e803a2a7c..41fd9c0a10 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,11 +45,12 @@ class AcceptDialog : public Window {
GDCLASS(AcceptDialog, Window);
Window *parent_visible = nullptr;
- Panel *bg;
- HBoxContainer *hbc;
- Label *label;
- Button *ok;
+ Panel *bg = nullptr;
+ HBoxContainer *hbc = nullptr;
+ Label *label = nullptr;
+ Button *ok = nullptr;
bool hide_on_ok = true;
+ bool close_on_escape = true;
void _custom_action(const String &p_action);
void _update_child_rects();
@@ -87,6 +88,9 @@ public:
void set_hide_on_ok(bool p_hide);
bool get_hide_on_ok() const;
+ void set_close_on_escape(bool p_enable);
+ bool get_close_on_escape() const;
+
void set_text(String p_text);
String get_text() const;
@@ -99,7 +103,7 @@ public:
class ConfirmationDialog : public AcceptDialog {
GDCLASS(ConfirmationDialog, AcceptDialog);
- Button *cancel;
+ Button *cancel = nullptr;
protected:
static void _bind_methods();
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 44853fc006..1725816c31 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,6 +42,17 @@ FileDialog::RegisterFunc FileDialog::unregister_func = nullptr;
void FileDialog::popup_file_dialog() {
popup_centered_clamped(Size2i(700, 500), 0.8f);
+ _focus_file_text();
+}
+
+void FileDialog::_focus_file_text() {
+ int lp = file->get_text().rfind(".");
+ if (lp != -1) {
+ file->select(0, lp);
+ if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file)) {
+ file->grab_focus();
+ }
+ }
}
VBoxContainer *FileDialog::get_vbox() {
@@ -81,27 +92,34 @@ void FileDialog::_theme_changed() {
}
void FileDialog::_notification(int p_what) {
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- if (!is_visible()) {
- set_process_unhandled_input(false);
- }
- }
- if (p_what == NOTIFICATION_ENTER_TREE) {
- dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog")));
- if (vbox->is_layout_rtl()) {
- dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog")));
- dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog")));
- } else {
- dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog")));
- dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog")));
- }
- refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog")));
- show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog")));
- _theme_changed();
+ switch (p_what) {
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible()) {
+ set_process_shortcut_input(false);
+ }
+ } break;
+
+ case NOTIFICATION_ENTER_TREE: {
+ dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog")));
+ if (vbox->is_layout_rtl()) {
+ dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog")));
+ dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog")));
+ } else {
+ dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog")));
+ dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog")));
+ }
+ refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog")));
+ show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog")));
+ _theme_changed();
+ } break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ update_filters();
+ } break;
}
}
-void FileDialog::unhandled_input(const Ref<InputEvent> &p_event) {
+void FileDialog::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventKey> k = p_event;
@@ -155,7 +173,14 @@ void FileDialog::update_dir() {
dir->set_text(dir_access->get_current_dir(false));
if (drives->is_visible()) {
- drives->select(dir_access->get_current_drive());
+ if (dir_access->get_current_dir().is_network_share_path()) {
+ _update_drives(false);
+ drives->add_item(RTR("Network"));
+ drives->set_item_disabled(-1, true);
+ drives->select(drives->get_item_count() - 1);
+ } else {
+ drives->select(dir_access->get_current_drive());
+ }
}
// Deselect any item, to make "Select Current Folder" button text by default.
@@ -192,7 +217,7 @@ void FileDialog::_post_popup() {
tree->grab_focus();
}
- set_process_unhandled_input(true);
+ set_process_shortcut_input(true);
// For open dir mode, deselect all items on file dialog open.
if (mode == FILE_MODE_OPEN_DIR) {
@@ -237,7 +262,8 @@ void FileDialog::_action_pressed() {
return;
}
- String f = dir_access->get_current_dir().plus_file(file->get_text());
+ String file_text = file->get_text();
+ String f = file_text.is_absolute_path() ? file_text : dir_access->get_current_dir().plus_file(file_text);
if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
emit_signal(SNAME("file_selected"), f);
@@ -473,6 +499,13 @@ void FileDialog::update_file_list() {
dir_access->list_dir_begin();
+ if (dir_access->is_readable(dir_access->get_current_dir().utf8().get_data())) {
+ message->hide();
+ } else {
+ message->set_text(TTRC("You don't have permission to access contents of this folder."));
+ message->show();
+ }
+
TreeItem *root = tree->create_item();
Ref<Texture2D> folder = vbox->get_theme_icon(SNAME("folder"), SNAME("FileDialog"));
Ref<Texture2D> file_icon = vbox->get_theme_icon(SNAME("file"), SNAME("FileDialog"));
@@ -482,17 +515,11 @@ void FileDialog::update_file_list() {
List<String> dirs;
bool is_hidden;
- String item;
-
- if (dir_access->is_readable(dir_access->get_current_dir().utf8().get_data())) {
- message->hide();
- } else {
- message->set_text(TTRC("You don't have permission to access contents of this folder."));
- message->show();
- }
+ String item = dir_access->get_next();
- while ((item = dir_access->get_next()) != "") {
+ while (!item.is_empty()) {
if (item == "." || item == "..") {
+ item = dir_access->get_next();
continue;
}
@@ -505,6 +532,7 @@ void FileDialog::update_file_list() {
dirs.push_back(item);
}
}
+ item = dir_access->get_next();
}
dirs.sort_custom<NaturalNoCaseComparator>();
@@ -625,7 +653,7 @@ void FileDialog::update_filters() {
all_filters += ", ...";
}
- filter->add_item(String(TTRC("All Recognized")) + " (" + all_filters + ")");
+ filter->add_item(RTR("All Recognized") + " (" + all_filters + ")");
}
for (int i = 0; i < filters.size(); i++) {
String flt = filters[i].get_slice(";", 0).strip_edges();
@@ -637,7 +665,7 @@ void FileDialog::update_filters() {
}
}
- filter->add_item(TTRC("All Files (*)"));
+ filter->add_item(RTR("All Files") + " (*)");
}
void FileDialog::clear_filters() {
@@ -647,6 +675,7 @@ void FileDialog::clear_filters() {
}
void FileDialog::add_filter(const String &p_filter) {
+ ERR_FAIL_COND_MSG(p_filter.begins_with("."), "Filter must be \"filename.extension\", can't start with dot.");
filters.push_back(p_filter);
update_filters();
invalidate();
@@ -685,13 +714,7 @@ void FileDialog::set_current_file(const String &p_file) {
file->set_text(p_file);
update_dir();
invalidate();
- int lp = p_file.rfind(".");
- if (lp != -1) {
- file->select(0, lp);
- if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file)) {
- file->grab_focus();
- }
- }
+ _focus_file_text();
}
void FileDialog::set_current_path(const String &p_path) {
@@ -775,7 +798,6 @@ void FileDialog::set_access(Access p_access) {
if (access == p_access) {
return;
}
- memdelete(dir_access);
switch (p_access) {
case ACCESS_FILESYSTEM: {
dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
@@ -835,7 +857,7 @@ void FileDialog::_select_drive(int p_idx) {
_push_history();
}
-void FileDialog::_update_drives() {
+void FileDialog::_update_drives(bool p_select) {
int dc = dir_access->get_drive_count();
if (dc == 0 || access != ACCESS_FILESYSTEM) {
drives->hide();
@@ -853,7 +875,9 @@ void FileDialog::_update_drives() {
drives->add_item(dir_access->get_drive(i));
}
- drives->select(dir_access->get_current_drive());
+ if (p_select) {
+ drives->select(dir_access->get_current_drive());
+ }
}
}
@@ -891,12 +915,12 @@ void FileDialog::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_overrides_title"), "set_mode_overrides_title", "is_mode_overriding_title");
ADD_PROPERTY(PropertyInfo(Variant::INT, "file_mode", PROPERTY_HINT_ENUM, "Open File,Open Files,Open Folder,Open Any,Save"), "set_file_mode", "get_file_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User Data,File System"), "set_access", "get_access");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir"), "set_current_dir", "get_current_dir");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file"), "set_current_file", "get_current_file");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_path"), "set_current_path", "get_current_path");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir", PROPERTY_HINT_DIR, "", PROPERTY_USAGE_NONE), "set_current_dir", "get_current_dir");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file", PROPERTY_HINT_FILE, "*", PROPERTY_USAGE_NONE), "set_current_file", "get_current_file");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_path", "get_current_path");
ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path")));
ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths")));
@@ -964,7 +988,7 @@ FileDialog::FileDialog() {
hbc->add_child(drives);
dir = memnew(LineEdit);
- dir->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
+ dir->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
hbc->add_child(dir);
dir->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -998,14 +1022,14 @@ FileDialog::FileDialog() {
message = memnew(Label);
message->hide();
message->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- message->set_align(Label::ALIGN_CENTER);
- message->set_valign(Label::VALIGN_CENTER);
+ message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
tree->add_child(message);
file_box = memnew(HBoxContainer);
file_box->add_child(memnew(Label(TTRC("File:"))));
file = memnew(LineEdit);
- file->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
+ file->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
file->set_stretch_ratio(4);
file->set_h_size_flags(Control::SIZE_EXPAND_FILL);
file_box->add_child(file);
@@ -1039,7 +1063,7 @@ FileDialog::FileDialog() {
makedialog->add_child(makevb);
makedirname = memnew(LineEdit);
- makedirname->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
+ makedirname->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
makevb->add_margin_child(TTRC("Name:"), makedirname);
add_child(makedialog, false, INTERNAL_MODE_FRONT);
makedialog->register_text_enter(makedirname);
@@ -1066,5 +1090,4 @@ FileDialog::~FileDialog() {
if (unregister_func) {
unregister_func(this);
}
- memdelete(dir_access);
}
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index b5190bdab1..2e326d2949 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -65,35 +65,34 @@ public:
static RegisterFunc unregister_func;
private:
- ConfirmationDialog *makedialog;
- LineEdit *makedirname;
+ ConfirmationDialog *makedialog = nullptr;
+ LineEdit *makedirname = nullptr;
- Button *makedir;
+ Button *makedir = nullptr;
Access access = ACCESS_RESOURCES;
- //Button *action;
- VBoxContainer *vbox;
+ VBoxContainer *vbox = nullptr;
FileMode mode;
- LineEdit *dir;
- HBoxContainer *drives_container;
- HBoxContainer *shortcuts_container;
- OptionButton *drives;
- Tree *tree;
- HBoxContainer *file_box;
- LineEdit *file;
- OptionButton *filter;
- AcceptDialog *mkdirerr;
- AcceptDialog *exterr;
- DirAccess *dir_access;
- ConfirmationDialog *confirm_save;
-
- Label *message;
-
- Button *dir_prev;
- Button *dir_next;
- Button *dir_up;
-
- Button *refresh;
- Button *show_hidden;
+ LineEdit *dir = nullptr;
+ HBoxContainer *drives_container = nullptr;
+ HBoxContainer *shortcuts_container = nullptr;
+ OptionButton *drives = nullptr;
+ Tree *tree = nullptr;
+ HBoxContainer *file_box = nullptr;
+ LineEdit *file = nullptr;
+ OptionButton *filter = nullptr;
+ AcceptDialog *mkdirerr = nullptr;
+ AcceptDialog *exterr = nullptr;
+ Ref<DirAccess> dir_access;
+ ConfirmationDialog *confirm_save = nullptr;
+
+ Label *message = nullptr;
+
+ Button *dir_prev = nullptr;
+ Button *dir_next = nullptr;
+ Button *dir_up = nullptr;
+
+ Button *refresh = nullptr;
+ Button *show_hidden = nullptr;
Vector<String> filters;
@@ -113,6 +112,8 @@ private:
void update_file_list();
void update_filters();
+ void _focus_file_text();
+
void _tree_multi_selected(Object *p_object, int p_cell, bool p_selected);
void _tree_selected();
@@ -130,9 +131,9 @@ private:
void _go_back();
void _go_forward();
- void _update_drives();
+ void _update_drives(bool p_select = true);
- virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
+ virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
bool _is_open_should_be_disabled();
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
new file mode 100644
index 0000000000..30b694da76
--- /dev/null
+++ b/scene/gui/flow_container.cpp
@@ -0,0 +1,281 @@
+/*************************************************************************/
+/* flow_container.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 "flow_container.h"
+
+struct _LineData {
+ int child_count = 0;
+ int min_line_height = 0;
+ int min_line_length = 0;
+ int stretch_avail = 0;
+ float stretch_ratio_total = 0;
+};
+
+void FlowContainer::_resort() {
+ // Avoid resorting if invisible.
+ if (!is_visible_in_tree()) {
+ return;
+ }
+
+ int separation_horizontal = get_theme_constant(SNAME("h_separation"));
+ int separation_vertical = get_theme_constant(SNAME("v_separation"));
+
+ bool rtl = is_layout_rtl();
+
+ HashMap<Control *, Size2i> children_minsize_cache;
+
+ Vector<_LineData> lines_data;
+
+ Vector2i ofs;
+ int line_height = 0;
+ int line_length = 0;
+ float line_stretch_ratio_total = 0;
+ int current_container_size = vertical ? get_rect().size.y : get_rect().size.x;
+ int children_in_current_line = 0;
+
+ // First pass for line wrapping and minimum size calculation.
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *child = Object::cast_to<Control>(get_child(i));
+ if (!child || !child->is_visible()) {
+ continue;
+ }
+ if (child->is_set_as_top_level()) {
+ continue;
+ }
+
+ Size2i child_msc = child->get_combined_minimum_size();
+
+ if (vertical) { /* VERTICAL */
+ if (children_in_current_line > 0) {
+ ofs.y += separation_vertical;
+ }
+ if (ofs.y + child_msc.y > current_container_size) {
+ line_length = ofs.y - separation_vertical;
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Move in new column (vertical line).
+ ofs.x += line_height + separation_horizontal;
+ ofs.y = 0;
+ line_height = 0;
+ line_stretch_ratio_total = 0;
+ children_in_current_line = 0;
+ }
+
+ line_height = MAX(line_height, child_msc.x);
+ if (child->get_v_size_flags() & SIZE_EXPAND) {
+ line_stretch_ratio_total += child->get_stretch_ratio();
+ }
+ ofs.y += child_msc.y;
+
+ } else { /* HORIZONTAL */
+ if (children_in_current_line > 0) {
+ ofs.x += separation_horizontal;
+ }
+ if (ofs.x + child_msc.x > current_container_size) {
+ line_length = ofs.x - separation_horizontal;
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Move in new line.
+ ofs.y += line_height + separation_vertical;
+ ofs.x = 0;
+ line_height = 0;
+ line_stretch_ratio_total = 0;
+ children_in_current_line = 0;
+ }
+
+ line_height = MAX(line_height, child_msc.y);
+ if (child->get_h_size_flags() & SIZE_EXPAND) {
+ line_stretch_ratio_total += child->get_stretch_ratio();
+ }
+ ofs.x += child_msc.x;
+ }
+
+ children_minsize_cache[child] = child_msc;
+ children_in_current_line++;
+ }
+ line_length = vertical ? (ofs.y) : (ofs.x);
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Second pass for in-line expansion and alignment.
+
+ int current_line_idx = 0;
+ int child_idx_in_line = 0;
+
+ ofs.x = 0;
+ ofs.y = 0;
+
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *child = Object::cast_to<Control>(get_child(i));
+ if (!child || !child->is_visible()) {
+ continue;
+ }
+ if (child->is_set_as_top_level()) {
+ continue;
+ }
+ Size2i child_size = children_minsize_cache[child];
+
+ _LineData line_data = lines_data[current_line_idx];
+ if (child_idx_in_line >= lines_data[current_line_idx].child_count) {
+ current_line_idx++;
+ child_idx_in_line = 0;
+ if (vertical) {
+ ofs.x += line_data.min_line_height + separation_horizontal;
+ ofs.y = 0;
+ } else {
+ ofs.x = 0;
+ ofs.y += line_data.min_line_height + separation_vertical;
+ }
+ line_data = lines_data[current_line_idx];
+ }
+
+ if (vertical) { /* VERTICAL */
+ if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+ child_size.width = line_data.min_line_height;
+ }
+
+ if (child->get_v_size_flags() & SIZE_EXPAND) {
+ int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
+ child_size.height += stretch;
+ }
+
+ } else { /* HORIZONTAL */
+ if (child->get_v_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+ child_size.height = line_data.min_line_height;
+ }
+
+ if (child->get_h_size_flags() & SIZE_EXPAND) {
+ int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
+ child_size.width += stretch;
+ }
+ }
+
+ Rect2 child_rect = Rect2(ofs, child_size);
+ if (rtl) {
+ child_rect.position.x = get_rect().size.x - child_rect.position.x - child_rect.size.width;
+ }
+
+ fit_child_in_rect(child, child_rect);
+
+ if (vertical) { /* VERTICAL */
+ ofs.y += child_size.height + separation_vertical;
+ } else { /* HORIZONTAL */
+ ofs.x += child_size.width + separation_horizontal;
+ }
+
+ child_idx_in_line++;
+ }
+ cached_size = (vertical ? ofs.x : ofs.y) + line_height;
+ cached_line_count = lines_data.size();
+}
+
+Size2 FlowContainer::get_minimum_size() const {
+ Size2i minimum;
+
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
+
+ if (!c->is_visible()) {
+ continue;
+ }
+
+ Size2i size = c->get_combined_minimum_size();
+
+ if (vertical) { /* VERTICAL */
+ minimum.height = MAX(minimum.height, size.height);
+ minimum.width = cached_size;
+
+ } else { /* HORIZONTAL */
+ minimum.width = MAX(minimum.width, size.width);
+ minimum.height = cached_size;
+ }
+ }
+
+ return minimum;
+}
+
+Vector<int> FlowContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (!vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> FlowContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+void FlowContainer::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_SORT_CHILDREN: {
+ _resort();
+ update_minimum_size();
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ update_minimum_size();
+ } break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ queue_sort();
+ } break;
+ }
+}
+
+int FlowContainer::get_line_count() const {
+ return cached_line_count;
+}
+
+FlowContainer::FlowContainer(bool p_vertical) {
+ vertical = p_vertical;
+}
+
+void FlowContainer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count);
+}
diff --git a/scene/3d/proximity_group_3d.h b/scene/gui/flow_container.h
index e45adc3040..a2da43e071 100644
--- a/scene/3d/proximity_group_3d.h
+++ b/scene/gui/flow_container.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* proximity_group_3d.h */
+/* flow_container.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,36 +28,21 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PROXIMITY_GROUP_H
-#define PROXIMITY_GROUP_H
+#ifndef FLOW_CONTAINER_H
+#define FLOW_CONTAINER_H
-#include "node_3d.h"
+#include "scene/gui/container.h"
-class ProximityGroup3D : public Node3D {
- GDCLASS(ProximityGroup3D, Node3D);
-
-public:
- enum DispatchMode {
- MODE_PROXY,
- MODE_SIGNAL,
- };
+class FlowContainer : public Container {
+ GDCLASS(FlowContainer, Container);
private:
- Map<StringName, uint32_t> groups;
-
- String group_name;
- DispatchMode dispatch_mode = MODE_PROXY;
- Vector3 grid_radius = Vector3(1, 1, 1);
+ int cached_size = 0;
+ int cached_line_count = 0;
- real_t cell_size = 1.0;
- uint32_t group_version = 0;
+ bool vertical = false;
- void _clear_groups();
- void _update_groups();
- void _add_groups(int *p_cell, String p_base, int p_depth);
- void _new_group(StringName p_name);
-
- void _proximity_group_broadcast(String p_method, Variant p_parameters);
+ void _resort();
protected:
void _notification(int p_what);
@@ -65,21 +50,30 @@ protected:
static void _bind_methods();
public:
- void set_group_name(const String &p_group_name);
- String get_group_name() const;
+ int get_line_count() const;
+
+ virtual Size2 get_minimum_size() const override;
- void set_dispatch_mode(DispatchMode p_mode);
- DispatchMode get_dispatch_mode() const;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
- void set_grid_radius(const Vector3 &p_radius);
- Vector3 get_grid_radius() const;
+ FlowContainer(bool p_vertical = false);
+};
- void broadcast(String p_method, Variant p_parameters);
+class HFlowContainer : public FlowContainer {
+ GDCLASS(HFlowContainer, FlowContainer);
- ProximityGroup3D();
- ~ProximityGroup3D() {}
+public:
+ HFlowContainer() :
+ FlowContainer(false) {}
};
-VARIANT_ENUM_CAST(ProximityGroup3D::DispatchMode);
+class VFlowContainer : public FlowContainer {
+ GDCLASS(VFlowContainer, FlowContainer);
+
+public:
+ VFlowContainer() :
+ FlowContainer(true) {}
+};
-#endif // PROXIMITY_GROUP_H
+#endif // FLOW_CONTAINER_H
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index ae15b021a5..0690acbe16 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -89,7 +89,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && grabbed != -1) {
- points.remove(grabbed);
+ points.remove_at(grabbed);
grabbed = -1;
grabbing = false;
update();
@@ -109,7 +109,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
grabbed = _get_point_from_pos(mb->get_position().x);
if (grabbed != -1) {
- points.remove(grabbed);
+ points.remove_at(grabbed);
grabbed = -1;
grabbing = false;
update();
@@ -287,84 +287,85 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
void GradientEdit::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) {
- picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed));
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) {
+ picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed));
+ }
+ [[fallthrough]];
}
- }
+ case NOTIFICATION_THEME_CHANGED: {
+ draw_spacing = BASE_SPACING * get_theme_default_base_scale();
+ draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale();
+ } break;
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
- draw_spacing = BASE_SPACING * get_theme_default_base_scale();
- draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale();
- }
+ case NOTIFICATION_DRAW: {
+ int w = get_size().x;
+ int h = get_size().y;
- if (p_what == NOTIFICATION_DRAW) {
- int w = get_size().x;
- int h = get_size().y;
+ if (w == 0 || h == 0) {
+ return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size.
+ }
- if (w == 0 || h == 0) {
- return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size.
- }
+ int total_w = get_size().width - get_size().height - draw_spacing;
- int total_w = get_size().width - get_size().height - draw_spacing;
+ // Draw checker pattern for ramp.
+ draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
- // Draw checker pattern for ramp.
- draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
+ // Draw color ramp.
+ gradient_cache->set_points(points);
+ gradient_cache->set_interpolation_mode(interpolation_mode);
+ preview_texture->set_gradient(gradient_cache);
+ draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h));
- // Draw color ramp.
+ // Draw point markers.
+ for (int i = 0; i < points.size(); i++) {
+ Color col = points[i].color.inverted();
+ col.a = 0.9;
- gradient_cache->set_points(points);
- gradient_cache->set_interpolation_mode(interpolation_mode);
- preview_texture->set_gradient(gradient_cache);
- draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h));
+ draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col);
+ Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2);
+ draw_rect(rect, points[i].color, true);
+ draw_rect(rect, col, false);
+ if (grabbed == i) {
+ rect = rect.grow(-1);
+ if (has_focus()) {
+ draw_rect(rect, Color(1, 0, 0, 0.9), false);
+ } else {
+ draw_rect(rect, Color(0.6, 0, 0, 0.9), false);
+ }
- // Draw point markers.
- for (int i = 0; i < points.size(); i++) {
- Color col = points[i].color.inverted();
- col.a = 0.9;
-
- draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col);
- Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2);
- draw_rect(rect, points[i].color, true);
- draw_rect(rect, col, false);
- if (grabbed == i) {
- rect = rect.grow(-1);
- if (has_focus()) {
- draw_rect(rect, Color(1, 0, 0, 0.9), false);
- } else {
- draw_rect(rect, Color(0.6, 0, 0, 0.9), false);
+ rect = rect.grow(-1);
+ draw_rect(rect, col, false);
}
-
- rect = rect.grow(-1);
- draw_rect(rect, col, false);
}
- }
- //Draw "button" for color selector
- draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true);
- if (grabbed != -1) {
- //Draw with selection color
- draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color);
- } else {
- //if no color selected draw grey color with 'X' on top.
- draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1));
- draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6));
- draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
- }
+ // Draw "button" for color selector.
+ draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true);
+ if (grabbed != -1) {
+ // Draw with selection color.
+ draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color);
+ } else {
+ // If no color selected draw grey color with 'X' on top.
+ draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1));
+ draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
+ }
- // Draw borders around color ramp if in focus.
- if (has_focus()) {
- draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(total_w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
- }
- }
+ // Draw borders around color ramp if in focus.
+ if (has_focus()) {
+ draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(total_w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
+ }
+ } break;
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- if (!is_visible()) {
- grabbing = false;
- }
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible()) {
+ grabbing = false;
+ }
+ } break;
}
}
@@ -432,6 +433,10 @@ Gradient::InterpolationMode GradientEdit::get_interpolation_mode() {
return interpolation_mode;
}
+ColorPicker *GradientEdit::get_picker() {
+ return picker;
+}
+
void GradientEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("ramp_changed"));
}
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 66b60d87c7..4e3c6525f9 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,14 +33,13 @@
#include "scene/gui/color_picker.h"
#include "scene/gui/popup.h"
-#include "scene/resources/default_theme/theme_data.h"
#include "scene/resources/gradient.h"
class GradientEdit : public Control {
GDCLASS(GradientEdit, Control);
- PopupPanel *popup;
- ColorPicker *picker;
+ PopupPanel *popup = nullptr;
+ ColorPicker *picker = nullptr;
bool grabbing = false;
int grabbed = -1;
@@ -75,6 +74,7 @@ public:
Vector<Gradient::Point> &get_points();
void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode);
Gradient::InterpolationMode get_interpolation_mode();
+ ColorPicker *get_picker();
virtual Size2 get_minimum_size() const override;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index e7d98a686f..0ed9a4e989 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,6 +35,7 @@
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
+#include "scene/gui/view_panner.h"
constexpr int MINIMAP_OFFSET = 12;
constexpr int MINIMAP_PADDING = 5;
@@ -422,82 +423,86 @@ void GraphEdit::remove_child_notify(Node *p_child) {
}
void GraphEdit::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
- port_grab_distance_horizontal = get_theme_constant(SNAME("port_grab_distance_horizontal"));
- port_grab_distance_vertical = get_theme_constant(SNAME("port_grab_distance_vertical"));
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ port_grab_distance_horizontal = get_theme_constant(SNAME("port_grab_distance_horizontal"));
+ port_grab_distance_vertical = get_theme_constant(SNAME("port_grab_distance_vertical"));
+
+ zoom_minus->set_icon(get_theme_icon(SNAME("minus")));
+ zoom_reset->set_icon(get_theme_icon(SNAME("reset")));
+ zoom_plus->set_icon(get_theme_icon(SNAME("more")));
+ snap_button->set_icon(get_theme_icon(SNAME("snap")));
+ minimap_button->set_icon(get_theme_icon(SNAME("minimap")));
+ layout_button->set_icon(get_theme_icon(SNAME("layout")));
+
+ zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale());
+ } break;
- zoom_minus->set_icon(get_theme_icon(SNAME("minus")));
- zoom_reset->set_icon(get_theme_icon(SNAME("reset")));
- zoom_plus->set_icon(get_theme_icon(SNAME("more")));
- snap_button->set_icon(get_theme_icon(SNAME("snap")));
- minimap_button->set_icon(get_theme_icon(SNAME("minimap")));
- layout_button->set_icon(get_theme_icon(SNAME("layout")));
+ case NOTIFICATION_READY: {
+ Size2 hmin = h_scroll->get_combined_minimum_size();
+ Size2 vmin = v_scroll->get_combined_minimum_size();
- zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale());
- }
- if (p_what == NOTIFICATION_READY) {
- Size2 hmin = h_scroll->get_combined_minimum_size();
- Size2 vmin = v_scroll->get_combined_minimum_size();
+ h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0);
+ h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height);
+ h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
- h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0);
- h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height);
- h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
+ v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width);
+ v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
+ v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
+ } break;
- v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width);
- v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
- v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
- }
- if (p_what == NOTIFICATION_DRAW) {
- draw_style_box(get_theme_stylebox(SNAME("bg")), Rect2(Point2(), get_size()));
+ case NOTIFICATION_DRAW: {
+ draw_style_box(get_theme_stylebox(SNAME("bg")), Rect2(Point2(), get_size()));
- if (is_using_snap()) {
- //draw grid
+ if (is_using_snap()) {
+ // Draw grid.
+ int snap = get_snap();
- int snap = get_snap();
+ Vector2 offset = get_scroll_ofs() / zoom;
+ Size2 size = get_size() / zoom;
- Vector2 offset = get_scroll_ofs() / zoom;
- Size2 size = get_size() / zoom;
+ Point2i from = (offset / float(snap)).floor();
+ Point2i len = (size / float(snap)).floor() + Vector2(1, 1);
- Point2i from = (offset / float(snap)).floor();
- Point2i len = (size / float(snap)).floor() + Vector2(1, 1);
+ Color grid_minor = get_theme_color(SNAME("grid_minor"));
+ Color grid_major = get_theme_color(SNAME("grid_major"));
- Color grid_minor = get_theme_color(SNAME("grid_minor"));
- Color grid_major = get_theme_color(SNAME("grid_major"));
+ for (int i = from.x; i < from.x + len.x; i++) {
+ Color color;
- for (int i = from.x; i < from.x + len.x; i++) {
- Color color;
+ if (ABS(i) % 10 == 0) {
+ color = grid_major;
+ } else {
+ color = grid_minor;
+ }
- if (ABS(i) % 10 == 0) {
- color = grid_major;
- } else {
- color = grid_minor;
+ float base_ofs = i * snap * zoom - offset.x * zoom;
+ draw_line(Vector2(base_ofs, 0), Vector2(base_ofs, get_size().height), color);
}
- float base_ofs = i * snap * zoom - offset.x * zoom;
- draw_line(Vector2(base_ofs, 0), Vector2(base_ofs, get_size().height), color);
- }
+ for (int i = from.y; i < from.y + len.y; i++) {
+ Color color;
- for (int i = from.y; i < from.y + len.y; i++) {
- Color color;
+ if (ABS(i) % 10 == 0) {
+ color = grid_major;
+ } else {
+ color = grid_minor;
+ }
- if (ABS(i) % 10 == 0) {
- color = grid_major;
- } else {
- color = grid_minor;
+ float base_ofs = i * snap * zoom - offset.y * zoom;
+ draw_line(Vector2(0, base_ofs), Vector2(get_size().width, base_ofs), color);
}
-
- float base_ofs = i * snap * zoom - offset.y * zoom;
- draw_line(Vector2(0, base_ofs), Vector2(get_size().width, base_ofs), color);
}
- }
- }
+ } break;
- if (p_what == NOTIFICATION_RESIZED) {
- _update_scroll();
- top_layer->update();
- minimap->update();
+ case NOTIFICATION_RESIZED: {
+ _update_scroll();
+ top_layer->update();
+ minimap->update();
+ } break;
}
}
@@ -518,7 +523,7 @@ void GraphEdit::_update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<S
}
}
- p_comment_enclosed_nodes.set(p_node->get_name(), enclosed_nodes);
+ p_comment_enclosed_nodes.insert(p_node->get_name(), enclosed_nodes);
}
void GraphEdit::_set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag) {
@@ -549,15 +554,13 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
}
for (int j = 0; j < gn->get_connection_output_count(); j++) {
- Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, false)) {
+ if (is_in_output_hotzone(gn, j, p_point / zoom, port_size)) {
return true;
}
}
for (int j = 0; j < gn->get_connection_input_count(); j++) {
- Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, true)) {
+ if (is_in_input_hotzone(gn, j, p_point / zoom, port_size)) {
return true;
}
}
@@ -582,7 +585,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos / zoom, click_pos, port_size, false)) {
+ if (is_in_output_hotzone(gn, j, click_pos, port_size)) {
if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
//check disconnect
for (const Connection &E : connections) {
@@ -602,6 +605,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
to = get_node(String(connecting_from)); //maybe it was erased
if (Object::cast_to<GraphNode>(to)) {
connecting = true;
+ emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false);
}
return;
}
@@ -618,13 +622,14 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_target = false;
connecting_to = pos;
just_disconnected = false;
+ emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true);
return;
}
}
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos / zoom, click_pos, port_size, true)) {
+ if (is_in_input_hotzone(gn, j, click_pos, port_size)) {
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
//check disconnect
for (const Connection &E : connections) {
@@ -644,6 +649,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
fr = get_node(String(connecting_from)); //maybe it was erased
if (Object::cast_to<GraphNode>(fr)) {
connecting = true;
+ emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true);
}
return;
}
@@ -660,7 +666,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_target = false;
connecting_to = pos;
just_disconnected = false;
-
+ emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false);
return;
}
}
@@ -690,7 +696,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
int type = gn->get_connection_output_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, false)) {
+ if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_output_hotzone(gn, j, mpos, port_size)) {
connecting_target = true;
connecting_to = pos;
connecting_target_to = gn->get_name();
@@ -702,7 +708,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
int type = gn->get_connection_input_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, true)) {
+ if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_input_hotzone(gn, j, mpos, port_size)) {
connecting_target = true;
connecting_to = pos;
connecting_target_to = gn->get_name();
@@ -742,11 +748,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
- connecting = false;
- top_layer->update();
- minimap->update();
- update();
- connections_layer->update();
+ if (connecting) {
+ force_connection_drag_end();
+ }
}
}
@@ -773,25 +777,29 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos)
}
}
-bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
- if (p_left) {
- if (!Rect2(
- pos.x - p_port_size.x / 2 - port_grab_distance_horizontal,
- pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2,
- p_port_size.x + port_grab_distance_horizontal,
- p_port_size.y + port_grab_distance_vertical)
- .has_point(p_mouse_pos)) {
- return false;
- }
+bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
+ bool success;
+ if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_graph_node, p_slot_index, p_mouse_pos, success)) {
+ return success;
} else {
- if (!Rect2(
- pos.x - p_port_size.x / 2,
- pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2,
- p_port_size.x + port_grab_distance_horizontal,
- p_port_size.y + port_grab_distance_vertical)
- .has_point(p_mouse_pos)) {
- return false;
- }
+ Vector2 pos = p_graph_node->get_connection_input_position(p_slot_index) + p_graph_node->get_position();
+ return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, true);
+ }
+}
+
+bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
+ bool success;
+ if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_graph_node, p_slot_index, p_mouse_pos, success)) {
+ return success;
+ } else {
+ Vector2 pos = p_graph_node->get_connection_output_position(p_slot_index) + p_graph_node->get_position();
+ return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, false);
+ }
+}
+
+bool GraphEdit::is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
+ if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) {
+ return false;
}
for (int i = 0; i < get_child_count(); i++) {
@@ -973,7 +981,7 @@ void GraphEdit::_minimap_draw() {
Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox(SNAME("node"))->duplicate();
// Override default values with colors provided by the GraphNode's stylebox, if possible.
- Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "commentfocus" : "comment");
+ Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "comment_focus" : "comment");
if (sbf.is_valid()) {
Color node_color = sbf->get_bg_color();
sb_minimap->set_bg_color(node_color);
@@ -996,7 +1004,7 @@ void GraphEdit::_minimap_draw() {
Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox(SNAME("node"))->duplicate();
// Override default values with colors provided by the GraphNode's stylebox, if possible.
- Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "selectedframe" : "frame");
+ Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "selected_frame" : "frame");
if (sbf.is_valid()) {
Color node_color = sbf->get_border_color();
sb_minimap->set_bg_color(node_color);
@@ -1066,13 +1074,11 @@ void GraphEdit::set_selected(Node *p_child) {
void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
ERR_FAIL_COND(p_ev.is_null());
+ if (panner->gui_input(p_ev, warped_panning ? get_global_rect() : Rect2())) {
+ return;
+ }
Ref<InputEventMouseMotion> mm = p_ev;
- if (mm.is_valid() && ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && Input::get_singleton()->is_key_pressed(Key::SPACE)))) {
- Vector2i relative = Input::get_singleton()->warp_mouse_motion(mm, get_global_rect());
- h_scroll->set_value(h_scroll->get_value() - relative.x);
- v_scroll->set_value(v_scroll->get_value() - relative.y);
- }
if (mm.is_valid() && dragging) {
if (!moving_selection) {
@@ -1162,11 +1168,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
minimap->update();
} else {
if (connecting) {
- connecting = false;
- top_layer->update();
- minimap->update();
+ force_connection_drag_end();
} else {
- emit_signal(SNAME("popup_request"), b->get_global_position());
+ emit_signal(SNAME("popup_request"), b->get_position());
}
}
}
@@ -1274,7 +1278,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (_filter_input(b->get_position())) {
return;
}
- if (Input::get_singleton()->is_key_pressed(Key::SPACE)) {
+ if (panner->is_panning()) {
return;
}
@@ -1326,22 +1330,6 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
top_layer->update();
minimap->update();
}
-
- int scroll_direction = (b->get_button_index() == MouseButton::WHEEL_DOWN) - (b->get_button_index() == MouseButton::WHEEL_UP);
- if (scroll_direction != 0) {
- if (b->is_ctrl_pressed()) {
- if (b->is_shift_pressed()) {
- // Horizontal scrolling.
- h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * b->get_factor() / 8) * scroll_direction);
- } else {
- // Vertical scrolling.
- v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * b->get_factor() / 8) * scroll_direction);
- }
- } else {
- // Zooming.
- set_zoom_custom(scroll_direction < 0 ? zoom * zoom_step : zoom / zoom_step, b->get_position());
- }
- }
}
if (p_ev->is_pressed()) {
@@ -1355,7 +1343,19 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
emit_signal(SNAME("paste_nodes_request"));
accept_event();
} else if (p_ev->is_action("ui_graph_delete")) {
- emit_signal(SNAME("delete_nodes_request"));
+ TypedArray<StringName> nodes;
+
+ for (int i = 0; i < get_child_count(); i++) {
+ GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
+ if (!gn) {
+ continue;
+ }
+ if (gn->is_selected() && gn->is_close_button_visible()) {
+ nodes.push_back(gn->get_name());
+ }
+ }
+
+ emit_signal(SNAME("delete_nodes_request"), nodes);
accept_event();
}
}
@@ -1372,6 +1372,23 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
}
+void GraphEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
+ if (p_scroll_vec.x != 0) {
+ h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * Math::abs(p_scroll_vec.x) / 8) * SIGN(p_scroll_vec.x));
+ } else {
+ v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * Math::abs(p_scroll_vec.y) / 8) * SIGN(p_scroll_vec.y));
+ }
+}
+
+void GraphEdit::_pan_callback(Vector2 p_scroll_vec) {
+ h_scroll->set_value(h_scroll->get_value() - p_scroll_vec.x);
+ v_scroll->set_value(v_scroll->get_value() - p_scroll_vec.y);
+}
+
+void GraphEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
+ set_zoom_custom(p_scroll_vec.y < 0 ? zoom * zoom_step : zoom / zoom_step, p_origin);
+}
+
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
for (Connection &E : connections) {
if (E.from == p_from && E.from_port == p_from_port && E.to == p_to && E.to_port == p_to_port) {
@@ -1394,6 +1411,26 @@ void GraphEdit::clear_connections() {
connections_layer->update();
}
+void GraphEdit::force_connection_drag_end() {
+ ERR_FAIL_COND_MSG(!connecting, "Drag end requested without active drag!");
+ connecting = false;
+ connecting_valid = false;
+ top_layer->update();
+ minimap->update();
+ update();
+ connections_layer->update();
+ emit_signal(SNAME("connection_drag_ended"));
+}
+
+void GraphEdit::set_panning_scheme(PanningScheme p_scheme) {
+ panning_scheme = p_scheme;
+ panner->set_control_scheme((ViewPanner::ControlScheme)p_scheme);
+}
+
+GraphEdit::PanningScheme GraphEdit::get_panning_scheme() const {
+ return panning_scheme;
+}
+
void GraphEdit::set_zoom(float p_zoom) {
set_zoom_custom(p_zoom, get_size() / 2);
}
@@ -1543,26 +1580,17 @@ void GraphEdit::_update_zoom_label() {
}
void GraphEdit::add_valid_connection_type(int p_type, int p_with_type) {
- ConnType ct;
- ct.type_a = p_type;
- ct.type_b = p_with_type;
-
+ ConnType ct(p_type, p_with_type);
valid_connection_types.insert(ct);
}
void GraphEdit::remove_valid_connection_type(int p_type, int p_with_type) {
- ConnType ct;
- ct.type_a = p_type;
- ct.type_b = p_with_type;
-
+ ConnType ct(p_type, p_with_type);
valid_connection_types.erase(ct);
}
bool GraphEdit::is_valid_connection_type(int p_type, int p_with_type) const {
- ConnType ct;
- ct.type_a = p_type;
- ct.type_b = p_with_type;
-
+ ConnType ct(p_type, p_with_type);
return valid_connection_types.has(ct);
}
@@ -1621,6 +1649,7 @@ float GraphEdit::get_minimap_opacity() const {
void GraphEdit::set_minimap_enabled(bool p_enable) {
minimap_button->set_pressed(p_enable);
+ _minimap_toggled();
minimap->update();
}
@@ -1659,12 +1688,21 @@ HBoxContainer *GraphEdit::get_zoom_hbox() {
return zoom_hb;
}
-int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, const Set<StringName> &r_v) {
+Ref<ViewPanner> GraphEdit::get_panner() {
+ return panner;
+}
+
+void GraphEdit::set_warped_panning(bool p_warped) {
+ warped_panning = p_warped;
+}
+
+int GraphEdit::_set_operations(SET_OPERATIONS p_operation, RBSet<StringName> &r_u, const RBSet<StringName> &r_v) {
switch (p_operation) {
case GraphEdit::IS_EQUAL: {
- for (Set<StringName>::Element *E = r_u.front(); E; E = E->next()) {
- if (!r_v.has(E->get()))
+ for (RBSet<StringName>::Element *E = r_u.front(); E; E = E->next()) {
+ if (!r_v.has(E->get())) {
return 0;
+ }
}
return r_u.size() == r_v.size();
} break;
@@ -1672,14 +1710,15 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u,
if (r_u.size() == r_v.size() && !r_u.size()) {
return 1;
}
- for (Set<StringName>::Element *E = r_u.front(); E; E = E->next()) {
- if (!r_v.has(E->get()))
+ for (RBSet<StringName>::Element *E = r_u.front(); E; E = E->next()) {
+ if (!r_v.has(E->get())) {
return 0;
+ }
}
return 1;
} break;
case GraphEdit::DIFFERENCE: {
- for (Set<StringName>::Element *E = r_u.front(); E; E = E->next()) {
+ for (RBSet<StringName>::Element *E = r_u.front(); E; E = E->next()) {
if (r_v.has(E->get())) {
r_u.erase(E->get());
}
@@ -1687,12 +1726,12 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u,
return r_u.size();
} break;
case GraphEdit::UNION: {
- for (Set<StringName>::Element *E = r_v.front(); E; E = E->next()) {
+ for (RBSet<StringName>::Element *E = r_v.front(); E; E = E->next()) {
if (!r_u.has(E->get())) {
r_u.insert(E->get());
}
}
- return r_v.size();
+ return r_u.size();
} break;
default:
break;
@@ -1700,27 +1739,27 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u,
return -1;
}
-HashMap<int, Vector<StringName>> GraphEdit::_layering(const Set<StringName> &r_selected_nodes, const HashMap<StringName, Set<StringName>> &r_upper_neighbours) {
+HashMap<int, Vector<StringName>> GraphEdit::_layering(const RBSet<StringName> &r_selected_nodes, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours) {
HashMap<int, Vector<StringName>> l;
- Set<StringName> p = r_selected_nodes, q = r_selected_nodes, u, z;
+ RBSet<StringName> p = r_selected_nodes, q = r_selected_nodes, u, z;
int current_layer = 0;
bool selected = false;
while (!_set_operations(GraphEdit::IS_EQUAL, q, u)) {
_set_operations(GraphEdit::DIFFERENCE, p, u);
- for (const Set<StringName>::Element *E = p.front(); E; E = E->next()) {
- Set<StringName> n = r_upper_neighbours[E->get()];
+ for (const RBSet<StringName>::Element *E = p.front(); E; E = E->next()) {
+ RBSet<StringName> n = r_upper_neighbours[E->get()];
if (_set_operations(GraphEdit::IS_SUBSET, n, z)) {
Vector<StringName> t;
t.push_back(E->get());
if (!l.has(current_layer)) {
- l.set(current_layer, Vector<StringName>{});
+ l.insert(current_layer, Vector<StringName>{});
}
selected = true;
t.append_array(l[current_layer]);
- l.set(current_layer, t);
- Set<StringName> V;
+ l.insert(current_layer, t);
+ RBSet<StringName> V;
V.insert(E->get());
_set_operations(GraphEdit::UNION, u, V);
}
@@ -1762,8 +1801,8 @@ Vector<StringName> GraphEdit::_split(const Vector<StringName> &r_layer, const Ha
return left;
}
-void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, Set<StringName>> &r_upper_neighbours, const Set<StringName> &r_selected_nodes) {
- for (const Set<StringName>::Element *E = r_selected_nodes.front(); E; E = E->next()) {
+void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours, const RBSet<StringName> &r_selected_nodes) {
+ for (const RBSet<StringName>::Element *E = r_selected_nodes.front(); E; E = E->next()) {
r_root[E->get()] = E->get();
r_align[E->get()] = E->get();
}
@@ -1787,8 +1826,8 @@ void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, c
}
}
- int start = up.size() / 2;
- int end = up.size() % 2 ? start : start + 1;
+ int start = (up.size() - 1) / 2;
+ int end = (up.size() - 1) % 2 ? start + 1 : start;
for (int p = start; p <= end; p++) {
StringName Align = r_align[current_node];
if (Align == current_node && r < up[p].first) {
@@ -1802,7 +1841,7 @@ void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, c
}
}
-void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, Set<StringName>> &r_upper_neighbours) {
+void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours) {
if (r_layers.size() == 1) {
return;
}
@@ -1833,15 +1872,15 @@ void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layer
}
d[q] = crossings;
}
- c.set(p, d);
+ c.insert(p, d);
}
- r_layers.set(i, _split(lower_layer, c));
+ r_layers.insert(i, _split(lower_layer, c));
}
}
-void GraphEdit::_calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const Set<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info) {
- for (const Set<StringName>::Element *E = r_block_heads.front(); E; E = E->next()) {
+void GraphEdit::_calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const RBSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info) {
+ for (const RBSet<StringName>::Element *E = r_block_heads.front(); E; E = E->next()) {
real_t left = 0;
StringName u = E->get();
StringName v = r_align[u];
@@ -1999,7 +2038,7 @@ void GraphEdit::_place_block(StringName p_v, float p_delta, const HashMap<int, V
threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions);
w = r_align[w];
} while (w != p_v);
- r_node_positions.set(p_v, pos);
+ r_node_positions.insert(p_v, pos);
}
#undef PRED
@@ -2013,7 +2052,7 @@ void GraphEdit::arrange_nodes() {
}
Dictionary node_names;
- Set<StringName> selected_nodes;
+ RBSet<StringName> selected_nodes;
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@@ -2024,7 +2063,7 @@ void GraphEdit::arrange_nodes() {
node_names[gn->get_name()] = gn;
}
- HashMap<StringName, Set<StringName>> upper_neighbours;
+ HashMap<StringName, RBSet<StringName>> upper_neighbours;
HashMap<StringName, Pair<int, int>> port_info;
Vector2 origin(FLT_MAX, FLT_MAX);
@@ -2039,7 +2078,7 @@ void GraphEdit::arrange_nodes() {
if (gn->is_selected()) {
selected_nodes.insert(gn->get_name());
- Set<StringName> s;
+ RBSet<StringName> s;
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
GraphNode *p_from = Object::cast_to<GraphNode>(node_names[E->get().from]);
if (E->get().to == gn->get_name() && p_from->is_selected()) {
@@ -2055,10 +2094,10 @@ void GraphEdit::arrange_nodes() {
ports = p_ports;
}
}
- port_info.set(_connection, ports);
+ port_info.insert(_connection, ports);
}
}
- upper_neighbours.set(gn->get_name(), s);
+ upper_neighbours.insert(gn->get_name(), s);
}
}
@@ -2076,13 +2115,13 @@ void GraphEdit::arrange_nodes() {
HashMap<StringName, Vector2> new_positions;
Vector2 default_position(FLT_MAX, FLT_MAX);
Dictionary inner_shift;
- Set<StringName> block_heads;
+ RBSet<StringName> block_heads;
- for (const Set<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) {
+ for (const RBSet<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) {
inner_shift[E->get()] = 0.0f;
sink[E->get()] = E->get();
shift[E->get()] = FLT_MAX;
- new_positions.set(E->get(), default_position);
+ new_positions.insert(E->get(), default_position);
if ((StringName)root[E->get()] == E->get()) {
block_heads.insert(E->get());
}
@@ -2090,24 +2129,24 @@ void GraphEdit::arrange_nodes() {
_calculate_inner_shifts(inner_shift, root, node_names, align, block_heads, port_info);
- for (const Set<StringName>::Element *E = block_heads.front(); E; E = E->next()) {
+ for (const RBSet<StringName>::Element *E = block_heads.front(); E; E = E->next()) {
_place_block(E->get(), gap_v, layers, root, align, node_names, inner_shift, sink, shift, new_positions);
}
origin.y = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().y - (new_positions[layers[0][0]].y + (float)inner_shift[layers[0][0]]);
origin.x = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().x;
- for (const Set<StringName>::Element *E = block_heads.front(); E; E = E->next()) {
+ for (const RBSet<StringName>::Element *E = block_heads.front(); E; E = E->next()) {
StringName u = E->get();
float start_from = origin.y + new_positions[E->get()].y;
do {
Vector2 cal_pos;
cal_pos.y = start_from + (real_t)inner_shift[u];
- new_positions.set(u, cal_pos);
+ new_positions.insert(u, cal_pos);
u = align[u];
} while (u != E->get());
}
- //Compute horizontal co-ordinates individually for layers to get uniform gap
+ // Compute horizontal coordinates individually for layers to get uniform gap.
float start_from = origin.x;
float largest_node_size = 0.0f;
@@ -2134,7 +2173,7 @@ void GraphEdit::arrange_nodes() {
}
cal_pos.x = current_node_start_pos;
}
- new_positions.set(layer[j], cal_pos);
+ new_positions.insert(layer[j], cal_pos);
}
start_from += largest_node_size + gap_h;
@@ -2142,7 +2181,7 @@ void GraphEdit::arrange_nodes() {
}
emit_signal(SNAME("begin_node_move"));
- for (const Set<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) {
+ for (const RBSet<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) {
GraphNode *gn = Object::cast_to<GraphNode>(node_names[E->get()]);
gn->set_drag(true);
Vector2 pos = (new_positions[E->get()]);
@@ -2165,8 +2204,9 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_connection_activity", "from", "from_port", "to", "to_port", "amount"), &GraphEdit::set_connection_activity);
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections);
+ ClassDB::bind_method(D_METHOD("force_connection_drag_end"), &GraphEdit::force_connection_drag_end);
ClassDB::bind_method(D_METHOD("get_scroll_ofs"), &GraphEdit::get_scroll_ofs);
- ClassDB::bind_method(D_METHOD("set_scroll_ofs", "ofs"), &GraphEdit::set_scroll_ofs);
+ ClassDB::bind_method(D_METHOD("set_scroll_ofs", "offset"), &GraphEdit::set_scroll_ofs);
ClassDB::bind_method(D_METHOD("add_valid_right_disconnect_type", "type"), &GraphEdit::add_valid_right_disconnect_type);
ClassDB::bind_method(D_METHOD("remove_valid_right_disconnect_type", "type"), &GraphEdit::remove_valid_right_disconnect_type);
@@ -2177,6 +2217,9 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type);
ClassDB::bind_method(D_METHOD("get_connection_line", "from", "to"), &GraphEdit::get_connection_line);
+ ClassDB::bind_method(D_METHOD("set_panning_scheme", "scheme"), &GraphEdit::set_panning_scheme);
+ ClassDB::bind_method(D_METHOD("get_panning_scheme"), &GraphEdit::get_panning_scheme);
+
ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &GraphEdit::set_zoom);
ClassDB::bind_method(D_METHOD("get_zoom"), &GraphEdit::get_zoom);
@@ -2216,6 +2259,8 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
ClassDB::bind_method(D_METHOD("_update_scroll_offset"), &GraphEdit::_update_scroll_offset);
+ GDVIRTUAL_BIND(_is_in_input_hotzone, "graph_node", "slot_index", "mouse_position");
+ GDVIRTUAL_BIND(_is_in_output_hotzone, "graph_node", "slot_index", "mouse_position");
ClassDB::bind_method(D_METHOD("get_zoom_hbox"), &GraphEdit::get_zoom_hbox);
@@ -2229,6 +2274,7 @@ void GraphEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset"), "set_scroll_ofs", "get_scroll_ofs");
ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme");
ADD_GROUP("Connection Lines", "connection_lines");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness"), "set_connection_lines_thickness", "get_connection_lines_thickness");
@@ -2256,10 +2302,15 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position")));
ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"), PropertyInfo(Variant::VECTOR2, "release_position")));
- ADD_SIGNAL(MethodInfo("delete_nodes_request"));
+ ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName")));
ADD_SIGNAL(MethodInfo("begin_node_move"));
ADD_SIGNAL(MethodInfo("end_node_move"));
- ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "ofs")));
+ ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset")));
+ ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::STRING, "slot"), PropertyInfo(Variant::BOOL, "is_output")));
+ ADD_SIGNAL(MethodInfo("connection_drag_ended"));
+
+ BIND_ENUM_CONSTANT(SCROLL_ZOOMS);
+ BIND_ENUM_CONSTANT(SCROLL_PANS);
}
GraphEdit::GraphEdit() {
@@ -2272,12 +2323,16 @@ GraphEdit::GraphEdit() {
// Allow zooming 4 times from the default zoom level.
zoom_max = (1 * Math::pow(zoom_step, 4));
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &GraphEdit::_scroll_callback), callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback));
+
top_layer = memnew(GraphEditFilter(this));
add_child(top_layer, false, INTERNAL_MODE_BACK);
top_layer->set_mouse_filter(MOUSE_FILTER_PASS);
top_layer->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
top_layer->connect("draw", callable_mp(this, &GraphEdit::_top_layer_draw));
top_layer->connect("gui_input", callable_mp(this, &GraphEdit::_top_layer_input));
+ top_layer->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
connections_layer = memnew(Control);
add_child(connections_layer, false, INTERNAL_MODE_FRONT);
@@ -2312,7 +2367,7 @@ GraphEdit::GraphEdit() {
zoom_hb->add_child(zoom_label);
zoom_label->set_visible(false);
zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
- zoom_label->set_align(Label::ALIGN_CENTER);
+ zoom_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
zoom_label->set_custom_minimum_size(Size2(48, 0));
_update_zoom_label();
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 6c11f9df6a..9e34d5528f 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,18 +36,17 @@
#include "scene/gui/graph_node.h"
#include "scene/gui/label.h"
#include "scene/gui/scroll_bar.h"
-#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
-#include "scene/gui/texture_rect.h"
class GraphEdit;
+class ViewPanner;
class GraphEditFilter : public Control {
GDCLASS(GraphEditFilter, Control);
friend class GraphEdit;
friend class GraphEditMinimap;
- GraphEdit *ge;
+ GraphEdit *ge = nullptr;
virtual bool has_point(const Point2 &p_point) const override;
public:
@@ -59,7 +58,7 @@ class GraphEditMinimap : public Control {
friend class GraphEdit;
friend class GraphEditFilter;
- GraphEdit *ge;
+ GraphEdit *ge = nullptr;
protected:
public:
@@ -103,24 +102,36 @@ public:
float activity = 0.0;
};
+ // Should be in sync with ControlScheme in ViewPanner.
+ enum PanningScheme {
+ SCROLL_ZOOMS,
+ SCROLL_PANS,
+ };
+
private:
- Label *zoom_label;
- Button *zoom_minus;
- Button *zoom_reset;
- Button *zoom_plus;
+ Label *zoom_label = nullptr;
+ Button *zoom_minus = nullptr;
+ Button *zoom_reset = nullptr;
+ Button *zoom_plus = nullptr;
- Button *snap_button;
- SpinBox *snap_amount;
+ Button *snap_button = nullptr;
+ SpinBox *snap_amount = nullptr;
- Button *minimap_button;
+ Button *minimap_button = nullptr;
- Button *layout_button;
+ Button *layout_button = nullptr;
- HScrollBar *h_scroll;
- VScrollBar *v_scroll;
+ HScrollBar *h_scroll = nullptr;
+ VScrollBar *v_scroll = nullptr;
float port_grab_distance_horizontal = 0.0;
- float port_grab_distance_vertical;
+ float port_grab_distance_vertical = 0.0;
+
+ Ref<ViewPanner> panner;
+ bool warped_panning = true;
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
bool connecting = false;
String connecting_from;
@@ -131,11 +142,12 @@ private:
bool connecting_target = false;
Vector2 connecting_to;
String connecting_target_to;
- int connecting_target_index;
+ int connecting_target_index = 0;
bool just_disconnected = false;
bool connecting_valid = false;
Vector2 click_pos;
+ PanningScheme panning_scheme = SCROLL_ZOOMS;
bool dragging = false;
bool just_selected = false;
bool moving_selection = false;
@@ -143,8 +155,9 @@ private:
float zoom = 1.0;
float zoom_step = 1.2;
- float zoom_min;
- float zoom_max;
+ // Proper values set in constructor.
+ float zoom_min = 0.0;
+ float zoom_max = 0.0;
void _zoom_minus();
void _zoom_reset();
@@ -178,12 +191,14 @@ private:
void _scroll_moved(double);
virtual void gui_input(const Ref<InputEvent> &p_ev) override;
- Control *connections_layer;
- GraphEditFilter *top_layer;
- GraphEditMinimap *minimap;
+ Control *connections_layer = nullptr;
+ GraphEditFilter *top_layer = nullptr;
+ GraphEditMinimap *minimap = nullptr;
void _top_layer_input(const Ref<InputEvent> &p_ev);
- bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
+ bool is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ bool is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ bool is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
void _top_layer_draw();
void _connections_layer_draw();
@@ -192,7 +207,7 @@ private:
Array _get_connection_list() const;
- bool lines_on_bg;
+ bool lines_on_bg = false;
struct ConnType {
union {
@@ -213,16 +228,16 @@ private:
}
};
- Set<ConnType> valid_connection_types;
- Set<int> valid_left_disconnect_types;
- Set<int> valid_right_disconnect_types;
+ RBSet<ConnType> valid_connection_types;
+ RBSet<int> valid_left_disconnect_types;
+ RBSet<int> valid_right_disconnect_types;
HashMap<StringName, Vector<GraphNode *>> comment_enclosed_nodes;
void _update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes);
void _set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag);
void _set_position_of_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, Vector2 p_pos);
- HBoxContainer *zoom_hb;
+ HBoxContainer *zoom_hb = nullptr;
friend class GraphEditFilter;
bool _filter_input(const Point2 &p_point);
@@ -243,12 +258,12 @@ private:
UNION,
};
- int _set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, const Set<StringName> &r_v);
- HashMap<int, Vector<StringName>> _layering(const Set<StringName> &r_selected_nodes, const HashMap<StringName, Set<StringName>> &r_upper_neighbours);
+ int _set_operations(SET_OPERATIONS p_operation, RBSet<StringName> &r_u, const RBSet<StringName> &r_v);
+ HashMap<int, Vector<StringName>> _layering(const RBSet<StringName> &r_selected_nodes, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours);
Vector<StringName> _split(const Vector<StringName> &r_layer, const HashMap<StringName, Dictionary> &r_crossings);
- void _horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, Set<StringName>> &r_upper_neighbours, const Set<StringName> &r_selected_nodes);
- void _crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, Set<StringName>> &r_upper_neighbours);
- void _calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const Set<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info);
+ void _horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours, const RBSet<StringName> &r_selected_nodes);
+ void _crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours);
+ void _calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const RBSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info);
float _calculate_threshold(StringName p_v, StringName p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions);
void _place_block(StringName p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions);
@@ -259,12 +274,15 @@ protected:
void _notification(int p_what);
GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2)
+ GDVIRTUAL3R(bool, _is_in_input_hotzone, Object *, int, Vector2)
+ GDVIRTUAL3R(bool, _is_in_output_hotzone, Object *, int, Vector2)
public:
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void clear_connections();
+ void force_connection_drag_end();
void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity);
@@ -272,6 +290,9 @@ public:
void remove_valid_connection_type(int p_type, int p_with_type);
bool is_valid_connection_type(int p_type, int p_with_type) const;
+ void set_panning_scheme(PanningScheme p_scheme);
+ PanningScheme get_panning_scheme() const;
+
void set_zoom(float p_zoom);
void set_zoom_custom(float p_zoom, const Vector2 &p_center);
float get_zoom() const;
@@ -327,10 +348,14 @@ public:
bool is_connection_lines_antialiased() const;
HBoxContainer *get_zoom_hbox();
+ Ref<ViewPanner> get_panner();
+ void set_warped_panning(bool p_warped);
void arrange_nodes();
GraphEdit();
};
+VARIANT_ENUM_CAST(GraphEdit::PanningScheme);
+
#endif // GRAPHEdit_H
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index e7094c89b1..45f036d8fc 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,7 @@
#include "graph_node.h"
#include "core/string/translation.h"
+
#ifdef TOOLS_ENABLED
#include "graph_edit.h"
#endif
@@ -46,7 +47,7 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -54,7 +55,7 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@@ -92,11 +93,13 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
si.color_right = p_value;
} else if (what == "right_icon") {
si.custom_slot_right = p_value;
+ } else if (what == "draw_stylebox") {
+ si.draw_stylebox = p_value;
} else {
return false;
}
- set_slot(idx, si.enable_left, si.type_left, si.color_left, si.enable_right, si.type_right, si.color_right, si.custom_slot_left, si.custom_slot_right);
+ set_slot(idx, si.enable_left, si.type_left, si.color_left, si.enable_right, si.type_right, si.color_right, si.custom_slot_left, si.custom_slot_right, si.draw_stylebox);
update();
return true;
}
@@ -143,6 +146,8 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = si.color_right;
} else if (what == "right_icon") {
r_ret = si.custom_slot_right;
+ } else if (what == "draw_stylebox") {
+ r_ret = si.draw_stylebox;
} else {
return false;
}
@@ -153,7 +158,7 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
@@ -174,7 +179,7 @@ void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, base + "right_type"));
p_list->push_back(PropertyInfo(Variant::COLOR, base + "right_color"));
p_list->push_back(PropertyInfo(Variant::OBJECT, base + "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
-
+ p_list->push_back(PropertyInfo(Variant::BOOL, base + "draw_stylebox"));
idx++;
}
}
@@ -184,6 +189,7 @@ void GraphNode::_resort() {
Size2i new_size = get_size();
Ref<StyleBox> sb = get_theme_stylebox(SNAME("frame"));
+ Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot"));
int sep = get_theme_constant(SNAME("separation"));
@@ -192,7 +198,7 @@ void GraphNode::_resort() {
int stretch_min = 0;
int stretch_avail = 0;
float stretch_ratio_total = 0;
- Map<Control *, _MinSizeCache> min_size_cache;
+ HashMap<Control *, _MinSizeCache> min_size_cache;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
@@ -203,7 +209,7 @@ void GraphNode::_resort() {
continue;
}
- Size2i size = c->get_combined_minimum_size();
+ Size2i size = c->get_combined_minimum_size() + (slot_info[i].draw_stylebox ? sb_slot->get_minimum_size() : Size2());
_MinSizeCache msc;
stretch_min += size.height;
@@ -311,7 +317,9 @@ void GraphNode::_resort() {
int size = to - from;
- Rect2 rect(sb->get_margin(SIDE_LEFT), from, w, size);
+ float margin = sb->get_margin(SIDE_LEFT) + (slot_info[i].draw_stylebox ? sb_slot->get_margin(SIDE_LEFT) : 0);
+ float width = w - (slot_info[i].draw_stylebox ? sb_slot->get_minimum_size().x : 0);
+ Rect2 rect(margin, from, width, size);
fit_child_in_rect(c, rect);
cache_y.push_back(from - sb->get_margin(SIDE_TOP) + size * 0.5);
@@ -350,14 +358,14 @@ void GraphNode::_notification(int p_what) {
Ref<StyleBox> sb;
if (comment) {
- sb = get_theme_stylebox(selected ? "commentfocus" : "comment");
+ sb = get_theme_stylebox(selected ? SNAME("comment_focus") : SNAME("comment"));
} else {
- sb = get_theme_stylebox(selected ? "selectedframe" : "frame");
+ sb = get_theme_stylebox(selected ? SNAME("selected_frame") : SNAME("frame"));
}
- //sb=sb->duplicate();
- //sb->call("set_modulate",modulate);
+ Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot"));
+
Ref<Texture2D> port = get_theme_icon(SNAME("port"));
Ref<Texture2D> close = get_theme_icon(SNAME("close"));
Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
@@ -388,14 +396,9 @@ void GraphNode::_notification(int p_what) {
int w = get_size().width - sb->get_minimum_size().x;
- if (show_close) {
- w -= close->get_width();
- }
-
- title_buf->set_width(w);
title_buf->draw(get_canvas_item(), Point2(sb->get_margin(SIDE_LEFT) + title_h_offset, -title_buf->get_size().y + title_offset), title_color);
if (show_close) {
- Vector2 cpos = Point2(w + sb->get_margin(SIDE_LEFT) + close_h_offset, -close->get_height() + close_offset);
+ Vector2 cpos = Point2(w + sb->get_margin(SIDE_LEFT) + close_h_offset - close->get_width(), -close->get_height() + close_offset);
draw_texture(close, cpos, close_color);
close_rect.position = cpos;
close_rect.size = close->get_size();
@@ -411,7 +414,7 @@ void GraphNode::_notification(int p_what) {
continue;
}
const Slot &s = slot_info[E.key];
- //left
+ // Left port.
if (s.enable_left) {
Ref<Texture2D> p = port;
if (s.custom_slot_left.is_valid()) {
@@ -419,6 +422,7 @@ void GraphNode::_notification(int p_what) {
}
p->draw(get_canvas_item(), icofs + Point2(edgeofs, cache_y[E.key]), s.color_left);
}
+ // Right port.
if (s.enable_right) {
Ref<Texture2D> p = port;
if (s.custom_slot_right.is_valid()) {
@@ -426,6 +430,15 @@ void GraphNode::_notification(int p_what) {
}
p->draw(get_canvas_item(), icofs + Point2(get_size().x - edgeofs, cache_y[E.key]), s.color_right);
}
+
+ // Draw slot stylebox.
+ if (s.draw_stylebox) {
+ Control *c = Object::cast_to<Control>(get_child(E.key));
+ Rect2 c_rect = c->get_rect();
+ c_rect.position.x = sb->get_margin(SIDE_LEFT);
+ c_rect.size.width = w;
+ draw_style_box(sb_slot, c_rect);
+ }
}
if (resizable) {
@@ -442,7 +455,7 @@ void GraphNode::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
}
@@ -458,7 +471,7 @@ void GraphNode::_shape() {
} else {
title_buf->set_direction((TextServer::Direction)text_direction);
}
- title_buf->add_string(title, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ title_buf->add_string(title, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
}
#ifdef TOOLS_ENABLED
@@ -482,7 +495,7 @@ void GraphNode::_validate_property(PropertyInfo &property) const {
}
#endif
-void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right) {
+void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right, bool p_draw_stylebox) {
ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set slot with p_idx (%d) lesser than zero.", p_idx));
if (!p_enable_left && p_type_left == 0 && p_color_left == Color(1, 1, 1, 1) &&
@@ -501,6 +514,7 @@ void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const C
s.color_right = p_color_right;
s.custom_slot_left = p_custom_left;
s.custom_slot_right = p_custom_right;
+ s.draw_stylebox = p_draw_stylebox;
slot_info[p_idx] = s;
update();
connpos_dirty = true;
@@ -622,16 +636,39 @@ Color GraphNode::get_slot_color_right(int p_idx) const {
return slot_info[p_idx].color_right;
}
+bool GraphNode::is_slot_draw_stylebox(int p_idx) const {
+ if (!slot_info.has(p_idx)) {
+ return false;
+ }
+ return slot_info[p_idx].draw_stylebox;
+}
+
+void GraphNode::set_slot_draw_stylebox(int p_idx, bool p_enable) {
+ ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set draw_stylebox for the slot with p_idx (%d) lesser than zero.", p_idx));
+
+ slot_info[p_idx].draw_stylebox = p_enable;
+ update();
+ connpos_dirty = true;
+
+ emit_signal(SNAME("slot_updated"), p_idx);
+}
+
Size2 GraphNode::get_minimum_size() const {
- int sep = get_theme_constant(SNAME("separation"));
Ref<StyleBox> sb = get_theme_stylebox(SNAME("frame"));
+ Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot"));
+
+ int sep = get_theme_constant(SNAME("separation"));
+ int title_h_offset = get_theme_constant(SNAME("title_h_offset"));
+
bool first = true;
Size2 minsize;
- minsize.x = title_buf->get_size().x;
+ minsize.x = title_buf->get_size().x + title_h_offset;
if (show_close) {
+ int close_h_offset = get_theme_constant(SNAME("close_h_offset"));
Ref<Texture2D> close = get_theme_icon(SNAME("close"));
- minsize.x += sep + close->get_width();
+ //TODO: Remove this magic number after GraphNode rework.
+ minsize.x += 12 + close->get_width() + close_h_offset;
}
for (int i = 0; i < get_child_count(); i++) {
@@ -644,6 +681,9 @@ Size2 GraphNode::get_minimum_size() const {
}
Size2i size = c->get_combined_minimum_size();
+ if (slot_info.has(i)) {
+ size += slot_info[i].draw_stylebox ? sb_slot->get_minimum_size() : Size2();
+ }
minsize.y += size.y;
minsize.x = MAX(minsize.x, size.x);
@@ -666,7 +706,7 @@ void GraphNode::set_title(const String &p_title) {
_shape();
update();
- minimum_size_changed();
+ update_minimum_size();
}
String GraphNode::get_title() const {
@@ -959,6 +999,25 @@ bool GraphNode::is_resizable() const {
return resizable;
}
+Vector<int> GraphNode::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> GraphNode::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_EXPAND);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &GraphNode::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &GraphNode::get_title);
@@ -970,7 +1029,7 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_language", "language"), &GraphNode::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &GraphNode::get_language);
- ClassDB::bind_method(D_METHOD("set_slot", "idx", "enable_left", "type_left", "color_left", "enable_right", "type_right", "color_right", "custom_left", "custom_right"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()));
+ ClassDB::bind_method(D_METHOD("set_slot", "idx", "enable_left", "type_left", "color_left", "enable_right", "type_right", "color_right", "custom_left", "custom_right", "enable"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(true));
ClassDB::bind_method(D_METHOD("clear_slot", "idx"), &GraphNode::clear_slot);
ClassDB::bind_method(D_METHOD("clear_all_slots"), &GraphNode::clear_all_slots);
@@ -992,6 +1051,9 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_slot_color_right", "idx", "color_right"), &GraphNode::set_slot_color_right);
ClassDB::bind_method(D_METHOD("get_slot_color_right", "idx"), &GraphNode::get_slot_color_right);
+ ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "idx"), &GraphNode::is_slot_draw_stylebox);
+ ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "idx", "draw_stylebox"), &GraphNode::set_slot_draw_stylebox);
+
ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphNode::set_position_offset);
ClassDB::bind_method(D_METHOD("get_position_offset"), &GraphNode::get_position_offset);
@@ -1022,7 +1084,7 @@ void GraphNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset"), "set_position_offset", "get_position_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable");
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index 2238cfdb56..9481a7452d 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -54,6 +54,7 @@ private:
Color color_right = Color(1, 1, 1, 1);
Ref<Texture2D> custom_slot_left;
Ref<Texture2D> custom_slot_right;
+ bool draw_stylebox = true;
};
String title;
@@ -85,7 +86,7 @@ private:
Vector<ConnCache> conn_input_cache;
Vector<ConnCache> conn_output_cache;
- Map<int, Slot> slot_info;
+ HashMap<int, Slot> slot_info;
bool connpos_dirty = true;
@@ -115,7 +116,7 @@ protected:
public:
bool has_point(const Point2 &p_point) const override;
- void set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>());
+ void set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>(), bool p_draw_stylebox = true);
void clear_slot(int p_idx);
void clear_all_slots();
@@ -137,6 +138,9 @@ public:
void set_slot_color_right(int p_idx, const Color &p_color_right);
Color get_slot_color_right(int p_idx) const;
+ bool is_slot_draw_stylebox(int p_idx) const;
+ void set_slot_draw_stylebox(int p_idx, bool p_enable);
+
void set_title(const String &p_title);
String get_title() const;
@@ -182,7 +186,12 @@ public:
virtual Size2 get_minimum_size() const override;
- bool is_resizing() const { return resizing; }
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
+ bool is_resizing() const {
+ return resizing;
+ }
GraphNode();
};
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index 2beb2624d2..ec33018da0 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,13 +33,13 @@
void GridContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {
- Map<int, int> col_minw; // Max of min_width of all controls in each col (indexed by col).
- Map<int, int> row_minh; // Max of min_height of all controls in each row (indexed by row).
- Set<int> col_expanded; // Columns which have the SIZE_EXPAND flag set.
- Set<int> row_expanded; // Rows which have the SIZE_EXPAND flag set.
+ HashMap<int, int> col_minw; // Max of min_width of all controls in each col (indexed by col).
+ HashMap<int, int> row_minh; // Max of min_height of all controls in each row (indexed by row).
+ RBSet<int> col_expanded; // Columns which have the SIZE_EXPAND flag set.
+ RBSet<int> row_expanded; // Rows which have the SIZE_EXPAND flag set.
- int hsep = get_theme_constant(SNAME("hseparation"));
- int vsep = get_theme_constant(SNAME("vseparation"));
+ int hsep = get_theme_constant(SNAME("h_separation"));
+ int vsep = get_theme_constant(SNAME("v_separation"));
int max_col = MIN(get_child_count(), columns);
int max_row = ceil((float)get_child_count() / (float)columns);
@@ -50,6 +50,9 @@ void GridContainer::_notification(int p_what) {
if (!c || !c->is_visible_in_tree()) {
continue;
}
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
int row = valid_controls_index / columns;
int col = valid_controls_index % columns;
@@ -101,7 +104,7 @@ void GridContainer::_notification(int p_what) {
// Check if all minwidth constraints are OK if we use the remaining space.
can_fit = true;
int max_index = col_expanded.front()->get();
- for (Set<int>::Element *E = col_expanded.front(); E; E = E->next()) {
+ for (RBSet<int>::Element *E = col_expanded.front(); E; E = E->next()) {
if (col_minw[E->get()] > col_minw[max_index]) {
max_index = E->get();
}
@@ -122,7 +125,7 @@ void GridContainer::_notification(int p_what) {
// Check if all minheight constraints are OK if we use the remaining space.
can_fit = true;
int max_index = row_expanded.front()->get();
- for (Set<int>::Element *E = row_expanded.front(); E; E = E->next()) {
+ for (RBSet<int>::Element *E = row_expanded.front(); E; E = E->next()) {
if (row_minh[E->get()] > row_minh[max_index]) {
max_index = E->get();
}
@@ -139,13 +142,47 @@ void GridContainer::_notification(int p_what) {
}
// Finally, fit the nodes.
- int col_expand = col_expanded.size() > 0 ? remaining_space.width / col_expanded.size() : 0;
- int row_expand = row_expanded.size() > 0 ? remaining_space.height / row_expanded.size() : 0;
+ int col_remaining_pixel = 0;
+ int col_expand = 0;
+ if (col_expanded.size() > 0) {
+ col_expand = remaining_space.width / col_expanded.size();
+ col_remaining_pixel = remaining_space.width - col_expanded.size() * col_expand;
+ }
+
+ int row_remaining_pixel = 0;
+ int row_expand = 0;
+ if (row_expanded.size() > 0) {
+ row_expand = remaining_space.height / row_expanded.size();
+ row_remaining_pixel = remaining_space.height - row_expanded.size() * row_expand;
+ }
+
bool rtl = is_layout_rtl();
int col_ofs = 0;
int row_ofs = 0;
+ // Calculate the index of rows and columns that receive the remaining pixel.
+ int col_remaining_pixel_index = 0;
+ for (int i = 0; i < max_col; i++) {
+ if (col_remaining_pixel == 0) {
+ break;
+ }
+ if (col_expanded.has(i)) {
+ col_remaining_pixel_index = i + 1;
+ col_remaining_pixel--;
+ }
+ }
+ int row_remaining_pixel_index = 0;
+ for (int i = 0; i < max_row; i++) {
+ if (row_remaining_pixel == 0) {
+ break;
+ }
+ if (row_expanded.has(i)) {
+ row_remaining_pixel_index = i + 1;
+ row_remaining_pixel--;
+ }
+ }
+
valid_controls_index = 0;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
@@ -164,26 +201,40 @@ void GridContainer::_notification(int p_what) {
}
if (row > 0) {
row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + vsep;
+
+ if (row_expanded.has(row - 1) && row - 1 < row_remaining_pixel_index) {
+ // Apply the remaining pixel of the previous row.
+ row_ofs++;
+ }
}
}
+ Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]);
+
+ // Add the remaining pixel to the expanding columns and rows, starting from left and top.
+ if (col_expanded.has(col) && col < col_remaining_pixel_index) {
+ s.x++;
+ }
+ if (row_expanded.has(row) && row < row_remaining_pixel_index) {
+ s.y++;
+ }
+
if (rtl) {
- Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]);
Point2 p(col_ofs - s.width, row_ofs);
fit_child_in_rect(c, Rect2(p, s));
col_ofs -= s.width + hsep;
} else {
Point2 p(col_ofs, row_ofs);
- Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]);
fit_child_in_rect(c, Rect2(p, s));
col_ofs += s.width + hsep;
}
}
-
} break;
+
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
queue_sort();
@@ -195,7 +246,7 @@ void GridContainer::set_columns(int p_columns) {
ERR_FAIL_COND(p_columns < 1);
columns = p_columns;
queue_sort();
- minimum_size_changed();
+ update_minimum_size();
}
int GridContainer::get_columns() const {
@@ -210,11 +261,11 @@ void GridContainer::_bind_methods() {
}
Size2 GridContainer::get_minimum_size() const {
- Map<int, int> col_minw;
- Map<int, int> row_minh;
+ HashMap<int, int> col_minw;
+ HashMap<int, int> row_minh;
- int hsep = get_theme_constant(SNAME("hseparation"));
- int vsep = get_theme_constant(SNAME("vseparation"));
+ int hsep = get_theme_constant(SNAME("h_separation"));
+ int vsep = get_theme_constant(SNAME("v_separation"));
int max_row = 0;
int max_col = 0;
diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h
index 9b43a5bc7e..9d77f90ab3 100644
--- a/scene/gui/grid_container.h
+++ b/scene/gui/grid_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index c0f2cdbef1..8b22f3722a 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,7 +43,7 @@ void ItemList::_shape(int p_idx) {
} else {
item.text_buf->set_direction((TextServer::Direction)item.text_direction);
}
- item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.opentype_features, (item.language != "") ? item.language : TranslationServer::get_singleton()->get_tool_locale());
+ item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.opentype_features, (!item.language.is_empty()) ? item.language : TranslationServer::get_singleton()->get_tool_locale());
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
item.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND);
} else {
@@ -83,6 +83,9 @@ int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
}
void ItemList::set_item_text(int p_idx, const String &p_text) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].text = p_text;
@@ -97,6 +100,9 @@ String ItemList::get_item_text(int p_idx) const {
}
void ItemList::set_item_text_direction(int p_idx, Control::TextDirection p_text_direction) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (items[p_idx].text_direction != p_text_direction) {
@@ -119,6 +125,9 @@ void ItemList::clear_item_opentype_features(int p_idx) {
}
void ItemList::set_item_opentype_feature(int p_idx, const String &p_name, int p_value) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
int32_t tag = TS->name_to_tag(p_name);
if (!items[p_idx].opentype_features.has(tag) || (int)items[p_idx].opentype_features[tag] != p_value) {
@@ -138,6 +147,9 @@ int ItemList::get_item_opentype_feature(int p_idx, const String &p_name) const {
}
void ItemList::set_item_language(int p_idx, const String &p_language) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].language != p_language) {
items.write[p_idx].language = p_language;
@@ -152,6 +164,9 @@ String ItemList::get_item_language(int p_idx) const {
}
void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tooltip_enabled = p_enabled;
}
@@ -162,6 +177,9 @@ bool ItemList::is_item_tooltip_enabled(int p_idx) const {
}
void ItemList::set_item_tooltip(int p_idx, const String &p_tooltip) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tooltip = p_tooltip;
@@ -175,6 +193,9 @@ String ItemList::get_item_tooltip(int p_idx) const {
}
void ItemList::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon = p_icon;
@@ -189,6 +210,9 @@ Ref<Texture2D> ItemList::get_item_icon(int p_idx) const {
}
void ItemList::set_item_icon_transposed(int p_idx, const bool p_transposed) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon_transposed = p_transposed;
@@ -203,6 +227,9 @@ bool ItemList::is_item_icon_transposed(int p_idx) const {
}
void ItemList::set_item_icon_region(int p_idx, const Rect2 &p_region) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon_region = p_region;
@@ -217,6 +244,9 @@ Rect2 ItemList::get_item_icon_region(int p_idx) const {
}
void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon_modulate = p_modulate;
@@ -230,6 +260,9 @@ Color ItemList::get_item_icon_modulate(int p_idx) const {
}
void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_color) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].custom_bg = p_custom_bg_color;
@@ -243,6 +276,9 @@ Color ItemList::get_item_custom_bg_color(int p_idx) const {
}
void ItemList::set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_color) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].custom_fg = p_custom_fg_color;
@@ -256,6 +292,9 @@ Color ItemList::get_item_custom_fg_color(int p_idx) const {
}
void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tag_icon = p_tag_icon;
@@ -270,6 +309,9 @@ Ref<Texture2D> ItemList::get_item_tag_icon(int p_idx) const {
}
void ItemList::set_item_selectable(int p_idx, bool p_selectable) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].selectable = p_selectable;
@@ -281,6 +323,9 @@ bool ItemList::is_item_selectable(int p_idx) const {
}
void ItemList::set_item_disabled(int p_idx, bool p_disabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].disabled = p_disabled;
@@ -293,6 +338,9 @@ bool ItemList::is_item_disabled(int p_idx) const {
}
void ItemList::set_item_metadata(int p_idx, const Variant &p_metadata) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].metadata = p_metadata;
@@ -381,7 +429,7 @@ void ItemList::move_item(int p_from_idx, int p_to_idx) {
}
Item item = items[p_from_idx];
- items.remove(p_from_idx);
+ items.remove_at(p_from_idx);
items.insert(p_to_idx, item);
update();
@@ -404,7 +452,7 @@ int ItemList::get_item_count() const {
void ItemList::remove_item(int p_idx) {
ERR_FAIL_INDEX(p_idx, items.size());
- items.remove(p_idx);
+ items.remove_at(p_idx);
if (current == p_idx) {
current = -1;
}
@@ -537,6 +585,9 @@ Size2 ItemList::Item::get_icon_size() const {
void ItemList::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
+#define CAN_SELECT(i) (items[i].selectable && !items[i].disabled)
+#define IS_SAME_ROW(i, row) (i / current_columns == row)
+
double prev_scroll = scroll_bar->get_value();
Ref<InputEventMouseMotion> mm = p_event;
@@ -555,7 +606,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (mb.is_valid() && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT)) && mb->is_pressed()) {
+ if (mb.is_valid() && mb->is_pressed()) {
search_string = ""; //any mousepress cancels
Vector2 pos = mb->get_position();
Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
@@ -580,7 +631,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (closest != -1) {
+ if (closest != -1 && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT))) {
int i = closest;
if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_pressed()) {
@@ -594,55 +645,47 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
SWAP(from, to);
}
for (int j = from; j <= to; j++) {
+ if (!CAN_SELECT(j)) {
+ continue;
+ }
bool selected = !items[j].selected;
select(j, false);
if (selected) {
emit_signal(SNAME("multi_selected"), j, true);
}
}
+ emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index());
- if (mb->get_button_index() == MouseButton::RIGHT) {
- emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
- }
} else {
if (!mb->is_double_click() && !mb->is_command_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) {
defer_select_single = i;
return;
}
- if (items[i].selected && mb->get_button_index() == MouseButton::RIGHT) {
- emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
- } else {
- bool selected = items[i].selected;
-
+ if (!items[i].selected || allow_reselect) {
select(i, select_mode == SELECT_SINGLE || !mb->is_command_pressed());
- if (!selected || allow_reselect) {
- if (select_mode == SELECT_SINGLE) {
- emit_signal(SNAME("item_selected"), i);
- } else {
- emit_signal(SNAME("multi_selected"), i, true);
- }
+ if (select_mode == SELECT_SINGLE) {
+ emit_signal(SNAME("item_selected"), i);
+ } else {
+ emit_signal(SNAME("multi_selected"), i, true);
}
+ }
- if (mb->get_button_index() == MouseButton::RIGHT) {
- emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
- } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_double_click()) {
- emit_signal(SNAME("item_activated"), i);
- }
+ emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index());
+
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
+ emit_signal(SNAME("item_activated"), i);
}
}
return;
+ } else if (closest != -1) {
+ emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index());
+ } else {
+ // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting.
+ emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index());
}
- if (mb->get_button_index() == MouseButton::RIGHT) {
- emit_signal(SNAME("rmb_clicked"), mb->get_position());
-
- return;
- }
-
- // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting.
- emit_signal(SNAME("nothing_selected"));
}
if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
scroll_bar->set_value(scroll_bar->get_value() - scroll_bar->get_page() * mb->get_factor() / 8);
@@ -653,13 +696,13 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (p_event->is_pressed() && items.size() > 0) {
if (p_event->is_action("ui_up")) {
- if (search_string != "") {
+ if (!search_string.is_empty()) {
uint64_t now = OS::get_singleton()->get_ticks_msec();
uint64_t diff = now - search_time_msec;
if (diff < uint64_t(ProjectSettings::get_singleton()->get("gui/timers/incremental_search_max_interval_msec")) * 2) {
for (int i = current - 1; i >= 0; i--) {
- if (items[i].text.begins_with(search_string)) {
+ if (CAN_SELECT(i) && items[i].text.begins_with(search_string)) {
set_current(i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -675,7 +718,15 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
if (current >= current_columns) {
- set_current(current - current_columns);
+ int next = current - current_columns;
+ while (next >= 0 && !CAN_SELECT(next)) {
+ next = next - current_columns;
+ }
+ if (next < 0) {
+ accept_event();
+ return;
+ }
+ set_current(next);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
emit_signal(SNAME("item_selected"), current);
@@ -683,13 +734,13 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
accept_event();
}
} else if (p_event->is_action("ui_down")) {
- if (search_string != "") {
+ if (!search_string.is_empty()) {
uint64_t now = OS::get_singleton()->get_ticks_msec();
uint64_t diff = now - search_time_msec;
if (diff < uint64_t(ProjectSettings::get_singleton()->get("gui/timers/incremental_search_max_interval_msec")) * 2) {
for (int i = current + 1; i < items.size(); i++) {
- if (items[i].text.begins_with(search_string)) {
+ if (CAN_SELECT(i) && items[i].text.begins_with(search_string)) {
set_current(i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -704,7 +755,15 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
if (current < items.size() - current_columns) {
- set_current(current + current_columns);
+ int next = current + current_columns;
+ while (next < items.size() && !CAN_SELECT(next)) {
+ next = next + current_columns;
+ }
+ if (next >= items.size()) {
+ accept_event();
+ return;
+ }
+ set_current(next);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
emit_signal(SNAME("item_selected"), current);
@@ -715,7 +774,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
for (int i = 4; i > 0; i--) {
- if (current - current_columns * i >= 0) {
+ if (current - current_columns * i >= 0 && CAN_SELECT(current - current_columns * i)) {
set_current(current - current_columns * i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -729,7 +788,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
for (int i = 4; i > 0; i--) {
- if (current + current_columns * i < items.size()) {
+ if (current + current_columns * i < items.size() && CAN_SELECT(current + current_columns * i)) {
set_current(current + current_columns * i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -744,7 +803,16 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
if (current % current_columns != 0) {
- set_current(current - 1);
+ int current_row = current / current_columns;
+ int next = current - 1;
+ while (!CAN_SELECT(next)) {
+ next = next - 1;
+ }
+ if (next < 0 || !IS_SAME_ROW(next, current_row)) {
+ accept_event();
+ return;
+ }
+ set_current(next);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
emit_signal(SNAME("item_selected"), current);
@@ -755,7 +823,16 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
if (current % current_columns != (current_columns - 1) && current + 1 < items.size()) {
- set_current(current + 1);
+ int current_row = current / current_columns;
+ int next = current + 1;
+ while (!CAN_SELECT(next)) {
+ next = next + 1;
+ }
+ if (items.size() <= next || !IS_SAME_ROW(next, current_row)) {
+ accept_event();
+ return;
+ }
+ set_current(next);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
emit_signal(SNAME("item_selected"), current);
@@ -831,6 +908,9 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (scroll_bar->get_value() != prev_scroll) {
accept_event(); //accept event if scroll changed
}
+
+#undef CAN_SELECT
+#undef IS_SAME_ROW
}
void ItemList::ensure_current_is_visible() {
@@ -855,445 +935,451 @@ static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) {
}
void ItemList::_notification(int p_what) {
- if (p_what == NOTIFICATION_RESIZED) {
- shape_changed = true;
- update();
- }
+ switch (p_what) {
+ case NOTIFICATION_RESIZED: {
+ shape_changed = true;
+ update();
+ } break;
+
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED:
+ case NOTIFICATION_THEME_CHANGED: {
+ for (int i = 0; i < items.size(); i++) {
+ _shape(i);
+ }
+ shape_changed = true;
+ update();
+ } break;
- if ((p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) || (p_what == NOTIFICATION_TRANSLATION_CHANGED) || (p_what == NOTIFICATION_THEME_CHANGED)) {
- for (int i = 0; i < items.size(); i++) {
- _shape(i);
- }
- shape_changed = true;
- update();
- }
+ case NOTIFICATION_DRAW: {
+ Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
- if (p_what == NOTIFICATION_DRAW) {
- Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
+ int mw = scroll_bar->get_minimum_size().x;
+ scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw);
+ scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP));
+ scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM));
- int mw = scroll_bar->get_minimum_size().x;
- scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw);
- scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP));
- scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM));
+ Size2 size = get_size();
- Size2 size = get_size();
+ int width = size.width - bg->get_minimum_size().width;
+ if (scroll_bar->is_visible()) {
+ width -= mw;
+ }
- int width = size.width - bg->get_minimum_size().width;
- if (scroll_bar->is_visible()) {
- width -= mw;
- }
+ draw_style_box(bg, Rect2(Point2(), size));
- draw_style_box(bg, Rect2(Point2(), size));
+ int hseparation = get_theme_constant(SNAME("h_separation"));
+ int vseparation = get_theme_constant(SNAME("v_separation"));
+ int icon_margin = get_theme_constant(SNAME("icon_margin"));
+ int line_separation = get_theme_constant(SNAME("line_separation"));
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
- int hseparation = get_theme_constant(SNAME("hseparation"));
- int vseparation = get_theme_constant(SNAME("vseparation"));
- int icon_margin = get_theme_constant(SNAME("icon_margin"));
- int line_separation = get_theme_constant(SNAME("line_separation"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
+ Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected"));
+ Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused"));
+ bool rtl = is_layout_rtl();
- Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected"));
- Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused"));
- bool rtl = is_layout_rtl();
+ Color guide_color = get_theme_color(SNAME("guide_color"));
+ Color font_color = get_theme_color(SNAME("font_color"));
+ Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
- Color guide_color = get_theme_color(SNAME("guide_color"));
- Color font_color = get_theme_color(SNAME("font_color"));
- Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
+ if (has_focus()) {
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
+ draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size));
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false);
+ }
- if (has_focus()) {
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
- draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size));
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false);
- }
+ if (shape_changed) {
+ float max_column_width = 0.0;
- if (shape_changed) {
- float max_column_width = 0.0;
+ //1- compute item minimum sizes
+ for (int i = 0; i < items.size(); i++) {
+ Size2 minsize;
+ if (items[i].icon.is_valid()) {
+ if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
+ minsize = fixed_icon_size * icon_scale;
+ } else {
+ minsize = items[i].get_icon_size() * icon_scale;
+ }
- //1- compute item minimum sizes
- for (int i = 0; i < items.size(); i++) {
- Size2 minsize;
- if (items[i].icon.is_valid()) {
- if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
- minsize = fixed_icon_size * icon_scale;
- } else {
- minsize = items[i].get_icon_size() * icon_scale;
+ if (!items[i].text.is_empty()) {
+ if (icon_mode == ICON_MODE_TOP) {
+ minsize.y += icon_margin;
+ } else {
+ minsize.x += icon_margin;
+ }
+ }
}
- if (items[i].text != "") {
- if (icon_mode == ICON_MODE_TOP) {
- minsize.y += icon_margin;
- } else {
- minsize.x += icon_margin;
+ if (!items[i].text.is_empty()) {
+ int max_width = -1;
+ if (fixed_column_width) {
+ max_width = fixed_column_width;
+ } else if (same_column_width) {
+ max_width = items[i].rect_cache.size.x;
}
- }
- }
+ items.write[i].text_buf->set_width(max_width);
+ Size2 s = items[i].text_buf->get_size();
- if (items[i].text != "") {
- int max_width = -1;
- if (fixed_column_width) {
- max_width = fixed_column_width;
- } else if (same_column_width) {
- max_width = items[i].rect_cache.size.x;
- }
- items.write[i].text_buf->set_width(max_width);
- Size2 s = items[i].text_buf->get_size();
+ if (icon_mode == ICON_MODE_TOP) {
+ minsize.x = MAX(minsize.x, s.width);
+ if (max_text_lines > 0) {
+ minsize.y += s.height + line_separation * max_text_lines;
+ } else {
+ minsize.y += s.height;
+ }
- if (icon_mode == ICON_MODE_TOP) {
- minsize.x = MAX(minsize.x, s.width);
- if (max_text_lines > 0) {
- minsize.y += s.height + line_separation * max_text_lines;
} else {
- minsize.y += s.height;
+ minsize.y = MAX(minsize.y, s.height);
+ minsize.x += s.width;
}
+ }
- } else {
- minsize.y = MAX(minsize.y, s.height);
- minsize.x += s.width;
+ if (fixed_column_width > 0) {
+ minsize.x = fixed_column_width;
}
+ max_column_width = MAX(max_column_width, minsize.x);
+
+ // elements need to adapt to the selected size
+ minsize.y += vseparation;
+ minsize.x += hseparation;
+ items.write[i].rect_cache.size = minsize;
+ items.write[i].min_rect_cache.size = minsize;
}
- if (fixed_column_width > 0) {
- minsize.x = fixed_column_width;
+ int fit_size = size.x - bg->get_minimum_size().width - mw;
+
+ //2-attempt best fit
+ current_columns = 0x7FFFFFFF;
+ if (max_columns > 0) {
+ current_columns = max_columns;
}
- max_column_width = MAX(max_column_width, minsize.x);
- // elements need to adapt to the selected size
- minsize.y += vseparation;
- minsize.x += hseparation;
- items.write[i].rect_cache.size = minsize;
- items.write[i].min_rect_cache.size = minsize;
- }
+ while (true) {
+ //repeat until all fits
+ bool all_fit = true;
+ Vector2 ofs;
+ int col = 0;
+ int max_h = 0;
+ separators.clear();
+ for (int i = 0; i < items.size(); i++) {
+ if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) {
+ //went past
+ current_columns = MAX(col, 1);
+ all_fit = false;
+ break;
+ }
- int fit_size = size.x - bg->get_minimum_size().width - mw;
+ if (same_column_width) {
+ items.write[i].rect_cache.size.x = max_column_width;
+ }
+ items.write[i].rect_cache.position = ofs;
+ max_h = MAX(max_h, items[i].rect_cache.size.y);
+ ofs.x += items[i].rect_cache.size.x + hseparation;
+ col++;
+ if (col == current_columns) {
+ if (i < items.size() - 1) {
+ separators.push_back(ofs.y + max_h + vseparation / 2);
+ }
- //2-attempt best fit
- current_columns = 0x7FFFFFFF;
- if (max_columns > 0) {
- current_columns = max_columns;
- }
+ for (int j = i; j >= 0 && col > 0; j--, col--) {
+ items.write[j].rect_cache.size.y = max_h;
+ }
- while (true) {
- //repeat until all fits
- bool all_fit = true;
- Vector2 ofs;
- int col = 0;
- int max_h = 0;
- separators.clear();
- for (int i = 0; i < items.size(); i++) {
- if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) {
- //went past
- current_columns = MAX(col, 1);
- all_fit = false;
- break;
+ ofs.x = 0;
+ ofs.y += max_h + vseparation;
+ col = 0;
+ max_h = 0;
+ }
}
- if (same_column_width) {
- items.write[i].rect_cache.size.x = max_column_width;
+ for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) {
+ items.write[j].rect_cache.size.y = max_h;
}
- items.write[i].rect_cache.position = ofs;
- max_h = MAX(max_h, items[i].rect_cache.size.y);
- ofs.x += items[i].rect_cache.size.x + hseparation;
- col++;
- if (col == current_columns) {
- if (i < items.size() - 1) {
- separators.push_back(ofs.y + max_h + vseparation / 2);
- }
- for (int j = i; j >= 0 && col > 0; j--, col--) {
- items.write[j].rect_cache.size.y = max_h;
+ if (all_fit) {
+ float page = MAX(0, size.height - bg->get_minimum_size().height);
+ float max = MAX(page, ofs.y + max_h);
+ if (auto_height) {
+ auto_height_value = ofs.y + max_h + bg->get_minimum_size().height;
}
+ scroll_bar->set_max(max);
+ scroll_bar->set_page(page);
+ if (max <= page) {
+ scroll_bar->set_value(0);
+ scroll_bar->hide();
+ } else {
+ scroll_bar->show();
- ofs.x = 0;
- ofs.y += max_h + vseparation;
- col = 0;
- max_h = 0;
+ if (do_autoscroll_to_bottom) {
+ scroll_bar->set_value(max);
+ }
+ }
+ break;
}
}
- for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) {
- items.write[j].rect_cache.size.y = max_h;
- }
+ update_minimum_size();
+ shape_changed = false;
+ }
- if (all_fit) {
- float page = MAX(0, size.height - bg->get_minimum_size().height);
- float max = MAX(page, ofs.y + max_h);
- if (auto_height) {
- auto_height_value = ofs.y + max_h + bg->get_minimum_size().height;
- }
- scroll_bar->set_max(max);
- scroll_bar->set_page(page);
- if (max <= page) {
- scroll_bar->set_value(0);
- scroll_bar->hide();
- } else {
- scroll_bar->show();
+ //ensure_selected_visible needs to be checked before we draw the list.
+ if (ensure_selected_visible && current >= 0 && current < items.size()) {
+ Rect2 r = items[current].rect_cache;
+ int from = scroll_bar->get_value();
+ int to = from + scroll_bar->get_page();
- if (do_autoscroll_to_bottom) {
- scroll_bar->set_value(max);
- }
- }
- break;
+ if (r.position.y < from) {
+ scroll_bar->set_value(r.position.y);
+ } else if (r.position.y + r.size.y > to) {
+ scroll_bar->set_value(r.position.y + r.size.y - (to - from));
}
}
- minimum_size_changed();
- shape_changed = false;
- }
-
- //ensure_selected_visible needs to be checked before we draw the list.
- if (ensure_selected_visible && current >= 0 && current < items.size()) {
- Rect2 r = items[current].rect_cache;
- int from = scroll_bar->get_value();
- int to = from + scroll_bar->get_page();
+ ensure_selected_visible = false;
- if (r.position.y < from) {
- scroll_bar->set_value(r.position.y);
- } else if (r.position.y + r.size.y > to) {
- scroll_bar->set_value(r.position.y + r.size.y - (to - from));
- }
- }
+ Vector2 base_ofs = bg->get_offset();
+ base_ofs.y -= int(scroll_bar->get_value());
- ensure_selected_visible = false;
+ const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there
- Vector2 base_ofs = bg->get_offset();
- base_ofs.y -= int(scroll_bar->get_value());
-
- const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there
-
- int first_item_visible;
- {
- // do a binary search to find the first item whose rect reaches below clip.position.y
- int lo = 0;
- int hi = items.size();
- while (lo < hi) {
- const int mid = (lo + hi) / 2;
- const Rect2 &rcache = items[mid].rect_cache;
- if (rcache.position.y + rcache.size.y < clip.position.y) {
- lo = mid + 1;
- } else {
- hi = mid;
+ int first_item_visible;
+ {
+ // do a binary search to find the first item whose rect reaches below clip.position.y
+ int lo = 0;
+ int hi = items.size();
+ while (lo < hi) {
+ const int mid = (lo + hi) / 2;
+ const Rect2 &rcache = items[mid].rect_cache;
+ if (rcache.position.y + rcache.size.y < clip.position.y) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
}
- }
- // we might have ended up with column 2, or 3, ..., so let's find the first column
- while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) {
- lo -= 1;
- }
- first_item_visible = lo;
- }
-
- for (int i = first_item_visible; i < items.size(); i++) {
- Rect2 rcache = items[i].rect_cache;
-
- if (rcache.position.y > clip.position.y + clip.size.y) {
- break; // done
+ // we might have ended up with column 2, or 3, ..., so let's find the first column
+ while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) {
+ lo -= 1;
+ }
+ first_item_visible = lo;
}
- if (!clip.intersects(rcache)) {
- continue;
- }
+ for (int i = first_item_visible; i < items.size(); i++) {
+ Rect2 rcache = items[i].rect_cache;
- if (current_columns == 1) {
- rcache.size.width = width - rcache.position.x;
- }
+ if (rcache.position.y > clip.position.y + clip.size.y) {
+ break; // done
+ }
- if (items[i].selected) {
- Rect2 r = rcache;
- r.position += base_ofs;
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
-
- if (rtl) {
- r.position.x = size.width - r.position.x - r.size.x;
+ if (!clip.intersects(rcache)) {
+ continue;
}
- draw_style_box(sbsel, r);
- }
- if (items[i].custom_bg.a > 0.001) {
- Rect2 r = rcache;
- r.position += base_ofs;
-
- // Size rect to make the align the temperature colors
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
-
- if (rtl) {
- r.position.x = size.width - r.position.x - r.size.x;
+ if (current_columns == 1) {
+ rcache.size.width = width - rcache.position.x;
}
- draw_rect(r, items[i].custom_bg);
- }
+ if (items[i].selected) {
+ Rect2 r = rcache;
+ r.position += base_ofs;
+ r.position.y -= vseparation / 2;
+ r.size.y += vseparation;
+ r.position.x -= hseparation / 2;
+ r.size.x += hseparation;
- Vector2 text_ofs;
- if (items[i].icon.is_valid()) {
- Size2 icon_size;
- //= _adjust_to_max_size(items[i].get_icon_size(),fixed_icon_size) * icon_scale;
+ if (rtl) {
+ r.position.x = size.width - r.position.x - r.size.x;
+ }
- if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
- icon_size = fixed_icon_size * icon_scale;
- } else {
- icon_size = items[i].get_icon_size() * icon_scale;
+ draw_style_box(sbsel, r);
}
+ if (items[i].custom_bg.a > 0.001) {
+ Rect2 r = rcache;
+ r.position += base_ofs;
- Vector2 icon_ofs;
+ // Size rect to make the align the temperature colors
+ r.position.y -= vseparation / 2;
+ r.size.y += vseparation;
+ r.position.x -= hseparation / 2;
+ r.size.x += hseparation;
- Point2 pos = items[i].rect_cache.position + icon_ofs + base_ofs;
+ if (rtl) {
+ r.position.x = size.width - r.position.x - r.size.x;
+ }
- if (icon_mode == ICON_MODE_TOP) {
- pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2);
- pos.y += icon_margin;
- text_ofs.y = icon_size.height + icon_margin * 2;
- } else {
- pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2);
- text_ofs.x = icon_size.width + icon_margin;
+ draw_rect(r, items[i].custom_bg);
}
- Rect2 draw_rect = Rect2(pos, icon_size);
+ Vector2 text_ofs;
+ if (items[i].icon.is_valid()) {
+ Size2 icon_size;
+ //= _adjust_to_max_size(items[i].get_icon_size(),fixed_icon_size) * icon_scale;
- if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
- Rect2 adj = _adjust_to_max_size(items[i].get_icon_size() * icon_scale, icon_size);
- draw_rect.position += adj.position;
- draw_rect.size = adj.size;
- }
+ if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
+ icon_size = fixed_icon_size * icon_scale;
+ } else {
+ icon_size = items[i].get_icon_size() * icon_scale;
+ }
- Color modulate = items[i].icon_modulate;
- if (items[i].disabled) {
- modulate.a *= 0.5;
- }
+ Vector2 icon_ofs;
- // If the icon is transposed, we have to switch the size so that it is drawn correctly
- if (items[i].icon_transposed) {
- Size2 size_tmp = draw_rect.size;
- draw_rect.size.x = size_tmp.y;
- draw_rect.size.y = size_tmp.x;
- }
+ Point2 pos = items[i].rect_cache.position + icon_ofs + base_ofs;
- Rect2 region = (items[i].icon_region.size.x == 0 || items[i].icon_region.size.y == 0) ? Rect2(Vector2(), items[i].icon->get_size()) : Rect2(items[i].icon_region);
+ if (icon_mode == ICON_MODE_TOP) {
+ pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2);
+ pos.y += icon_margin;
+ text_ofs.y = icon_size.height + icon_margin * 2;
+ } else {
+ pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2);
+ text_ofs.x = icon_size.width + icon_margin;
+ }
- if (rtl) {
- draw_rect.position.x = size.width - draw_rect.position.x - draw_rect.size.x;
- }
- draw_texture_rect_region(items[i].icon, draw_rect, region, modulate, items[i].icon_transposed);
- }
+ Rect2 draw_rect = Rect2(pos, icon_size);
- if (items[i].tag_icon.is_valid()) {
- Point2 draw_pos = items[i].rect_cache.position;
- if (rtl) {
- draw_pos.x = size.width - draw_pos.x - items[i].tag_icon->get_width();
- }
- draw_texture(items[i].tag_icon, draw_pos + base_ofs);
- }
+ if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
+ Rect2 adj = _adjust_to_max_size(items[i].get_icon_size() * icon_scale, icon_size);
+ draw_rect.position += adj.position;
+ draw_rect.size = adj.size;
+ }
- if (items[i].text != "") {
- int max_len = -1;
+ Color modulate = items[i].icon_modulate;
+ if (items[i].disabled) {
+ modulate.a *= 0.5;
+ }
- Vector2 size2 = items[i].text_buf->get_size();
- if (fixed_column_width) {
- max_len = fixed_column_width;
- } else if (same_column_width) {
- max_len = items[i].rect_cache.size.x;
- } else {
- max_len = size2.x;
- }
+ // If the icon is transposed, we have to switch the size so that it is drawn correctly
+ if (items[i].icon_transposed) {
+ Size2 size_tmp = draw_rect.size;
+ draw_rect.size.x = size_tmp.y;
+ draw_rect.size.y = size_tmp.x;
+ }
- Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color);
- if (items[i].disabled) {
- modulate.a *= 0.5;
- }
+ Rect2 region = (items[i].icon_region.size.x == 0 || items[i].icon_region.size.y == 0) ? Rect2(Vector2(), items[i].icon->get_size()) : Rect2(items[i].icon_region);
- if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
- text_ofs += base_ofs;
- text_ofs += items[i].rect_cache.position;
+ if (rtl) {
+ draw_rect.position.x = size.width - draw_rect.position.x - draw_rect.size.x;
+ }
+ draw_texture_rect_region(items[i].icon, draw_rect, region, modulate, items[i].icon_transposed);
+ }
+ if (items[i].tag_icon.is_valid()) {
+ Point2 draw_pos = items[i].rect_cache.position;
if (rtl) {
- text_ofs.x = size.width - text_ofs.x - max_len;
+ draw_pos.x = size.width - draw_pos.x - items[i].tag_icon->get_width();
}
+ draw_texture(items[i].tag_icon, draw_pos + base_ofs);
+ }
- items.write[i].text_buf->set_align(HALIGN_CENTER);
+ if (!items[i].text.is_empty()) {
+ int max_len = -1;
- if (outline_size > 0 && font_outline_color.a > 0) {
- items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ Vector2 size2 = items[i].text_buf->get_size();
+ if (fixed_column_width) {
+ max_len = fixed_column_width;
+ } else if (same_column_width) {
+ max_len = items[i].rect_cache.size.x;
+ } else {
+ max_len = size2.x;
}
- items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
- } else {
- if (fixed_column_width > 0) {
- size2.x = MIN(size2.x, fixed_column_width);
+ Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color);
+ if (items[i].disabled) {
+ modulate.a *= 0.5;
}
- if (icon_mode == ICON_MODE_TOP) {
- text_ofs.x += (items[i].rect_cache.size.width - size2.x) / 2;
- } else {
- text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2;
- }
+ if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
+ text_ofs += base_ofs;
+ text_ofs += items[i].rect_cache.position;
- text_ofs += base_ofs;
- text_ofs += items[i].rect_cache.position;
+ if (rtl) {
+ text_ofs.x = size.width - text_ofs.x - max_len;
+ }
- if (rtl) {
- text_ofs.x = size.width - text_ofs.x - max_len;
- }
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- items.write[i].text_buf->set_width(max_len);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ }
- if (rtl) {
- items.write[i].text_buf->set_align(HALIGN_RIGHT);
+ items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
} else {
- items.write[i].text_buf->set_align(HALIGN_LEFT);
+ if (fixed_column_width > 0) {
+ size2.x = MIN(size2.x, fixed_column_width);
+ }
+
+ if (icon_mode == ICON_MODE_TOP) {
+ text_ofs.x += (items[i].rect_cache.size.width - size2.x) / 2;
+ } else {
+ text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2;
+ }
+
+ text_ofs += base_ofs;
+ text_ofs += items[i].rect_cache.position;
+
+ if (rtl) {
+ text_ofs.x = size.width - text_ofs.x - max_len;
+ }
+
+ items.write[i].text_buf->set_width(width - text_ofs.x);
+
+ if (rtl) {
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ } else {
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT);
+ }
+
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ }
+
+ if (width - text_ofs.x > 0) {
+ items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
+ }
}
+ }
- if (outline_size > 0 && font_outline_color.a > 0) {
- items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ if (select_mode == SELECT_MULTI && i == current) {
+ Rect2 r = rcache;
+ r.position += base_ofs;
+ r.position.y -= vseparation / 2;
+ r.size.y += vseparation;
+ r.position.x -= hseparation / 2;
+ r.size.x += hseparation;
+
+ if (rtl) {
+ r.position.x = size.width - r.position.x - r.size.x;
}
- items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
+ draw_style_box(cursor, r);
}
}
- if (select_mode == SELECT_MULTI && i == current) {
- Rect2 r = rcache;
- r.position += base_ofs;
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
-
- if (rtl) {
- r.position.x = size.width - r.position.x - r.size.x;
+ int first_visible_separator = 0;
+ {
+ // do a binary search to find the first separator that is below clip_position.y
+ int lo = 0;
+ int hi = separators.size();
+ while (lo < hi) {
+ const int mid = (lo + hi) / 2;
+ if (separators[mid] < clip.position.y) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
}
-
- draw_style_box(cursor, r);
+ first_visible_separator = lo;
}
- }
- int first_visible_separator = 0;
- {
- // do a binary search to find the first separator that is below clip_position.y
- int lo = 0;
- int hi = separators.size();
- while (lo < hi) {
- const int mid = (lo + hi) / 2;
- if (separators[mid] < clip.position.y) {
- lo = mid + 1;
- } else {
- hi = mid;
+ for (int i = first_visible_separator; i < separators.size(); i++) {
+ if (separators[i] > clip.position.y + clip.size.y) {
+ break; // done
}
- }
- first_visible_separator = lo;
- }
- for (int i = first_visible_separator; i < separators.size(); i++) {
- if (separators[i] > clip.position.y + clip.size.y) {
- break; // done
+ const int y = base_ofs.y + separators[i];
+ draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color);
}
-
- const int y = base_ofs.y + separators[i];
- draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color);
- }
+ } break;
}
}
@@ -1360,10 +1446,10 @@ String ItemList::get_tooltip(const Point2 &p_pos) const {
if (!items[closest].tooltip_enabled) {
return "";
}
- if (items[closest].tooltip != "") {
+ if (!items[closest].tooltip.is_empty()) {
return items[closest].tooltip;
}
- if (items[closest].text != "") {
+ if (!items[closest].text.is_empty()) {
return items[closest].text;
}
}
@@ -1492,6 +1578,9 @@ bool ItemList::_set(const StringName &p_name, const Variant &p_value) {
} else if (components[1] == "disabled") {
set_item_disabled(item_index, p_value);
return true;
+ } else if (components[1] == "selectable") {
+ set_item_selectable(item_index, p_value);
+ return true;
}
}
#ifndef DISABLE_DEPRECATED
@@ -1528,6 +1617,9 @@ bool ItemList::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components[1] == "disabled") {
r_ret = is_item_disabled(item_index);
return true;
+ } else if (components[1] == "selectable") {
+ r_ret = is_item_selectable(item_index);
+ return true;
}
}
return false;
@@ -1541,6 +1633,10 @@ void ItemList::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/selectable", i));
+ pi.usage &= ~(is_item_selectable(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
@@ -1652,7 +1748,7 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("ensure_current_is_visible"), &ItemList::ensure_current_is_visible);
- ClassDB::bind_method(D_METHOD("get_v_scroll"), &ItemList::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &ItemList::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior);
@@ -1663,7 +1759,7 @@ void ItemList::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_text_lines", PROPERTY_HINT_RANGE, "1,10,1,or_greater"), "set_max_text_lines", "get_max_text_lines");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
- ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
+ ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_");
ADD_GROUP("Columns", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_columns", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), "set_max_columns", "get_max_columns");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "same_column_width"), "set_same_column_width", "is_same_column_width");
@@ -1680,11 +1776,10 @@ void ItemList::_bind_methods() {
BIND_ENUM_CONSTANT(SELECT_MULTI);
ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index")));
- ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position")));
+ ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index")));
+ ADD_SIGNAL(MethodInfo("item_clicked", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index")));
ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "selected")));
ADD_SIGNAL(MethodInfo("item_activated", PropertyInfo(Variant::INT, "index")));
- ADD_SIGNAL(MethodInfo("rmb_clicked", PropertyInfo(Variant::VECTOR2, "at_position")));
- ADD_SIGNAL(MethodInfo("nothing_selected"));
GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000);
ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index e780179e7b..ffbe7d055a 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -62,7 +62,7 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
- bool selectable = false;
+ bool selectable = true;
bool selected = false;
bool disabled = false;
bool tooltip_enabled = true;
@@ -98,8 +98,8 @@ private:
SelectMode select_mode = SELECT_SINGLE;
IconMode icon_mode = ICON_MODE_LEFT;
- VScrollBar *scroll_bar;
- TextParagraph::OverrunBehavior text_overrun_behavior = TextParagraph::OVERRUN_NO_TRIMMING;
+ VScrollBar *scroll_bar = nullptr;
+ TextParagraph::OverrunBehavior text_overrun_behavior = TextParagraph::OVERRUN_TRIM_ELLIPSIS;
uint64_t search_time_msec = 0;
String search_string;
@@ -255,7 +255,7 @@ public:
void set_autoscroll_to_bottom(const bool p_enable);
- VScrollBar *get_v_scroll() { return scroll_bar; }
+ VScrollBar *get_v_scroll_bar() { return scroll_bar; }
ItemList();
~ItemList();
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 50908f6a77..eda3d40f63 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,7 +44,7 @@ void Label::set_autowrap_mode(Label::AutowrapMode p_mode) {
update();
if (clip || overrun_behavior != OVERRUN_NO_TRIMMING) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -82,8 +82,11 @@ void Label::_shape() {
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label"));
int width = (get_size().width - style->get_minimum_size().width);
- if (dirty) {
- TS->shaped_text_clear(text_rid);
+ if (dirty || font_dirty) {
+ String lang = (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale();
+ if (dirty) {
+ TS->shaped_text_clear(text_rid);
+ }
if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
} else {
@@ -92,19 +95,27 @@ void Label::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
- String text = (uppercase) ? xl_text.to_upper() : xl_text;
- if (visible_chars >= 0) {
+ String text = (uppercase) ? TS->string_to_upper(xl_text, lang) : xl_text;
+ if (visible_chars >= 0 && visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
text = text.substr(0, visible_chars);
}
- TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ if (dirty) {
+ TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang);
+ } else {
+ int spans = TS->shaped_get_span_count(text_rid);
+ for (int i = 0; i < spans; i++) {
+ TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features);
+ }
+ }
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text));
dirty = false;
+ font_dirty = false;
lines_dirty = true;
}
if (lines_dirty) {
for (int i = 0; i < lines_rid.size(); i++) {
- TS->free(lines_rid[i]);
+ TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
@@ -175,7 +186,7 @@ void Label::_shape() {
if (lines_hidden) {
overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS;
}
- if (align == ALIGN_FILL) {
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
for (int i = 0; i < lines_rid.size(); i++) {
if (i < visible_lines - 1 || lines_rid.size() == 1) {
TS->shaped_text_fit_to_width(lines_rid[i], width);
@@ -183,15 +194,13 @@ void Label::_shape() {
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
}
}
-
} else if (lines_hidden) {
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
}
-
} else {
// Autowrap disabled.
for (int i = 0; i < lines_rid.size(); i++) {
- if (align == ALIGN_FILL) {
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(lines_rid[i], width);
overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
@@ -207,7 +216,7 @@ void Label::_shape() {
_update_visible();
if (autowrap_mode == AUTOWRAP_OFF || !clip || overrun_behavior == OVERRUN_NO_TRIMMING) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -254,253 +263,288 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col
}
void Label::_notification(int p_what) {
- if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- String new_text = atr(text);
- if (new_text == xl_text) {
- return; // Nothing new.
- }
- xl_text = new_text;
- if (percent_visible < 1) {
- visible_chars = get_total_character_count() * percent_visible;
- }
- dirty = true;
+ switch (p_what) {
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ String new_text = atr(text);
+ if (new_text == xl_text) {
+ return; // Nothing new.
+ }
+ xl_text = new_text;
+ if (percent_visible < 1) {
+ visible_chars = get_total_character_count() * percent_visible;
+ }
+ dirty = true;
- update();
- }
+ update();
+ } break;
- if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) {
- update();
- }
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ update();
+ } break;
- if (p_what == NOTIFICATION_DRAW) {
- if (clip) {
- RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
- }
+ case NOTIFICATION_DRAW: {
+ if (clip) {
+ RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
+ }
- if (dirty || lines_dirty) {
- _shape();
- }
+ if (dirty || font_dirty || lines_dirty) {
+ _shape();
+ }
- RID ci = get_canvas_item();
-
- Size2 string_size;
- Size2 size = get_size();
- Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
- Ref<Font> font = get_theme_font(SNAME("font"));
- Color font_color = get_theme_color(SNAME("font_color"));
- Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
- Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
- int line_spacing = get_theme_constant(SNAME("line_spacing"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
- int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
- bool rtl = TS->shaped_text_get_direction(text_rid);
- bool rtl_layout = is_layout_rtl();
-
- style->draw(ci, Rect2(Point2(0, 0), get_size()));
-
- float total_h = 0.0;
- int lines_visible = 0;
-
- // Get number of lines to fit to the height.
- for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
- if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
- break;
+ RID ci = get_canvas_item();
+
+ Size2 string_size;
+ Size2 size = get_size();
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+ Ref<Font> font = get_theme_font(SNAME("font"));
+ Color font_color = get_theme_color(SNAME("font_color"));
+ Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
+ Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
+ int line_spacing = get_theme_constant(SNAME("line_spacing"));
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+ int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
+ bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL);
+ bool rtl_layout = is_layout_rtl();
+
+ style->draw(ci, Rect2(Point2(0, 0), get_size()));
+
+ float total_h = 0.0;
+ int lines_visible = 0;
+
+ // Get number of lines to fit to the height.
+ for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
+ break;
+ }
+ lines_visible++;
}
- lines_visible++;
- }
- if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
- lines_visible = max_lines_visible;
- }
+ if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
+ lines_visible = max_lines_visible;
+ }
- int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
+ int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
+ bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == VC_CHARS_AFTER_SHAPING);
+ bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_LTR) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && !rtl_layout));
+ bool trim_glyphs_rtl = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_RTL) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && rtl_layout));
+
+ // Get real total height.
+ int total_glyphs = 0;
+ total_h = 0;
+ for (int64_t i = lines_skipped; i < last_line; i++) {
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
+ }
+ int visible_glyphs = total_glyphs * percent_visible;
+ int processed_glyphs = 0;
+ total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM);
+
+ int vbegin = 0, vsep = 0;
+ if (lines_visible > 0) {
+ switch (vertical_alignment) {
+ case VERTICAL_ALIGNMENT_TOP: {
+ // Nothing.
+ } break;
+ case VERTICAL_ALIGNMENT_CENTER: {
+ vbegin = (size.y - (total_h - line_spacing)) / 2;
+ vsep = 0;
- // Get real total height.
- total_h = 0;
- for (int64_t i = lines_skipped; i < last_line; i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
- }
- total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM);
-
- int vbegin = 0, vsep = 0;
- if (lines_visible > 0) {
- switch (valign) {
- case VALIGN_TOP: {
- // Nothing.
- } break;
- case VALIGN_CENTER: {
- vbegin = (size.y - (total_h - line_spacing)) / 2;
- vsep = 0;
-
- } break;
- case VALIGN_BOTTOM: {
- vbegin = size.y - (total_h - line_spacing);
- vsep = 0;
-
- } break;
- case VALIGN_FILL: {
- vbegin = 0;
- if (lines_visible > 1) {
- vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1);
- } else {
+ } break;
+ case VERTICAL_ALIGNMENT_BOTTOM: {
+ vbegin = size.y - (total_h - line_spacing);
vsep = 0;
- }
- } break;
- }
- }
+ } break;
+ case VERTICAL_ALIGNMENT_FILL: {
+ vbegin = 0;
+ if (lines_visible > 1) {
+ vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1);
+ } else {
+ vsep = 0;
+ }
- Vector2 ofs;
- ofs.y = style->get_offset().y + vbegin;
- for (int i = lines_skipped; i < last_line; i++) {
- Size2 line_size = TS->shaped_text_get_size(lines_rid[i]);
- ofs.x = 0;
- ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP);
- switch (align) {
- case ALIGN_FILL:
- if (rtl && autowrap_mode != AUTOWRAP_OFF) {
- ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
- } else {
- ofs.x = style->get_offset().x;
- }
- break;
- case ALIGN_LEFT: {
- if (rtl_layout) {
- ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
- } else {
- ofs.x = style->get_offset().x;
- }
- } break;
- case ALIGN_CENTER: {
- ofs.x = int(size.width - line_size.width) / 2;
- } break;
- case ALIGN_RIGHT: {
- if (rtl_layout) {
- ofs.x = style->get_offset().x;
- } else {
- ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
- }
- } break;
+ } break;
+ }
}
- const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
- int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
-
- int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]);
- int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]);
-
- const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]);
- int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
+ Vector2 ofs;
+ ofs.y = style->get_offset().y + vbegin;
+ for (int i = lines_skipped; i < last_line; i++) {
+ Size2 line_size = TS->shaped_text_get_size(lines_rid[i]);
+ ofs.x = 0;
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP);
+ switch (horizontal_alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ if (rtl && autowrap_mode != AUTOWRAP_OFF) {
+ ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ } else {
+ ofs.x = style->get_offset().x;
+ }
+ break;
+ case HORIZONTAL_ALIGNMENT_LEFT: {
+ if (rtl_layout) {
+ ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ } else {
+ ofs.x = style->get_offset().x;
+ }
+ } break;
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ ofs.x = int(size.width - line_size.width) / 2;
+ } break;
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
+ if (rtl_layout) {
+ ofs.x = style->get_offset().x;
+ } else {
+ ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ }
+ } break;
+ }
- // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
- if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) {
- Vector2 offset = ofs;
- // Draw RTL ellipsis string when necessary.
- if (rtl && ellipsis_pos >= 0) {
- for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
- for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
- //Draw glyph outlines and shadow.
- draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
- offset.x += ellipsis_glyphs[gl_idx].advance;
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
+ int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
+
+ int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]);
+ int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]);
+
+ const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]);
+ int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
+
+ // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
+ int processed_glyphs_ol = processed_glyphs;
+ if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) {
+ Vector2 offset = ofs;
+ // Draw RTL ellipsis string when necessary.
+ if (rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
+ bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
+ //Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ }
+ processed_glyphs_ol++;
+ offset.x += ellipsis_glyphs[gl_idx].advance;
+ }
}
}
- }
- // Draw main text.
- for (int j = 0; j < gl_size; j++) {
- for (int k = 0; k < glyphs[j].repeat; k++) {
+ // Draw main text.
+ for (int j = 0; j < gl_size; j++) {
// Trim when necessary.
if (trim_pos >= 0) {
if (rtl) {
- if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ if (j < trim_pos) {
continue;
}
} else {
- if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ if (j >= trim_pos) {
break;
}
}
}
+ for (int k = 0; k < glyphs[j].repeat; k++) {
+ bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
- // Draw glyph outlines and shadow.
- draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
- offset.x += glyphs[j].advance;
+ // Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ }
+ processed_glyphs_ol++;
+ offset.x += glyphs[j].advance;
+ }
}
- }
- // Draw LTR ellipsis string when necessary.
- if (!rtl && ellipsis_pos >= 0) {
- for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
- for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
- //Draw glyph outlines and shadow.
- draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
- offset.x += ellipsis_glyphs[gl_idx].advance;
+ // Draw LTR ellipsis string when necessary.
+ if (!rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
+ bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
+ //Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ }
+ processed_glyphs_ol++;
+ offset.x += ellipsis_glyphs[gl_idx].advance;
+ }
}
}
}
- }
- // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps.
+ // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps.
- // Draw RTL ellipsis string when necessary.
- if (rtl && ellipsis_pos >= 0) {
- for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
- for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
- //Draw glyph outlines and shadow.
- draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
- ofs.x += ellipsis_glyphs[gl_idx].advance;
+ // Draw RTL ellipsis string when necessary.
+ if (rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
+ bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
+ //Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
+ }
+ processed_glyphs++;
+ ofs.x += ellipsis_glyphs[gl_idx].advance;
+ }
}
}
- }
- // Draw main text.
- for (int j = 0; j < gl_size; j++) {
- for (int k = 0; k < glyphs[j].repeat; k++) {
+ // Draw main text.
+ for (int j = 0; j < gl_size; j++) {
// Trim when necessary.
if (trim_pos >= 0) {
if (rtl) {
- if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ if (j < trim_pos) {
continue;
}
} else {
- if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ if (j >= trim_pos) {
break;
}
}
}
+ for (int k = 0; k < glyphs[j].repeat; k++) {
+ bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
- // Draw glyph outlines and shadow.
- draw_glyph(glyphs[j], ci, font_color, ofs);
- ofs.x += glyphs[j].advance;
+ // Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph(glyphs[j], ci, font_color, ofs);
+ }
+ processed_glyphs++;
+ ofs.x += glyphs[j].advance;
+ }
}
- }
- // Draw LTR ellipsis string when necessary.
- if (!rtl && ellipsis_pos >= 0) {
- for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
- for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
- //Draw glyph outlines and shadow.
- draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
- ofs.x += ellipsis_glyphs[gl_idx].advance;
+ // Draw LTR ellipsis string when necessary.
+ if (!rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
+ bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
+ //Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
+ }
+ processed_glyphs++;
+ ofs.x += ellipsis_glyphs[gl_idx].advance;
+ }
}
}
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM);
}
- ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM);
- }
- }
+ } break;
- if (p_what == NOTIFICATION_THEME_CHANGED) {
- dirty = true;
- update();
- }
- if (p_what == NOTIFICATION_RESIZED) {
- lines_dirty = true;
+ case NOTIFICATION_THEME_CHANGED: {
+ font_dirty = true;
+ update();
+ } break;
+
+ case NOTIFICATION_RESIZED: {
+ lines_dirty = true;
+ } break;
}
}
Size2 Label::get_minimum_size() const {
// don't want to mutable everything
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -524,7 +568,7 @@ int Label::get_line_count() const {
if (!is_inside_tree()) {
return 1;
}
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -556,29 +600,29 @@ int Label::get_visible_line_count() const {
return lines_visible;
}
-void Label::set_align(Align p_align) {
- ERR_FAIL_INDEX((int)p_align, 4);
- if (align != p_align) {
- if (align == ALIGN_FILL || p_align == ALIGN_FILL) {
+void Label::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ if (horizontal_alignment != p_alignment) {
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
lines_dirty = true; // Reshape lines.
}
- align = p_align;
+ horizontal_alignment = p_alignment;
}
update();
}
-Label::Align Label::get_align() const {
- return align;
+HorizontalAlignment Label::get_horizontal_alignment() const {
+ return horizontal_alignment;
}
-void Label::set_valign(VAlign p_align) {
- ERR_FAIL_INDEX((int)p_align, 4);
- valign = p_align;
+void Label::set_vertical_alignment(VerticalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ vertical_alignment = p_alignment;
update();
}
-Label::VAlign Label::get_valign() const {
- return valign;
+VerticalAlignment Label::get_vertical_alignment() const {
+ return vertical_alignment;
}
void Label::set_text(const String &p_string) {
@@ -592,19 +636,19 @@ void Label::set_text(const String &p_string) {
visible_chars = get_total_character_count() * percent_visible;
}
update();
- minimum_size_changed();
+ update_minimum_size();
}
void Label::set_text_direction(Control::TextDirection p_text_direction) {
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (text_direction != p_text_direction) {
text_direction = p_text_direction;
- dirty = true;
+ font_dirty = true;
update();
}
}
-void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
+void Label::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
if (st_parser != p_parser) {
st_parser = p_parser;
dirty = true;
@@ -612,7 +656,7 @@ void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_pa
}
}
-Control::StructuredTextParser Label::get_structured_text_bidi_override() const {
+TextServer::StructuredTextParser Label::get_structured_text_bidi_override() const {
return st_parser;
}
@@ -632,7 +676,7 @@ Control::TextDirection Label::get_text_direction() const {
void Label::clear_opentype_features() {
opentype_features.clear();
- dirty = true;
+ font_dirty = true;
update();
}
@@ -640,7 +684,7 @@ void Label::set_opentype_feature(const String &p_name, int p_value) {
int32_t tag = TS->name_to_tag(p_name);
if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
opentype_features[tag] = p_value;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -668,7 +712,7 @@ String Label::get_language() const {
void Label::set_clip_text(bool p_clip) {
clip = p_clip;
update();
- minimum_size_changed();
+ update_minimum_size();
}
bool Label::is_clipping_text() const {
@@ -682,7 +726,7 @@ void Label::set_text_overrun_behavior(Label::OverrunBehavior p_behavior) {
}
update();
if (clip || overrun_behavior != OVERRUN_NO_TRIMMING) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -702,7 +746,9 @@ void Label::set_visible_characters(int p_amount) {
} else {
percent_visible = 1.0;
}
- dirty = true;
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
+ dirty = true;
+ }
update();
}
}
@@ -720,7 +766,9 @@ void Label::set_percent_visible(float p_percent) {
visible_chars = get_total_character_count() * p_percent;
percent_visible = p_percent;
}
- dirty = true;
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
+ dirty = true;
+ }
update();
}
}
@@ -729,6 +777,18 @@ float Label::get_percent_visible() const {
return percent_visible;
}
+Label::VisibleCharactersBehavior Label::get_visible_characters_behavior() const {
+ return visible_chars_behavior;
+}
+
+void Label::set_visible_characters_behavior(Label::VisibleCharactersBehavior p_behavior) {
+ if (visible_chars_behavior != p_behavior) {
+ visible_chars_behavior = p_behavior;
+ dirty = true;
+ update();
+ }
+}
+
void Label::set_lines_skipped(int p_lines) {
ERR_FAIL_COND(p_lines < 0);
lines_skipped = p_lines;
@@ -751,7 +811,7 @@ int Label::get_max_lines_visible() const {
}
int Label::get_total_character_count() const {
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -763,17 +823,17 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
- dirty = true;
+ font_dirty = true;
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -803,16 +863,16 @@ bool Label::_get(const StringName &p_name, Variant &r_ret) const {
void Label::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
void Label::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_align", "align"), &Label::set_align);
- ClassDB::bind_method(D_METHOD("get_align"), &Label::get_align);
- ClassDB::bind_method(D_METHOD("set_valign", "valign"), &Label::set_valign);
- ClassDB::bind_method(D_METHOD("get_valign"), &Label::get_valign);
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label::get_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &Label::set_vertical_alignment);
+ ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label::get_vertical_alignment);
ClassDB::bind_method(D_METHOD("set_text", "text"), &Label::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction);
@@ -836,6 +896,8 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_total_character_count"), &Label::get_total_character_count);
ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &Label::set_visible_characters);
ClassDB::bind_method(D_METHOD("get_visible_characters"), &Label::get_visible_characters);
+ ClassDB::bind_method(D_METHOD("get_visible_characters_behavior"), &Label::get_visible_characters_behavior);
+ ClassDB::bind_method(D_METHOD("set_visible_characters_behavior", "behavior"), &Label::set_visible_characters_behavior);
ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &Label::set_percent_visible);
ClassDB::bind_method(D_METHOD("get_percent_visible"), &Label::get_percent_visible);
ClassDB::bind_method(D_METHOD("set_lines_skipped", "lines_skipped"), &Label::set_lines_skipped);
@@ -847,16 +909,6 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options);
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options);
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- BIND_ENUM_CONSTANT(ALIGN_FILL);
-
- BIND_ENUM_CONSTANT(VALIGN_TOP);
- BIND_ENUM_CONSTANT(VALIGN_CENTER);
- BIND_ENUM_CONSTANT(VALIGN_BOTTOM);
- BIND_ENUM_CONSTANT(VALIGN_FILL);
-
BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
@@ -868,19 +920,31 @@ void Label::_bind_methods() {
BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS);
BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS);
+ BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING);
+ BIND_ENUM_CONSTANT(VC_CHARS_AFTER_SHAPING);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_AUTO);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_LTR);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_RTL);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "valign", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_valign", "get_valign");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible");
+
+ // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied.
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
+
+ ADD_GROUP("Locale", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
+
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
@@ -896,8 +960,8 @@ Label::Label(const String &p_text) {
Label::~Label() {
for (int i = 0; i < lines_rid.size(); i++) {
- TS->free(lines_rid[i]);
+ TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
- TS->free(text_rid);
+ TS->free_rid(text_rid);
}
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 8b48eb9670..f7b725928f 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,20 +37,6 @@ class Label : public Control {
GDCLASS(Label, Control);
public:
- enum Align {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_FILL
- };
-
- enum VAlign {
- VALIGN_TOP,
- VALIGN_CENTER,
- VALIGN_BOTTOM,
- VALIGN_FILL
- };
-
enum AutowrapMode {
AUTOWRAP_OFF,
AUTOWRAP_ARBITRARY,
@@ -66,9 +52,17 @@ public:
OVERRUN_TRIM_WORD_ELLIPSIS,
};
+ enum VisibleCharactersBehavior {
+ VC_CHARS_BEFORE_SHAPING,
+ VC_CHARS_AFTER_SHAPING,
+ VC_GLYPHS_AUTO,
+ VC_GLYPHS_LTR,
+ VC_GLYPHS_RTL,
+ };
+
private:
- Align align = ALIGN_LEFT;
- VAlign valign = VALIGN_TOP;
+ HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT;
+ VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_TOP;
String text;
String xl_text;
AutowrapMode autowrap_mode = AUTOWRAP_OFF;
@@ -79,17 +73,19 @@ private:
bool lines_dirty = true;
bool dirty = true;
+ bool font_dirty = true;
RID text_rid;
Vector<RID> lines_rid;
Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
- Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
Array st_args;
float percent_visible = 1.0;
+ VisibleCharactersBehavior visible_chars_behavior = VC_CHARS_BEFORE_SHAPING;
int visible_chars = -1;
int lines_skipped = 0;
int max_lines_visible = -1;
@@ -109,11 +105,11 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
- void set_align(Align p_align);
- Align get_align() const;
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
- void set_valign(VAlign p_align);
- VAlign get_valign() const;
+ void set_vertical_alignment(VerticalAlignment p_alignment);
+ VerticalAlignment get_vertical_alignment() const;
void set_text(const String &p_string);
String get_text() const;
@@ -128,8 +124,8 @@ public:
void set_language(const String &p_language);
String get_language() const;
- void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
- Control::StructuredTextParser get_structured_text_bidi_override() const;
+ void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
+ TextServer::StructuredTextParser get_structured_text_bidi_override() const;
void set_structured_text_bidi_override_options(Array p_args);
Array get_structured_text_bidi_override_options() const;
@@ -140,6 +136,9 @@ public:
void set_uppercase(bool p_uppercase);
bool is_uppercase() const;
+ VisibleCharactersBehavior get_visible_characters_behavior() const;
+ void set_visible_characters_behavior(VisibleCharactersBehavior p_behavior);
+
void set_visible_characters(int p_amount);
int get_visible_characters() const;
int get_total_character_count() const;
@@ -167,9 +166,8 @@ public:
~Label();
};
-VARIANT_ENUM_CAST(Label::Align);
-VARIANT_ENUM_CAST(Label::VAlign);
VARIANT_ENUM_CAST(Label::AutowrapMode);
VARIANT_ENUM_CAST(Label::OverrunBehavior);
+VARIANT_ENUM_CAST(Label::VisibleCharactersBehavior);
#endif
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 124d5c7821..73188d6602 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -215,6 +215,27 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
}
}
+void LineEdit::unhandled_key_input(const Ref<InputEvent> &p_event) {
+ Ref<InputEventKey> k = p_event;
+
+ if (k.is_valid()) {
+ if (!k->is_pressed()) {
+ return;
+ }
+ // Handle Unicode (with modifiers active, process after shortcuts).
+ if (has_focus() && editable && (k->get_unicode() >= 32)) {
+ selection_delete();
+ char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 };
+ int prev_len = text.length();
+ insert_text_at_caret(ucodestr);
+ if (text.length() != prev_len) {
+ _text_changed();
+ }
+ accept_event();
+ }
+ }
+}
+
void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
@@ -227,8 +248,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
_ensure_menu();
- menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
- menu->set_size(Vector2(1, 1));
+ menu->set_position(get_screen_position() + get_local_mouse_position());
+ menu->reset_size();
menu->popup();
grab_focus();
accept_event();
@@ -268,7 +289,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- shift_selection_check_pre(b->is_shift_pressed());
+ if (b->is_shift_pressed()) {
+ shift_selection_check_pre(true);
+ }
set_caret_at_pixel_pos(b->get_position().x);
@@ -320,7 +343,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
deselect();
selection.start_column = caret_column;
selection.creating = true;
- } else if (selection.enabled) {
+ } else if (selection.enabled && !selection.double_click) {
selection.drag_attempt = true;
}
}
@@ -345,6 +368,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
selection.creating = false;
selection.double_click = false;
+ if (!drag_action) {
+ selection.drag_attempt = false;
+ }
show_virtual_keyboard();
}
@@ -369,21 +395,62 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
selection_fill_at_caret();
}
}
+
+ if (drag_action && can_drop_data(m->get_position(), get_viewport()->gui_get_drag_data())) {
+ drag_caret_force_displayed = true;
+ set_caret_at_pixel_pos(m->get_position().x);
+ }
}
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
if (!k->is_pressed()) {
+ if (alt_start && k->get_keycode() == Key::ALT) {
+ alt_start = false;
+ if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) {
+ char32_t ucodestr[2] = { (char32_t)alt_code, 0 };
+ insert_text_at_caret(ucodestr);
+ }
+ accept_event();
+ return;
+ }
return;
}
+ // Alt+ Unicode input:
+ if (k->is_alt_pressed()) {
+ if (!alt_start) {
+ if (k->get_keycode() == Key::KP_ADD) {
+ alt_start = true;
+ alt_code = 0;
+ accept_event();
+ return;
+ }
+ } else {
+ if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0);
+ }
+ if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::KP_0);
+ }
+ if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10;
+ }
+ accept_event();
+ return;
+ }
+ }
+
if (context_menu_enabled) {
if (k->is_action("ui_menu", true)) {
_ensure_menu();
Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")))) / 2);
- menu->set_position(get_global_transform().xform(pos));
- menu->set_size(Vector2(1, 1));
+ menu->set_position(get_screen_position() + pos);
+ menu->reset_size();
menu->popup();
menu->grab_focus();
}
@@ -538,17 +605,17 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
-void LineEdit::set_align(Align p_align) {
- ERR_FAIL_INDEX((int)p_align, 4);
- if (align != p_align) {
- align = p_align;
+void LineEdit::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ if (alignment != p_alignment) {
+ alignment = p_alignment;
_shape();
}
update();
}
-LineEdit::Align LineEdit::get_align() const {
- return align;
+HorizontalAlignment LineEdit::get_horizontal_alignment() const {
+ return alignment;
}
Variant LineEdit::get_drag_data(const Point2 &p_point) {
@@ -569,21 +636,50 @@ bool LineEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const
return drop_override;
}
- return p_data.get_type() == Variant::STRING;
+ return is_editable() && p_data.get_type() == Variant::STRING;
}
void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
Control::drop_data(p_point, p_data);
- if (p_data.get_type() == Variant::STRING) {
+ if (p_data.get_type() == Variant::STRING && is_editable()) {
set_caret_at_pixel_pos(p_point.x);
+ int caret_column_tmp = caret_column;
+ bool is_inside_sel = selection.enabled && caret_column >= selection.begin && caret_column <= selection.end;
+ if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ is_inside_sel = selection.enabled && caret_column > selection.begin && caret_column < selection.end;
+ }
+ if (selection.drag_attempt) {
+ selection.drag_attempt = false;
+ if (!is_inside_sel) {
+ if (!Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ if (caret_column_tmp > selection.end) {
+ caret_column_tmp = caret_column_tmp - (selection.end - selection.begin);
+ }
+ selection_delete();
+ }
- text = text.left(selection.begin) + text.substr(selection.end);
- _shape();
-
- insert_text_at_caret(p_data);
- selection.begin = caret_column - (selection.end - selection.begin);
- selection.end = caret_column;
+ set_caret_column(caret_column_tmp);
+ insert_text_at_caret(p_data);
+ }
+ } else if (selection.enabled && caret_column >= selection.begin && caret_column <= selection.end) {
+ caret_column_tmp = selection.begin;
+ selection_delete();
+ set_caret_column(caret_column_tmp);
+ insert_text_at_caret(p_data);
+ grab_focus();
+ } else {
+ insert_text_at_caret(p_data);
+ grab_focus();
+ }
+ select(caret_column_tmp, caret_column);
+ if (!text_changed_dirty) {
+ if (is_inside_tree()) {
+ MessageQueue::get_singleton()->push_call(this, "_text_changed");
+ }
+ text_changed_dirty = true;
+ }
+ update();
}
}
@@ -608,8 +704,8 @@ void LineEdit::_notification(int p_what) {
#ifdef TOOLS_ENABLED
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) {
- set_caret_blink_enabled(EDITOR_DEF("text_editor/appearance/caret/caret_blink", false));
- set_caret_blink_speed(EDITOR_DEF("text_editor/appearance/caret/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink"));
+ set_caret_blink_speed(EDITOR_GET("text_editor/appearance/caret/caret_blink_speed"));
if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) {
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed));
@@ -617,31 +713,37 @@ void LineEdit::_notification(int p_what) {
}
} break;
#endif
+
case NOTIFICATION_RESIZED: {
_fit_to_width();
scroll_offset = 0;
set_caret_column(get_caret_column());
} break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
_shape();
update();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED: {
placeholder_translated = atr(placeholder);
_shape();
update();
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
window_has_focus = true;
draw_caret = true;
update();
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
window_has_focus = false;
draw_caret = false;
update();
} break;
+
case NOTIFICATION_DRAW: {
if ((!has_focus() && !(menu && menu->has_focus()) && !caret_force_displayed) || !window_has_focus) {
draw_caret = false;
@@ -676,23 +778,23 @@ void LineEdit::_notification(int p_what) {
float text_width = TS->shaped_text_get_size(text_rid).x;
float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
- switch (align) {
- case ALIGN_FILL:
- case ALIGN_LEFT: {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - style->get_margin(SIDE_RIGHT) - (text_width)));
} else {
x_ofs = style->get_offset().x;
}
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (scroll_offset != 0) {
x_ofs = style->get_offset().x;
} else {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - (text_width)) / 2);
}
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl) {
x_ofs = style->get_offset().x;
} else {
@@ -713,7 +815,7 @@ void LineEdit::_notification(int p_what) {
// Draw placeholder color.
if (using_placeholder) {
- font_color.a *= placeholder_alpha;
+ font_color = get_theme_color(SNAME("font_placeholder_color"));
}
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
@@ -730,7 +832,7 @@ void LineEdit::_notification(int p_what) {
r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(SIDE_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon);
- if (align == ALIGN_CENTER) {
+ if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
if (scroll_offset == 0) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
}
@@ -741,8 +843,6 @@ void LineEdit::_notification(int p_what) {
ofs_max -= r_icon->get_width();
}
- int caret_width = Math::round(1 * get_theme_default_base_scale());
-
// Draw selections rects.
Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs);
if (selection.enabled) {
@@ -803,7 +903,10 @@ void LineEdit::_notification(int p_what) {
// Draw carets.
ofs.x = x_ofs + scroll_offset;
- if (draw_caret) {
+ if (draw_caret || drag_caret_force_displayed) {
+ // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1).
+ const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale());
+
if (ime_text.length() == 0) {
// Normal caret.
CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column);
@@ -884,6 +987,7 @@ void LineEdit::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_FOCUS_ENTER: {
if (!caret_force_displayed) {
if (caret_blink_enabled) {
@@ -903,6 +1007,7 @@ void LineEdit::_notification(int p_what) {
show_virtual_keyboard();
} break;
+
case NOTIFICATION_FOCUS_EXIT: {
if (caret_blink_enabled && !caret_force_displayed) {
caret_blink_timer->stop();
@@ -921,10 +1026,11 @@ void LineEdit::_notification(int p_what) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
- if (deselect_on_focus_loss_enabled) {
+ if (deselect_on_focus_loss_enabled && !selection.drag_attempt) {
deselect();
}
} break;
+
case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
if (has_focus()) {
ime_text = DisplayServer::get_singleton()->ime_get_text();
@@ -935,6 +1041,27 @@ void LineEdit::_notification(int p_what) {
update();
}
} break;
+
+ case NOTIFICATION_DRAG_BEGIN: {
+ drag_action = true;
+ } break;
+
+ case NOTIFICATION_DRAG_END: {
+ if (is_drag_successful()) {
+ if (selection.drag_attempt) {
+ selection.drag_attempt = false;
+ if (is_editable() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ selection_delete();
+ } else if (deselect_on_focus_loss_enabled) {
+ deselect();
+ }
+ }
+ } else {
+ selection.drag_attempt = false;
+ }
+ drag_action = false;
+ drag_caret_force_displayed = false;
+ } break;
}
}
@@ -959,7 +1086,7 @@ void LineEdit::paste_text() {
// Strip escape characters like \n and \t as they can't be displayed on LineEdit.
String paste_buffer = DisplayServer::get_singleton()->clipboard_get().strip_escapes();
- if (paste_buffer != "") {
+ if (!paste_buffer.is_empty()) {
int prev_len = text.length();
if (selection.enabled) {
selection_delete();
@@ -999,6 +1126,9 @@ void LineEdit::undo() {
} else if (undo_stack_pos == undo_stack.front()) {
return;
}
+
+ deselect();
+
undo_stack_pos = undo_stack_pos->prev();
TextOperation op = undo_stack_pos->get();
text = op.text;
@@ -1020,6 +1150,9 @@ void LineEdit::redo() {
if (undo_stack_pos == undo_stack.back()) {
return;
}
+
+ deselect();
+
undo_stack_pos = undo_stack_pos->next();
TextOperation op = undo_stack_pos->get();
text = op.text;
@@ -1051,23 +1184,23 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) {
int x_ofs = 0;
float text_width = TS->shaped_text_get_size(text_rid).x;
- switch (align) {
- case ALIGN_FILL:
- case ALIGN_LEFT: {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
} else {
x_ofs = style->get_offset().x;
}
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (scroll_offset != 0) {
x_ofs = style->get_offset().x;
} else {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
}
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl) {
x_ofs = style->get_offset().x;
} else {
@@ -1080,7 +1213,7 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) {
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
if (right_icon.is_valid() || display_clear_icon) {
Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
- if (align == ALIGN_CENTER) {
+ if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
if (scroll_offset == 0) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
}
@@ -1099,23 +1232,23 @@ Vector2i LineEdit::get_caret_pixel_pos() {
int x_ofs = 0;
float text_width = TS->shaped_text_get_size(text_rid).x;
- switch (align) {
- case ALIGN_FILL:
- case ALIGN_LEFT: {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
} else {
x_ofs = style->get_offset().x;
}
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (scroll_offset != 0) {
x_ofs = style->get_offset().x;
} else {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
}
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl) {
x_ofs = style->get_offset().x;
} else {
@@ -1128,7 +1261,7 @@ Vector2i LineEdit::get_caret_pixel_pos() {
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
if (right_icon.is_valid() || display_clear_icon) {
Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
- if (align == ALIGN_CENTER) {
+ if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
if (scroll_offset == 0) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
}
@@ -1355,7 +1488,7 @@ bool LineEdit::get_draw_control_chars() const {
return draw_control_chars;
}
-void LineEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
+void LineEdit::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
if (st_parser != p_parser) {
st_parser = p_parser;
_shape();
@@ -1363,7 +1496,7 @@ void LineEdit::set_structured_text_bidi_override(Control::StructuredTextParser p
}
}
-Control::StructuredTextParser LineEdit::get_structured_text_bidi_override() const {
+TextServer::StructuredTextParser LineEdit::get_structured_text_bidi_override() const {
return st_parser;
}
@@ -1412,15 +1545,6 @@ String LineEdit::get_placeholder() const {
return placeholder;
}
-void LineEdit::set_placeholder_alpha(float p_alpha) {
- placeholder_alpha = p_alpha;
- update();
-}
-
-float LineEdit::get_placeholder_alpha() const {
- return placeholder_alpha;
-}
-
void LineEdit::set_caret_column(int p_column) {
if (p_column > (int)text.length()) {
p_column = text.length();
@@ -1444,23 +1568,23 @@ void LineEdit::set_caret_column(int p_column) {
int x_ofs = 0;
float text_width = TS->shaped_text_get_size(text_rid).x;
- switch (align) {
- case ALIGN_FILL:
- case ALIGN_LEFT: {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
} else {
x_ofs = style->get_offset().x;
}
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (scroll_offset != 0) {
x_ofs = style->get_offset().x;
} else {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
}
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl) {
x_ofs = style->get_offset().x;
} else {
@@ -1474,7 +1598,7 @@ void LineEdit::set_caret_column(int p_column) {
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
if (right_icon.is_valid() || display_clear_icon) {
Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
- if (align == ALIGN_CENTER) {
+ if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
if (scroll_offset == 0) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
}
@@ -1551,7 +1675,7 @@ Size2 LineEdit::get_minimum_size() const {
Size2 min_size;
// Minimum size of text.
- int em_space_size = font->get_char_size('M', 0, font_size).x;
+ float em_space_size = font->get_char_size('M', 0, font_size).x;
min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size;
if (expand_to_text_length) {
@@ -1654,7 +1778,7 @@ void LineEdit::set_editable(bool p_editable) {
editable = p_editable;
- minimum_size_changed();
+ update_minimum_size();
update();
}
@@ -1877,14 +2001,14 @@ PopupMenu *LineEdit::get_menu() const {
void LineEdit::_editor_settings_changed() {
#ifdef TOOLS_ENABLED
- set_caret_blink_enabled(EDITOR_DEF("text_editor/appearance/caret/caret_blink", false));
- set_caret_blink_speed(EDITOR_DEF("text_editor/appearance/caret/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink"));
+ set_caret_blink_speed(EDITOR_GET("text_editor/appearance/caret/caret_blink_speed"));
#endif
}
void LineEdit::set_expand_to_text_length_enabled(bool p_enabled) {
expand_to_text_length = p_enabled;
- minimum_size_changed();
+ update_minimum_size();
set_caret_column(caret_column);
}
@@ -1898,7 +2022,7 @@ void LineEdit::set_clear_button_enabled(bool p_enabled) {
}
clear_button_enabled = p_enabled;
_fit_to_width();
- minimum_size_changed();
+ update_minimum_size();
update();
}
@@ -1959,7 +2083,7 @@ void LineEdit::set_right_icon(const Ref<Texture2D> &p_icon) {
}
right_icon = p_icon;
_fit_to_width();
- minimum_size_changed();
+ update_minimum_size();
update();
}
@@ -2014,7 +2138,7 @@ void LineEdit::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
- TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t));
full_width = TS->shaped_text_get_size(text_rid).x;
@@ -2023,12 +2147,12 @@ void LineEdit::_shape() {
Size2 size = TS->shaped_text_get_size(text_rid);
if ((expand_to_text_length && old_size.x != size.x) || (old_size.y != size.y)) {
- minimum_size_changed();
+ update_minimum_size();
}
}
void LineEdit::_fit_to_width() {
- if (align == ALIGN_FILL) {
+ if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
int t_width = get_size().width - style->get_margin(SIDE_RIGHT) - style->get_margin(SIDE_LEFT);
bool using_placeholder = text.is_empty() && ime_text.is_empty();
@@ -2100,7 +2224,7 @@ bool LineEdit::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -2108,7 +2232,7 @@ bool LineEdit::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@@ -2140,7 +2264,7 @@ bool LineEdit::_get(const StringName &p_name, Variant &r_ret) const {
void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
@@ -2154,8 +2278,8 @@ void LineEdit::_validate_property(PropertyInfo &property) const {
void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed);
- ClassDB::bind_method(D_METHOD("set_align", "align"), &LineEdit::set_align);
- ClassDB::bind_method(D_METHOD("get_align"), &LineEdit::get_align);
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment);
ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear);
ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1));
@@ -2181,8 +2305,6 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &LineEdit::get_structured_text_bidi_override_options);
ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &LineEdit::set_placeholder);
ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder);
- ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha);
- ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha);
ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column);
ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column);
ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset);
@@ -2233,11 +2355,6 @@ void LineEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring")));
ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text")));
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- BIND_ENUM_CONSTANT(ALIGN_FILL);
-
BIND_ENUM_CONSTANT(MENU_CUT);
BIND_ENUM_CONSTANT(MENU_COPY);
BIND_ENUM_CONSTANT(MENU_PASTE);
@@ -2269,7 +2386,8 @@ void LineEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_MAX);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
@@ -2285,14 +2403,11 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
- ADD_GROUP("Placeholder", "placeholder_");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed");
@@ -2379,7 +2494,7 @@ void LineEdit::_ensure_menu() {
}
}
-LineEdit::LineEdit() {
+LineEdit::LineEdit(const String &p_placeholder) {
text_rid = TS->create_shaped_text();
_create_undo_state();
@@ -2387,6 +2502,7 @@ LineEdit::LineEdit() {
set_focus_mode(FOCUS_ALL);
set_default_cursor_shape(CURSOR_IBEAM);
set_mouse_filter(MOUSE_FILTER_STOP);
+ set_process_unhandled_key_input(true);
caret_blink_timer = memnew(Timer);
add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT);
@@ -2394,9 +2510,11 @@ LineEdit::LineEdit() {
caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret));
set_caret_blink_enabled(false);
+ set_placeholder(p_placeholder);
+
set_editable(true); // Initialise to opposite first, so we get past the early-out in set_editable.
}
LineEdit::~LineEdit() {
- TS->free(text_rid);
+ TS->free_rid(text_rid);
}
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 134d5f8f76..0fb178fca4 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,13 +38,6 @@ class LineEdit : public Control {
GDCLASS(LineEdit, Control);
public:
- enum Align {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_FILL
- };
-
enum MenuItems {
MENU_CUT,
MENU_COPY,
@@ -78,18 +71,20 @@ public:
};
private:
- Align align = ALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
bool editable = false;
bool pass = false;
bool text_changed_dirty = false;
+ bool alt_start = false;
+ uint32_t alt_code = 0;
+
String undo_text;
String text;
String placeholder;
String placeholder_translated;
String secret_character = "*";
- float placeholder_alpha = 0.6;
String ime_text;
Point2 ime_selection;
@@ -104,7 +99,7 @@ private:
PopupMenu *menu_dir = nullptr;
PopupMenu *menu_ctl = nullptr;
- bool caret_mid_grapheme_enabled = false;
+ bool caret_mid_grapheme_enabled = true;
int caret_column = 0;
int scroll_offset = 0;
@@ -114,7 +109,7 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextDirection input_direction = TEXT_DIRECTION_LTR;
- Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
Array st_args;
bool draw_control_chars = false;
@@ -129,6 +124,9 @@ private:
bool middle_mouse_paste_enabled = true;
+ bool drag_action = false;
+ bool drag_caret_force_displayed = false;
+
Ref<Texture2D> right_icon;
bool flat = false;
@@ -207,6 +205,7 @@ private:
protected:
void _notification(int p_what);
static void _bind_methods();
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
virtual void gui_input(const Ref<InputEvent> &p_event) override;
bool _set(const StringName &p_name, const Variant &p_value);
@@ -215,8 +214,8 @@ protected:
void _validate_property(PropertyInfo &property) const override;
public:
- void set_align(Align p_align);
- Align get_align() const;
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
virtual Variant get_drag_data(const Point2 &p_point) override;
virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
@@ -257,8 +256,8 @@ public:
void set_draw_control_chars(bool p_draw_control_chars);
bool get_draw_control_chars() const;
- void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
- Control::StructuredTextParser get_structured_text_bidi_override() const;
+ void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
+ TextServer::StructuredTextParser get_structured_text_bidi_override() const;
void set_structured_text_bidi_override_options(Array p_args);
Array get_structured_text_bidi_override_options() const;
@@ -266,9 +265,6 @@ public:
void set_placeholder(String p_text);
String get_placeholder() const;
- void set_placeholder_alpha(float p_alpha);
- float get_placeholder_alpha() const;
-
void set_caret_column(int p_column);
int get_caret_column() const;
@@ -340,11 +336,10 @@ public:
void show_virtual_keyboard();
- LineEdit();
+ LineEdit(const String &p_placeholder = String());
~LineEdit();
};
-VARIANT_ENUM_CAST(LineEdit::Align);
VARIANT_ENUM_CAST(LineEdit::MenuItems);
#endif
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index c3201186ea..dca6437519 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "link_button.h"
+
#include "core/string/translation.h"
void LinkButton::_shape() {
@@ -42,7 +43,7 @@ void LinkButton::_shape() {
text_buf->set_direction((TextServer::Direction)text_direction);
}
TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, xl_text));
- text_buf->add_string(xl_text, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text_buf->add_string(xl_text, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
}
void LinkButton::set_text(const String &p_text) {
@@ -52,7 +53,7 @@ void LinkButton::set_text(const String &p_text) {
text = p_text;
xl_text = atr(text);
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
}
@@ -60,7 +61,7 @@ String LinkButton::get_text() const {
return text;
}
-void LinkButton::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
+void LinkButton::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
if (st_parser != p_parser) {
st_parser = p_parser;
_shape();
@@ -68,7 +69,7 @@ void LinkButton::set_structured_text_bidi_override(Control::StructuredTextParser
}
}
-Control::StructuredTextParser LinkButton::get_structured_text_bidi_override() const {
+TextServer::StructuredTextParser LinkButton::get_structured_text_bidi_override() const {
return st_parser;
}
@@ -148,18 +149,20 @@ void LinkButton::_notification(int p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
xl_text = atr(text);
_shape();
-
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
update();
} break;
+
case NOTIFICATION_THEME_CHANGED: {
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
+
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
Size2 size = get_size();
@@ -230,7 +233,6 @@ void LinkButton::_notification(int p_what) {
draw_line(Vector2(0, y), Vector2(width, y), color, text_buf->get_line_underline_thickness());
}
}
-
} break;
}
}
@@ -240,7 +242,7 @@ bool LinkButton::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -248,7 +250,7 @@ bool LinkButton::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@@ -280,7 +282,7 @@ bool LinkButton::_get(const StringName &p_name, Variant &r_ret) const {
void LinkButton::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
@@ -308,15 +310,17 @@ void LinkButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
}
-LinkButton::LinkButton() {
+LinkButton::LinkButton(const String &p_text) {
text_buf.instantiate();
set_focus_mode(FOCUS_NONE);
set_default_cursor_shape(CURSOR_POINTING_HAND);
+
+ set_text(p_text);
}
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index 231543c63c..6d2dcbde84 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,6 @@
#define LINKBUTTON_H
#include "scene/gui/base_button.h"
-#include "scene/resources/bit_map.h"
#include "scene/resources/text_line.h"
class LinkButton : public BaseButton {
@@ -54,7 +53,7 @@ private:
Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
- Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
Array st_args;
void _shape();
@@ -72,8 +71,8 @@ public:
void set_text(const String &p_text);
String get_text() const;
- void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
- Control::StructuredTextParser get_structured_text_bidi_override() const;
+ void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
+ TextServer::StructuredTextParser get_structured_text_bidi_override() const;
void set_structured_text_bidi_override_options(Array p_args);
Array get_structured_text_bidi_override_options() const;
@@ -91,7 +90,7 @@ public:
void set_underline_mode(UnderlineMode p_underline_mode);
UnderlineMode get_underline_mode() const;
- LinkButton();
+ LinkButton(const String &p_text = String());
};
VARIANT_ENUM_CAST(LinkButton::UnderlineMode);
diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp
index 50b4d192a9..fac37a8634 100644
--- a/scene/gui/margin_container.cpp
+++ b/scene/gui/margin_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -65,6 +65,24 @@ Size2 MarginContainer::get_minimum_size() const {
return max;
}
+Vector<int> MarginContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> MarginContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void MarginContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {
@@ -89,8 +107,9 @@ void MarginContainer::_notification(int p_what) {
fit_child_in_rect(c, Rect2(margin_left, margin_top, w, h));
}
} break;
+
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
} break;
}
}
diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h
index b782976ada..f8a3c5bb11 100644
--- a/scene/gui/margin_container.h
+++ b/scene/gui/margin_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,6 +42,9 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
MarginContainer();
};
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 39c7b04955..316fee53fe 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,10 +33,10 @@
#include "core/os/keyboard.h"
#include "scene/main/window.h"
-void MenuButton::unhandled_key_input(const Ref<InputEvent> &p_event) {
+void MenuButton::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (!_is_focus_owner_in_shorcut_context()) {
+ if (!_is_focus_owner_in_shortcut_context()) {
return;
}
@@ -98,7 +98,13 @@ void MenuButton::pressed() {
popup->set_position(gp);
popup->set_parent_rect(Rect2(Point2(gp - popup->get_position()), size));
- popup->take_mouse_focus();
+ // If not triggered by the mouse, start the popup with its first item selected.
+ if (popup->get_item_count() > 0 &&
+ ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) ||
+ (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) {
+ popup->set_current_index(0);
+ }
+
popup->popup();
}
@@ -130,11 +136,16 @@ int MenuButton::get_item_count() const {
void MenuButton::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ popup->set_layout_direction((Window::LayoutDirection)get_layout_direction());
+ } break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible_in_tree()) {
popup->hide();
}
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
Vector2i mouse_pos = DisplayServer::get_singleton()->mouse_get_position() - mouse_pos_adjusted;
MenuButton *menu_btn_other = Object::cast_to<MenuButton>(get_viewport()->gui_find_control(mouse_pos));
@@ -176,7 +187,7 @@ void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
- pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
+ pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As Checkbox,As Radio Button");
pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
@@ -184,7 +195,7 @@ void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
- pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+ pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
p_list->push_back(pi);
pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i));
@@ -207,7 +218,7 @@ void MenuButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_count"), &MenuButton::get_item_count);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_hover"), "set_switch_on_hover", "is_switch_on_hover");
- ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "popup/item_");
+ ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_");
ADD_SIGNAL(MethodInfo("about_to_popup"));
}
@@ -216,11 +227,12 @@ void MenuButton::set_disable_shortcuts(bool p_disabled) {
disable_shortcuts = p_disabled;
}
-MenuButton::MenuButton() {
+MenuButton::MenuButton(const String &p_text) :
+ Button(p_text) {
set_flat(true);
set_toggle_mode(true);
set_disable_shortcuts(false);
- set_process_unhandled_key_input(true);
+ set_process_shortcut_input(true);
set_focus_mode(FOCUS_NONE);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 455b7dc870..0a6b46c796 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,7 +40,7 @@ class MenuButton : public Button {
bool clicked = false;
bool switch_on_hover = false;
bool disable_shortcuts = false;
- PopupMenu *popup;
+ PopupMenu *popup = nullptr;
Vector2i mouse_pos_adjusted;
@@ -54,7 +54,7 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
- virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
+ virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
public:
virtual void pressed() override;
@@ -67,7 +67,7 @@ public:
void set_item_count(int p_count);
int get_item_count() const;
- MenuButton();
+ MenuButton(const String &p_text = String());
~MenuButton();
};
diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp
index 8bf25ac915..4f34ece86f 100644
--- a/scene/gui/nine_patch_rect.cpp
+++ b/scene/gui/nine_patch_rect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,18 +34,20 @@
#include "servers/rendering_server.h"
void NinePatchRect::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- if (texture.is_null()) {
- return;
- }
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ if (texture.is_null()) {
+ return;
+ }
- Rect2 rect = Rect2(Point2(), get_size());
- Rect2 src_rect = region_rect;
+ Rect2 rect = Rect2(Point2(), get_size());
+ Rect2 src_rect = region_rect;
- texture->get_rect_region(rect, src_rect, rect, src_rect);
+ texture->get_rect_region(rect, src_rect, rect, src_rect);
- RID ci = get_canvas_item();
- RS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center);
+ RID ci = get_canvas_item();
+ RS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center);
+ } break;
}
}
@@ -93,11 +95,7 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) {
}
texture = p_tex;
update();
- /*
- if (texture.is_valid())
- texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites
- */
- minimum_size_changed();
+ update_minimum_size();
emit_signal(SceneStringNames::get_singleton()->texture_changed);
}
@@ -109,7 +107,7 @@ void NinePatchRect::set_patch_margin(Side p_side, int p_size) {
ERR_FAIL_INDEX((int)p_side, 4);
margin[p_side] = p_size;
update();
- minimum_size_changed();
+ update_minimum_size();
}
int NinePatchRect::get_patch_margin(Side p_side) const {
diff --git a/scene/gui/nine_patch_rect.h b/scene/gui/nine_patch_rect.h
index f9a3f31fe5..23aebbb782 100644
--- a/scene/gui/nine_patch_rect.h
+++ b/scene/gui/nine_patch_rect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index dcf3cfeb09..4b79d79846 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,6 +32,8 @@
#include "core/string/print_string.h"
+static const int NONE_SELECTED = -1;
+
Size2 OptionButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
@@ -40,7 +42,7 @@ Size2 OptionButton::get_minimum_size() const {
const Size2 arrow_size = Control::get_theme_icon(SNAME("arrow"))->get_size();
Size2 content_size = minsize - padding;
- content_size.width += arrow_size.width + get_theme_constant(SNAME("hseparation"));
+ content_size.width += arrow_size.width + get_theme_constant(SNAME("h_separation"));
content_size.height = MAX(content_size.height, arrow_size.height);
minsize = content_size + padding;
@@ -89,8 +91,12 @@ void OptionButton::_notification(int p_what) {
}
arrow->draw(ci, ofs, clr);
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
- case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ popup->set_layout_direction((Window::LayoutDirection)get_layout_direction());
+ [[fallthrough]];
+ }
case NOTIFICATION_THEME_CHANGED: {
if (has_theme_icon(SNAME("arrow"))) {
if (is_layout_rtl()) {
@@ -102,6 +108,7 @@ void OptionButton::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible_in_tree()) {
popup->hide();
@@ -110,6 +117,65 @@ void OptionButton::_notification(int p_what) {
}
}
+bool OptionButton::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0] == "popup") {
+ String property = components[2];
+ if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") {
+ return false;
+ }
+
+ bool valid;
+ popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid);
+
+ int idx = components[1].get_slice("_", 1).to_int();
+ if (idx == current) {
+ // Force refreshing currently displayed item.
+ current = NONE_SELECTED;
+ _select(idx, false);
+ }
+
+ return valid;
+ }
+ return false;
+}
+
+bool OptionButton::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0] == "popup") {
+ String property = components[2];
+ if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") {
+ return false;
+ }
+
+ bool valid;
+ r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid);
+ return valid;
+ }
+ return false;
+}
+
+void OptionButton::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < popup->get_item_count(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("popup/item_%d/text", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("popup/item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i));
+ pi.usage &= ~(!popup->is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/separator", i));
+ pi.usage &= ~(!popup->is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
void OptionButton::_focused(int p_which) {
emit_signal(SNAME("item_focused"), p_which);
}
@@ -122,20 +188,33 @@ void OptionButton::pressed() {
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
popup->set_size(Size2(size.width, 0));
+
+ // If not triggered by the mouse, start the popup with the checked item selected.
+ if (popup->get_item_count() > 0) {
+ if ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) ||
+ (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept"))) {
+ popup->set_current_index(current > -1 ? current : 0);
+ } else {
+ popup->scroll_to_item(current > -1 ? current : 0);
+ }
+ }
+
popup->popup();
}
void OptionButton::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id) {
+ bool first_selectable = !has_selectable_items();
popup->add_icon_radio_check_item(p_icon, p_label, p_id);
- if (popup->get_item_count() == 1) {
- select(0);
+ if (first_selectable) {
+ select(get_item_count() - 1);
}
}
void OptionButton::add_item(const String &p_label, int p_id) {
+ bool first_selectable = !has_selectable_items();
popup->add_radio_check_item(p_label, p_id);
- if (popup->get_item_count() == 1) {
- select(0);
+ if (first_selectable) {
+ select(get_item_count() - 1);
}
}
@@ -163,6 +242,10 @@ void OptionButton::set_item_metadata(int p_idx, const Variant &p_metadata) {
popup->set_item_metadata(p_idx, p_metadata);
}
+void OptionButton::set_item_tooltip(int p_idx, const String &p_tooltip) {
+ popup->set_item_tooltip(p_idx, p_tooltip);
+}
+
void OptionButton::set_item_disabled(int p_idx, bool p_disabled) {
popup->set_item_disabled(p_idx, p_disabled);
}
@@ -176,6 +259,10 @@ Ref<Texture2D> OptionButton::get_item_icon(int p_idx) const {
}
int OptionButton::get_item_id(int p_idx) const {
+ if (p_idx == NONE_SELECTED) {
+ return NONE_SELECTED;
+ }
+
return popup->get_item_id(p_idx);
}
@@ -187,41 +274,99 @@ Variant OptionButton::get_item_metadata(int p_idx) const {
return popup->get_item_metadata(p_idx);
}
+String OptionButton::get_item_tooltip(int p_idx) const {
+ return popup->get_item_tooltip(p_idx);
+}
+
bool OptionButton::is_item_disabled(int p_idx) const {
return popup->is_item_disabled(p_idx);
}
+bool OptionButton::is_item_separator(int p_idx) const {
+ return popup->is_item_separator(p_idx);
+}
+void OptionButton::set_item_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+
+ int count_old = get_item_count();
+ if (p_count == count_old) {
+ return;
+ }
+
+ popup->set_item_count(p_count);
+
+ if (p_count > count_old) {
+ for (int i = count_old; i < p_count; i++) {
+ popup->set_item_as_radio_checkable(i, true);
+ }
+ }
+
+ notify_property_list_changed();
+}
+
+bool OptionButton::has_selectable_items() const {
+ for (int i = 0; i < get_item_count(); i++) {
+ if (!is_item_disabled(i) && !is_item_separator(i)) {
+ return true;
+ }
+ }
+ return false;
+}
+int OptionButton::get_selectable_item(bool p_from_last) const {
+ if (!p_from_last) {
+ for (int i = 0; i < get_item_count(); i++) {
+ if (!is_item_disabled(i) && !is_item_separator(i)) {
+ return i;
+ }
+ }
+ } else {
+ for (int i = get_item_count() - 1; i >= 0; i++) {
+ if (!is_item_disabled(i) && !is_item_separator(i)) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
int OptionButton::get_item_count() const {
return popup->get_item_count();
}
-void OptionButton::add_separator() {
- popup->add_separator();
+void OptionButton::add_separator(const String &p_text) {
+ popup->add_separator(p_text);
}
void OptionButton::clear() {
popup->clear();
set_text("");
- current = -1;
+ current = NONE_SELECTED;
}
void OptionButton::_select(int p_which, bool p_emit) {
- if (p_which < 0) {
- return;
- }
if (p_which == current) {
return;
}
- ERR_FAIL_INDEX(p_which, popup->get_item_count());
+ if (p_which == NONE_SELECTED) {
+ for (int i = 0; i < popup->get_item_count(); i++) {
+ popup->set_item_checked(i, false);
+ }
- for (int i = 0; i < popup->get_item_count(); i++) {
- popup->set_item_checked(i, i == p_which);
- }
+ current = NONE_SELECTED;
+ set_text("");
+ set_icon(nullptr);
+ } else {
+ ERR_FAIL_INDEX(p_which, popup->get_item_count());
- current = p_which;
- set_text(popup->get_item_text(current));
- set_icon(popup->get_item_icon(current));
+ for (int i = 0; i < popup->get_item_count(); i++) {
+ popup->set_item_checked(i, i == p_which);
+ }
+
+ current = p_which;
+ set_text(popup->get_item_text(current));
+ set_icon(popup->get_item_icon(current));
+ }
if (is_inside_tree() && p_emit) {
emit_signal(SNAME("item_selected"), current);
@@ -229,7 +374,7 @@ void OptionButton::_select(int p_which, bool p_emit) {
}
void OptionButton::_select_int(int p_which) {
- if (p_which < 0 || p_which >= popup->get_item_count()) {
+ if (p_which < NONE_SELECTED || p_which >= popup->get_item_count()) {
return;
}
_select(p_which, false);
@@ -244,10 +389,6 @@ int OptionButton::get_selected() const {
}
int OptionButton::get_selected_id() const {
- int idx = get_selected();
- if (idx < 0) {
- return 0;
- }
return get_item_id(current);
}
@@ -261,48 +402,25 @@ Variant OptionButton::get_selected_metadata() const {
void OptionButton::remove_item(int p_idx) {
popup->remove_item(p_idx);
+ if (current == p_idx) {
+ _select(NONE_SELECTED);
+ }
}
PopupMenu *OptionButton::get_popup() const {
return popup;
}
-Array OptionButton::_get_items() const {
- Array items;
- for (int i = 0; i < get_item_count(); i++) {
- items.push_back(get_item_text(i));
- items.push_back(get_item_icon(i));
- items.push_back(is_item_disabled(i));
- items.push_back(get_item_id(i));
- items.push_back(get_item_metadata(i));
- }
-
- return items;
+void OptionButton::get_translatable_strings(List<String> *p_strings) const {
+ popup->get_translatable_strings(p_strings);
}
-void OptionButton::_set_items(const Array &p_items) {
- ERR_FAIL_COND(p_items.size() % 5);
- clear();
-
- for (int i = 0; i < p_items.size(); i += 5) {
- String text = p_items[i + 0];
- Ref<Texture2D> icon = p_items[i + 1];
- bool disabled = p_items[i + 2];
- int id = p_items[i + 3];
- Variant meta = p_items[i + 4];
-
- int idx = get_item_count();
- add_item(text, id);
- set_item_icon(idx, icon);
- set_item_disabled(idx, disabled);
- set_item_metadata(idx, meta);
+void OptionButton::_validate_property(PropertyInfo &property) const {
+ if (property.name == "text" || property.name == "icon") {
+ property.usage = PROPERTY_USAGE_NONE;
}
}
-void OptionButton::get_translatable_strings(List<String> *p_strings) const {
- popup->get_translatable_strings(p_strings);
-}
-
void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_item", "label", "id"), &OptionButton::add_item, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item, DEFVAL(-1));
@@ -311,14 +429,16 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_disabled", "idx", "disabled"), &OptionButton::set_item_disabled);
ClassDB::bind_method(D_METHOD("set_item_id", "idx", "id"), &OptionButton::set_item_id);
ClassDB::bind_method(D_METHOD("set_item_metadata", "idx", "metadata"), &OptionButton::set_item_metadata);
+ ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &OptionButton::set_item_tooltip);
ClassDB::bind_method(D_METHOD("get_item_text", "idx"), &OptionButton::get_item_text);
ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &OptionButton::get_item_icon);
ClassDB::bind_method(D_METHOD("get_item_id", "idx"), &OptionButton::get_item_id);
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &OptionButton::get_item_index);
ClassDB::bind_method(D_METHOD("get_item_metadata", "idx"), &OptionButton::get_item_metadata);
+ ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &OptionButton::get_item_tooltip);
ClassDB::bind_method(D_METHOD("is_item_disabled", "idx"), &OptionButton::is_item_disabled);
- ClassDB::bind_method(D_METHOD("get_item_count"), &OptionButton::get_item_count);
- ClassDB::bind_method(D_METHOD("add_separator"), &OptionButton::add_separator);
+ ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &OptionButton::is_item_separator);
+ ClassDB::bind_method(D_METHOD("add_separator", "text"), &OptionButton::add_separator, DEFVAL(String()));
ClassDB::bind_method(D_METHOD("clear"), &OptionButton::clear);
ClassDB::bind_method(D_METHOD("select", "idx"), &OptionButton::select);
ClassDB::bind_method(D_METHOD("get_selected"), &OptionButton::get_selected);
@@ -329,19 +449,22 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &OptionButton::get_popup);
- ClassDB::bind_method(D_METHOD("_set_items"), &OptionButton::_set_items);
- ClassDB::bind_method(D_METHOD("_get_items"), &OptionButton::_get_items);
+ ClassDB::bind_method(D_METHOD("set_item_count", "count"), &OptionButton::set_item_count);
+ ClassDB::bind_method(D_METHOD("get_item_count"), &OptionButton::get_item_count);
+ ClassDB::bind_method(D_METHOD("has_selectable_items"), &OptionButton::has_selectable_items);
+ ClassDB::bind_method(D_METHOD("get_selectable_item", "from_last"), &OptionButton::get_selectable_item, DEFVAL(false));
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
- // "selected" property must come after "items", otherwise GH-10213 occurs.
+ // "selected" property must come after "item_count", otherwise GH-10213 occurs.
+ ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected");
ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index")));
ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index")));
}
-OptionButton::OptionButton() {
+OptionButton::OptionButton(const String &p_text) :
+ Button(p_text) {
set_toggle_mode(true);
- set_text_align(ALIGN_LEFT);
+ set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
if (is_layout_rtl()) {
if (has_theme_icon(SNAME("arrow"))) {
_set_internal_margin(SIDE_LEFT, Control::get_theme_icon(SNAME("arrow"))->get_width());
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 953337ecce..7896132626 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,7 +37,7 @@
class OptionButton : public Button {
GDCLASS(OptionButton, Button);
- PopupMenu *popup;
+ PopupMenu *popup = nullptr;
int current = -1;
void _focused(int p_which);
@@ -45,14 +45,15 @@ class OptionButton : public Button {
void _select(int p_which, bool p_emit = false);
void _select_int(int p_which);
- Array _get_items() const;
- void _set_items(const Array &p_items);
-
virtual void pressed() override;
protected:
Size2 get_minimum_size() const override;
void _notification(int p_what);
+ 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;
static void _bind_methods();
public:
@@ -68,6 +69,7 @@ public:
void set_item_id(int p_idx, int p_id);
void set_item_metadata(int p_idx, const Variant &p_metadata);
void set_item_disabled(int p_idx, bool p_disabled);
+ void set_item_tooltip(int p_idx, const String &p_tooltip);
String get_item_text(int p_idx) const;
Ref<Texture2D> get_item_icon(int p_idx) const;
@@ -75,10 +77,16 @@ public:
int get_item_index(int p_id) const;
Variant get_item_metadata(int p_idx) const;
bool is_item_disabled(int p_idx) const;
+ bool is_item_separator(int p_idx) const;
+ String get_item_tooltip(int p_idx) const;
+
+ bool has_selectable_items() const;
+ int get_selectable_item(bool p_from_last = false) const;
+ void set_item_count(int p_count);
int get_item_count() const;
- void add_separator();
+ void add_separator(const String &p_text = "");
void clear();
@@ -93,7 +101,7 @@ public:
virtual void get_translatable_strings(List<String> *p_strings) const override;
- OptionButton();
+ OptionButton(const String &p_text = String());
~OptionButton();
};
diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp
index e8e7e3d997..1ac6cf57ab 100644
--- a/scene/gui/panel.cpp
+++ b/scene/gui/panel.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,35 +30,16 @@
#include "panel.h"
-#include "core/string/print_string.h"
-
void Panel::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
- Ref<StyleBox> style = mode == MODE_BACKGROUND ? get_theme_stylebox(SNAME("panel")) : get_theme_stylebox(SNAME("panel_fg"));
- style->draw(ci, Rect2(Point2(), get_size()));
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
+ style->draw(ci, Rect2(Point2(), get_size()));
+ } break;
}
}
-void Panel::set_mode(Mode p_mode) {
- mode = p_mode;
- update();
-}
-
-Panel::Mode Panel::get_mode() const {
- return mode;
-}
-
-void Panel::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_mode", "mode"), &Panel::set_mode);
- ClassDB::bind_method(D_METHOD("get_mode"), &Panel::get_mode);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Background,Foreground"), "set_mode", "get_mode");
-
- BIND_ENUM_CONSTANT(MODE_BACKGROUND);
- BIND_ENUM_CONSTANT(MODE_FOREGROUND);
-}
-
Panel::Panel() {
// Has visible stylebox, so stop by default.
set_mouse_filter(MOUSE_FILTER_STOP);
diff --git a/scene/gui/panel.h b/scene/gui/panel.h
index 84fd6aaead..5d2e912680 100644
--- a/scene/gui/panel.h
+++ b/scene/gui/panel.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,26 +36,11 @@
class Panel : public Control {
GDCLASS(Panel, Control);
-public:
- enum Mode {
- MODE_BACKGROUND,
- MODE_FOREGROUND,
- };
-
-private:
- Mode mode = MODE_BACKGROUND;
-
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
- void set_mode(Mode p_mode);
- Mode get_mode() const;
-
Panel();
};
-VARIANT_ENUM_CAST(Panel::Mode)
-
#endif // PANEL_H
diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp
index d910e1e882..fe01712a89 100644
--- a/scene/gui/panel_container.cpp
+++ b/scene/gui/panel_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -60,47 +60,67 @@ Size2 PanelContainer::get_minimum_size() const {
return ms;
}
-void PanelContainer::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
- Ref<StyleBox> style;
-
- if (has_theme_stylebox(SNAME("panel"))) {
- style = get_theme_stylebox(SNAME("panel"));
- } else {
- style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
- }
+Vector<int> PanelContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
- style->draw(ci, Rect2(Point2(), get_size()));
- }
+Vector<int> PanelContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
- if (p_what == NOTIFICATION_SORT_CHILDREN) {
- Ref<StyleBox> style;
+void PanelContainer::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
+ Ref<StyleBox> style;
- if (has_theme_stylebox(SNAME("panel"))) {
- style = get_theme_stylebox(SNAME("panel"));
- } else {
- style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
- }
+ if (has_theme_stylebox(SNAME("panel"))) {
+ style = get_theme_stylebox(SNAME("panel"));
+ } else {
+ style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
+ }
- Size2 size = get_size();
- Point2 ofs;
- if (style.is_valid()) {
- size -= style->get_minimum_size();
- ofs += style->get_offset();
- }
+ style->draw(ci, Rect2(Point2(), get_size()));
+ } break;
- for (int i = 0; i < get_child_count(); i++) {
- Control *c = Object::cast_to<Control>(get_child(i));
- if (!c || !c->is_visible_in_tree()) {
- continue;
+ case NOTIFICATION_SORT_CHILDREN: {
+ Ref<StyleBox> style;
+
+ if (has_theme_stylebox(SNAME("panel"))) {
+ style = get_theme_stylebox(SNAME("panel"));
+ } else {
+ style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
}
- if (c->is_set_as_top_level()) {
- continue;
+
+ Size2 size = get_size();
+ Point2 ofs;
+ if (style.is_valid()) {
+ size -= style->get_minimum_size();
+ ofs += style->get_offset();
}
- fit_child_in_rect(c, Rect2(ofs, size));
- }
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c || !c->is_visible_in_tree()) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
+
+ fit_child_in_rect(c, Rect2(ofs, size));
+ }
+ } break;
}
}
diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h
index f27ca7fad7..8f07ce38eb 100644
--- a/scene/gui/panel_container.h
+++ b/scene/gui/panel_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,6 +42,9 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
PanelContainer();
};
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index a48ad0f770..6532fc5934 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,26 +42,30 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
}
void Popup::_initialize_visible_parents() {
- visible_parents.clear();
-
- Window *parent_window = this;
- while (parent_window) {
- parent_window = parent_window->get_parent_visible_window();
- if (parent_window) {
- visible_parents.push_back(parent_window);
- parent_window->connect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- parent_window->connect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ if (is_embedded()) {
+ visible_parents.clear();
+
+ Window *parent_window = this;
+ while (parent_window) {
+ parent_window = parent_window->get_parent_visible_window();
+ if (parent_window) {
+ visible_parents.push_back(parent_window);
+ parent_window->connect("focus_entered", callable_mp(this, &Popup::_parent_focused));
+ parent_window->connect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ }
}
}
}
void Popup::_deinitialize_visible_parents() {
- for (uint32_t i = 0; i < visible_parents.size(); ++i) {
- visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
- }
+ if (is_embedded()) {
+ for (uint32_t i = 0; i < visible_parents.size(); ++i) {
+ visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
+ visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ }
- visible_parents.clear();
+ visible_parents.clear();
+ }
}
void Popup::_notification(int p_what) {
@@ -74,19 +78,19 @@ void Popup::_notification(int p_what) {
emit_signal(SNAME("popup_hide"));
popped_up = false;
}
-
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
if (has_focus()) {
popped_up = true;
}
} break;
+
case NOTIFICATION_EXIT_TREE: {
_deinitialize_visible_parents();
} break;
- case NOTIFICATION_WM_CLOSE_REQUEST: {
- _close_pressed();
- } break;
+
+ case NOTIFICATION_WM_CLOSE_REQUEST:
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
_close_pressed();
} break;
@@ -94,7 +98,7 @@ void Popup::_notification(int p_what) {
}
void Popup::_parent_focused() {
- if (popped_up && close_on_parent_focus) {
+ if (popped_up && get_flag(FLAG_POPUP)) {
_close_pressed();
}
}
@@ -104,27 +108,25 @@ void Popup::_close_pressed() {
_deinitialize_visible_parents();
- call_deferred(SNAME("hide"));
+ // Hide after returning to process events, but only if we don't
+ // get popped up in the interim.
+ call_deferred(SNAME("_popup_conditional_hide"));
}
-void Popup::set_as_minsize() {
- set_size(get_contents_minimum_size());
+void Popup::_post_popup() {
+ Window::_post_popup();
+ popped_up = true;
}
-void Popup::set_close_on_parent_focus(bool p_close) {
- close_on_parent_focus = p_close;
-}
-
-bool Popup::get_close_on_parent_focus() {
- return close_on_parent_focus;
+void Popup::_popup_conditional_hide() {
+ if (!popped_up) {
+ hide();
+ }
}
void Popup::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_close_on_parent_focus", "close"), &Popup::set_close_on_parent_focus);
- ClassDB::bind_method(D_METHOD("get_close_on_parent_focus"), &Popup::get_close_on_parent_focus);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "close_on_parent_focus"), "set_close_on_parent_focus", "get_close_on_parent_focus");
-
ADD_SIGNAL(MethodInfo("popup_hide"));
+ ClassDB::bind_method(D_METHOD("_popup_conditional_hide"), &Popup::_popup_conditional_hide);
}
Rect2i Popup::_popup_adjust_rect() const {
@@ -184,6 +186,7 @@ Popup::Popup() {
set_transient(true);
set_flag(FLAG_BORDERLESS, true);
set_flag(FLAG_RESIZE_DISABLED, true);
+ set_flag(FLAG_POPUP, true);
connect("window_input", callable_mp(this, &Popup::_input_from_window));
}
@@ -241,13 +244,20 @@ void PopupPanel::_update_child_rects() {
}
void PopupPanel::_notification(int p_what) {
- if (p_what == NOTIFICATION_THEME_CHANGED) {
- panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name()));
- } else if (p_what == NOTIFICATION_READY || p_what == NOTIFICATION_ENTER_TREE) {
- panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name()));
- _update_child_rects();
- } else if (p_what == NOTIFICATION_WM_SIZE_CHANGED) {
- _update_child_rects();
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name()));
+ } break;
+
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_READY: {
+ panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name()));
+ _update_child_rects();
+ } break;
+
+ case NOTIFICATION_WM_SIZE_CHANGED: {
+ _update_child_rects();
+ } break;
}
}
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 8458a75eef..27f46d4a97 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,7 +42,6 @@ class Popup : public Window {
LocalVector<Window *> visible_parents;
bool popped_up = false;
- bool close_on_parent_focus = true;
void _input_from_window(const Ref<InputEvent> &p_event);
@@ -58,12 +57,11 @@ protected:
void _notification(int p_what);
static void _bind_methods();
-public:
- void set_as_minsize();
+ void _popup_conditional_hide();
- void set_close_on_parent_focus(bool p_close);
- bool get_close_on_parent_focus();
+ virtual void _post_popup() override;
+public:
Popup();
~Popup();
};
@@ -71,7 +69,7 @@ public:
class PopupPanel : public Popup {
GDCLASS(PopupPanel, Popup);
- Panel *panel;
+ Panel *panel = nullptr;
protected:
void _update_child_rects();
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 7e9b545776..8303d6db57 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,7 @@
#include "popup_menu.h"
+#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -46,11 +47,11 @@ String PopupMenu::_get_accel_text(const Item &p_item) const {
}
Size2 PopupMenu::_get_contents_minimum_size() const {
- int vseparation = get_theme_constant(SNAME("vseparation"));
- int hseparation = get_theme_constant(SNAME("hseparation"));
+ int vseparation = get_theme_constant(SNAME("v_separation"));
+ int hseparation = get_theme_constant(SNAME("h_separation"));
Size2 minsize = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); // Accounts for margin in the margin container
- minsize.x += scroll_container->get_v_scrollbar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
+ minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
float max_w = 0.0;
float icon_w = 0.0;
@@ -67,7 +68,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
size.width += items[i].h_ofs;
- if (items[i].checkable_type) {
+ if (items[i].checkable_type && !items[i].separator) {
has_check = true;
}
@@ -80,7 +81,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
accel_max_w = MAX(accel_w, accel_max_w);
}
- if (items[i].submenu != "") {
+ if (!items[i].submenu.is_empty()) {
size.width += get_theme_icon(SNAME("submenu"))->get_width();
}
@@ -108,10 +109,9 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int PopupMenu::_get_item_height(int p_item) const {
ERR_FAIL_INDEX_V(p_item, items.size(), 0);
- ERR_FAIL_COND_V(p_item < 0, 0);
int icon_height = items[p_item].get_icon_size().height;
- if (items[p_item].checkable_type) {
+ if (items[p_item].checkable_type && !items[p_item].separator) {
icon_height = MAX(icon_height, MAX(get_theme_icon(SNAME("checked"))->get_height(), get_theme_icon(SNAME("radio_checked"))->get_height()));
}
@@ -129,7 +129,7 @@ int PopupMenu::_get_item_height(int p_item) const {
}
int PopupMenu::_get_items_total_height() const {
- int vsep = get_theme_constant(SNAME("vseparation"));
+ int vsep = get_theme_constant(SNAME("v_separation"));
// Get total height of all items by taking max of icon height and font height
int items_total_height = 0;
@@ -141,23 +141,6 @@ int PopupMenu::_get_items_total_height() const {
return items_total_height - vsep;
}
-void PopupMenu::_scroll_to_item(int p_item) {
- ERR_FAIL_INDEX(p_item, items.size());
- ERR_FAIL_COND(p_item < 0);
-
- // Scroll item into view (upwards)
- if (items[p_item]._ofs_cache < -control->get_position().y) {
- int amnt_over = items[p_item]._ofs_cache + control->get_position().y;
- scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
- }
-
- // Scroll item into view (downwards)
- if (items[p_item]._ofs_cache + items[p_item]._height_cache > -control->get_position().y + scroll_container->get_size().height) {
- int amnt_over = items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y - scroll_container->get_size().height;
- scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
- }
-}
-
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
if (p_over.x < 0 || p_over.x >= get_size().width) {
return -1;
@@ -165,7 +148,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); // Accounts for margin in the margin container
- int vseparation = get_theme_constant(SNAME("vseparation"));
+ int vseparation = get_theme_constant(SNAME("v_separation"));
Point2 ofs = style->get_offset() + Point2(0, vseparation / 2);
@@ -186,61 +169,77 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
return -1;
}
-void PopupMenu::_activate_submenu(int p_over) {
+void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
Node *n = get_node(items[p_over].submenu);
ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + items[p_over].submenu + ".");
Popup *submenu_popup = Object::cast_to<Popup>(n);
ERR_FAIL_COND_MSG(!submenu_popup, "Item subnode is not a Popup: " + items[p_over].submenu + ".");
if (submenu_popup->is_visible()) {
- return; //already visible!
+ return; // Already visible.
}
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
- int vsep = get_theme_constant(SNAME("vseparation"));
+ int vsep = get_theme_constant(SNAME("v_separation"));
Point2 this_pos = get_position();
Rect2 this_rect(this_pos, get_size());
float scroll_offset = control->get_position().y;
- Point2 submenu_pos;
+ submenu_popup->reset_size(); // Shrink the popup size to its contents.
Size2 submenu_size = submenu_popup->get_size();
+
+ Point2 submenu_pos;
if (control->is_layout_rtl()) {
submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset);
} else {
submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset);
}
- // Fix pos if going outside parent rect
+ // Fix pos if going outside parent rect.
if (submenu_pos.x < get_parent_rect().position.x) {
submenu_pos.x = this_pos.x + submenu_size.width;
}
- if (submenu_pos.x + submenu_size.width > get_parent_rect().size.width) {
+ if (submenu_pos.x + submenu_size.width > get_parent_rect().position.x + get_parent_rect().size.width) {
submenu_pos.x = this_pos.x - submenu_size.width;
}
- submenu_popup->set_close_on_parent_focus(false);
submenu_popup->set_position(submenu_pos);
- submenu_popup->set_as_minsize(); // Shrink the popup size to its contents.
- submenu_popup->popup();
- // Set autohide areas
PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup);
- if (submenu_pum) {
- submenu_pum->take_mouse_focus();
- // Make the position of the parent popup relative to submenu popup
- this_rect.position = this_rect.position - submenu_pum->get_position();
-
- // Autohide area above the submenu item
- submenu_pum->clear_autohide_areas();
- submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
-
- // If there is an area below the submenu item, add an autohide area there.
- if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
- int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
- submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
- }
+ if (!submenu_pum) {
+ submenu_popup->popup();
+ return;
+ }
+
+ submenu_pum->activated_by_keyboard = p_by_keyboard;
+
+ // If not triggered by the mouse, start the popup with its first item selected.
+ if (submenu_pum->get_item_count() > 0 && p_by_keyboard) {
+ submenu_pum->set_current_index(0);
+ }
+
+ submenu_pum->popup();
+
+ // Set autohide areas.
+
+ Rect2 safe_area = this_rect;
+ safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2;
+ safe_area.size.y = items[p_over]._height_cache;
+ DisplayServer::get_singleton()->window_set_popup_safe_rect(submenu_popup->get_window_id(), safe_area);
+
+ // Make the position of the parent popup relative to submenu popup.
+ this_rect.position = this_rect.position - submenu_pum->get_position();
+
+ // Autohide area above the submenu item.
+ submenu_pum->clear_autohide_areas();
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
+
+ // If there is an area below the submenu item, add an autohide area there.
+ if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
+ int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
}
}
@@ -266,7 +265,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
match_found = true;
@@ -280,7 +279,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -298,7 +297,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
match_found = true;
@@ -312,7 +311,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -326,14 +325,14 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
set_input_as_handled();
}
} else if (p_event->is_action("ui_right") && p_event->is_pressed()) {
- if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && items[mouse_over].submenu != "" && submenu_over != mouse_over) {
- _activate_submenu(mouse_over);
+ if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) {
+ _activate_submenu(mouse_over, true);
set_input_as_handled();
}
} else if (p_event->is_action("ui_accept") && p_event->is_pressed()) {
if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) {
- if (items[mouse_over].submenu != "" && submenu_over != mouse_over) {
- _activate_submenu(mouse_over);
+ if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) {
+ _activate_submenu(mouse_over, true);
} else {
activate_item(mouse_over);
}
@@ -343,11 +342,11 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
// Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar.
Rect2 item_clickable_area = scroll_container->get_rect();
- if (scroll_container->get_v_scrollbar()->is_visible_in_tree()) {
+ if (scroll_container->get_v_scroll_bar()->is_visible_in_tree()) {
if (is_layout_rtl()) {
- item_clickable_area.position.x += scroll_container->get_v_scrollbar()->get_size().width;
+ item_clickable_area.position.x += scroll_container->get_v_scroll_bar()->get_size().width;
} else {
- item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width;
+ item_clickable_area.size.width -= scroll_container->get_v_scroll_bar()->get_size().width;
}
}
@@ -371,7 +370,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
// Disable clicks under a time threshold to avoid selection right when opening the popup.
uint64_t now = OS::get_singleton()->get_ticks_msec();
uint64_t diff = now - popup_time_msec;
- if (diff < 100) {
+ if (diff < 150) {
return;
}
@@ -387,7 +386,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (items[over].submenu != "") {
+ if (!items[over].submenu.is_empty()) {
_activate_submenu(over);
return;
}
@@ -399,6 +398,11 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> m = p_event;
if (m.is_valid()) {
+ if (m->get_velocity().is_equal_approx(Vector2())) {
+ return;
+ }
+ activated_by_keyboard = false;
+
for (const Rect2 &E : autohide_areas) {
if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E.has_point(m->get_position())) {
_close_pressed();
@@ -419,7 +423,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (items[over].submenu != "" && submenu_over != over) {
+ if (!items[over].submenu.is_empty() && submenu_over != over) {
submenu_over = over;
submenu_timer->start();
}
@@ -462,7 +466,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (items[i].text.findn(search_string) == 0) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -486,7 +490,7 @@ void PopupMenu::_draw_items() {
bool rtl = control->is_layout_rtl();
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
Ref<StyleBox> hover = get_theme_stylebox(SNAME("hover"));
- // In Item::checkable_type enum order (less the non-checkable member)
+ // In Item::checkable_type enum order (less the non-checkable member).
Ref<Texture2D> check[] = { get_theme_icon(SNAME("checked")), get_theme_icon(SNAME("radio_checked")) };
Ref<Texture2D> uncheck[] = { get_theme_icon(SNAME("unchecked")), get_theme_icon(SNAME("radio_unchecked")) };
Ref<Texture2D> submenu;
@@ -500,21 +504,25 @@ void PopupMenu::_draw_items() {
Ref<StyleBox> labeled_separator_left = get_theme_stylebox(SNAME("labeled_separator_left"));
Ref<StyleBox> labeled_separator_right = get_theme_stylebox(SNAME("labeled_separator_right"));
- int vseparation = get_theme_constant(SNAME("vseparation"));
- int hseparation = get_theme_constant(SNAME("hseparation"));
+ int vseparation = get_theme_constant(SNAME("v_separation"));
+ int hseparation = get_theme_constant(SNAME("h_separation"));
Color font_color = get_theme_color(SNAME("font_color"));
Color font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
Color font_accelerator_color = get_theme_color(SNAME("font_accelerator_color"));
Color font_hover_color = get_theme_color(SNAME("font_hover_color"));
Color font_separator_color = get_theme_color(SNAME("font_separator_color"));
- float scroll_width = scroll_container->get_v_scrollbar()->is_visible_in_tree() ? scroll_container->get_v_scrollbar()->get_size().width : 0;
+ float scroll_width = scroll_container->get_v_scroll_bar()->is_visible_in_tree() ? scroll_container->get_v_scroll_bar()->get_size().width : 0;
float display_width = control->get_size().width - scroll_width;
// Find the widest icon and whether any items have a checkbox, and store the offsets for each.
float icon_ofs = 0.0;
bool has_check = false;
for (int i = 0; i < items.size(); i++) {
+ if (items[i].separator) {
+ continue;
+ }
+
icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs);
if (items[i].checkable_type) {
@@ -556,31 +564,39 @@ void PopupMenu::_draw_items() {
// Separator
item_ofs.x += items[i].h_ofs;
if (items[i].separator) {
- int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
- int sep_ofs = Math::floor((h - sep_h) / 2.0);
- if (text != String()) {
- int text_size = items[i].text_buf->get_size().width;
- int text_center = display_width / 2;
- int text_left = text_center - text_size / 2;
- int text_right = text_center + text_size / 2;
- if (text_left > item_ofs.x) {
- labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, text_left - item_ofs.x), sep_h)));
+ if (!text.is_empty() || !items[i].icon.is_null()) {
+ int content_size = items[i].text_buf->get_size().width + hseparation * 2;
+ if (!items[i].icon.is_null()) {
+ content_size += icon_size.width + hseparation;
}
- if (text_right < display_width) {
- labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - text_right), sep_h)));
+
+ int content_center = display_width / 2;
+ int content_left = content_center - content_size / 2;
+ int content_right = content_center + content_size / 2;
+ if (content_left > item_ofs.x) {
+ int sep_h = labeled_separator_left->get_center_size().height + labeled_separator_left->get_minimum_size().height;
+ int sep_ofs = Math::floor((h - sep_h) / 2.0);
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h)));
+ }
+ if (content_right < display_width) {
+ int sep_h = labeled_separator_right->get_center_size().height + labeled_separator_right->get_minimum_size().height;
+ int sep_ofs = Math::floor((h - sep_h) / 2.0);
+ labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h)));
}
} else {
+ int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
+ int sep_ofs = Math::floor((h - sep_h) / 2.0);
separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h)));
}
}
- Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+ Color icon_color(1, 1, 1, items[i].disabled && !items[i].separator ? 0.5 : 1);
// For non-separator items, add some padding for the content.
item_ofs.x += item_start_padding;
// Checkboxes
- if (items[i].checkable_type) {
+ if (items[i].checkable_type && !items[i].separator) {
Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
if (rtl) {
icon->draw(ci, Size2(control->get_size().width - item_ofs.x - icon->get_width(), item_ofs.y) + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
@@ -589,17 +605,29 @@ void PopupMenu::_draw_items() {
}
}
+ int separator_ofs = (display_width - items[i].text_buf->get_size().width) / 2;
+
// Icon
if (!items[i].icon.is_null()) {
- if (rtl) {
- items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ if (items[i].separator) {
+ separator_ofs -= (icon_size.width + hseparation) / 2;
+
+ if (rtl) {
+ items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ } else {
+ items[i].icon->draw(ci, item_ofs + Size2(separator_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ }
} else {
- items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ if (rtl) {
+ items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ } else {
+ items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ }
}
}
- // Submenu arrow on right hand side
- if (items[i].submenu != "") {
+ // Submenu arrow on right hand side.
+ if (!items[i].submenu.is_empty()) {
if (rtl) {
submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
} else {
@@ -607,20 +635,28 @@ void PopupMenu::_draw_items() {
}
}
- // Text
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
+
+ // Text
if (items[i].separator) {
- if (text != String()) {
- int center = (display_width - items[i].text_buf->get_size().width) / 2;
- Vector2 text_pos = Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
- if (outline_size > 0 && font_outline_color.a > 0) {
- items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ Color font_separator_outline_color = get_theme_color(SNAME("font_separator_outline_color"));
+ int separator_outline_size = get_theme_constant(SNAME("separator_outline_size"));
+
+ if (!text.is_empty()) {
+ Vector2 text_pos = Point2(separator_ofs, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ if (!rtl && !items[i].icon.is_null()) {
+ text_pos.x += icon_size.width + hseparation;
+ }
+
+ if (separator_outline_size > 0 && font_separator_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(ci, text_pos, separator_outline_size, font_separator_outline_color);
}
items[i].text_buf->draw(ci, text_pos, font_separator_color);
}
} else {
item_ofs.x += icon_ofs + check_ofs;
+
if (rtl) {
Vector2 text_pos = Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
if (outline_size > 0 && font_outline_color.a > 0) {
@@ -650,7 +686,7 @@ void PopupMenu::_draw_items() {
items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? font_hover_color : font_accelerator_color);
}
- // Cache the item vertical offset from the first item and the height
+ // Cache the item vertical offset from the first item and the height.
items.write[i]._ofs_cache = ofs.y;
items.write[i]._height_cache = h;
@@ -667,7 +703,7 @@ void PopupMenu::_draw_background() {
void PopupMenu::_minimum_lifetime_timeout() {
close_allowed = true;
// If the mouse still isn't in this popup after timer expires, close.
- if (!get_visible_rect().has_point(get_mouse_position())) {
+ if (!activated_by_keyboard && !get_visible_rect().has_point(get_mouse_position())) {
_close_pressed();
}
}
@@ -693,15 +729,15 @@ void PopupMenu::_shape_item(int p_item) {
if (items.write[p_item].dirty) {
items.write[p_item].text_buf->clear();
- Ref<Font> font = get_theme_font(SNAME("font"));
- int font_size = get_theme_font_size(SNAME("font_size"));
+ Ref<Font> font = get_theme_font(items[p_item].separator ? SNAME("font_separator") : SNAME("font"));
+ int font_size = get_theme_font_size(items[p_item].separator ? SNAME("font_separator_size") : SNAME("font_size"));
if (items[p_item].text_direction == Control::TEXT_DIRECTION_INHERITED) {
items.write[p_item].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
} else {
items.write[p_item].text_buf->set_direction((TextServer::Direction)items[p_item].text_direction);
}
- items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].opentype_features, (items[p_item].language != "") ? items[p_item].language : TranslationServer::get_singleton()->get_tool_locale());
+ items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].opentype_features, !items[p_item].language.is_empty() ? items[p_item].language : TranslationServer::get_singleton()->get_tool_locale());
items.write[p_item].accel_text_buf->clear();
items.write[p_item].accel_text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
@@ -715,11 +751,12 @@ void PopupMenu::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent());
if (pm) {
- // Inherit submenu's popup delay time from parent menu
+ // Inherit submenu's popup delay time from parent menu.
float pm_delay = pm->get_submenu_popup_delay();
set_submenu_popup_delay(pm_delay);
}
} break;
+
case NOTIFICATION_THEME_CHANGED:
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
@@ -732,24 +769,26 @@ void PopupMenu::_notification(int p_what) {
child_controls_changed();
control->update();
} break;
+
case NOTIFICATION_WM_MOUSE_ENTER: {
grab_focus();
} break;
+
case NOTIFICATION_WM_MOUSE_EXIT: {
- if (mouse_over >= 0 && (items[mouse_over].submenu == "" || submenu_over != -1)) {
+ if (mouse_over >= 0 && (items[mouse_over].submenu.is_empty() || submenu_over != -1)) {
mouse_over = -1;
control->update();
}
} break;
+
case NOTIFICATION_POST_POPUP: {
initial_button_mask = Input::get_singleton()->get_mouse_button_mask();
during_grabbed_click = (bool)initial_button_mask;
} break;
- case NOTIFICATION_WM_SIZE_CHANGED: {
- } break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
- //only used when using operating system windows
- if (!is_embedded() && autohide_areas.size()) {
+ // Only used when using operating system windows.
+ if (!activated_by_keyboard && !is_embedded() && autohide_areas.size()) {
Point2 mouse_pos = DisplayServer::get_singleton()->mouse_get_position();
mouse_pos -= get_position();
@@ -761,6 +800,7 @@ void PopupMenu::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible()) {
if (mouse_over >= 0) {
@@ -769,7 +809,7 @@ void PopupMenu::_notification(int p_what) {
}
for (int i = 0; i < items.size(); i++) {
- if (items[i].submenu == "") {
+ if (items[i].submenu.is_empty()) {
continue;
}
@@ -794,10 +834,10 @@ void PopupMenu::_notification(int p_what) {
// Set margin on the margin container
Ref<StyleBox> panel_style = get_theme_stylebox(SNAME("panel"));
- margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Side::SIDE_TOP));
- margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Side::SIDE_BOTTOM));
margin_container->add_theme_constant_override("margin_left", panel_style->get_margin(Side::SIDE_LEFT));
+ margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Side::SIDE_TOP));
margin_container->add_theme_constant_override("margin_right", panel_style->get_margin(Side::SIDE_RIGHT));
+ margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Side::SIDE_BOTTOM));
}
} break;
}
@@ -975,9 +1015,16 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
/* Methods to modify existing items. */
void PopupMenu::set_item_text(int p_idx, const String &p_text) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].text == p_text) {
+ return;
+ }
items.write[p_idx].text = p_text;
items.write[p_idx].xl_text = atr(p_text);
+ items.write[p_idx].dirty = true;
_shape_item(p_idx);
control->update();
@@ -985,6 +1032,9 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) {
}
void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_text_direction) {
+ if (p_item < 0) {
+ p_item += get_item_count();
+ }
ERR_FAIL_INDEX(p_item, items.size());
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (items[p_item].text_direction != p_text_direction) {
@@ -995,6 +1045,9 @@ void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_tex
}
void PopupMenu::clear_item_opentype_features(int p_item) {
+ if (p_item < 0) {
+ p_item += get_item_count();
+ }
ERR_FAIL_INDEX(p_item, items.size());
items.write[p_item].opentype_features.clear();
items.write[p_item].dirty = true;
@@ -1002,6 +1055,9 @@ void PopupMenu::clear_item_opentype_features(int p_item) {
}
void PopupMenu::set_item_opentype_feature(int p_item, const String &p_name, int p_value) {
+ if (p_item < 0) {
+ p_item += get_item_count();
+ }
ERR_FAIL_INDEX(p_item, items.size());
int32_t tag = TS->name_to_tag(p_name);
if (!items[p_item].opentype_features.has(tag) || (int)items[p_item].opentype_features[tag] != p_value) {
@@ -1012,6 +1068,9 @@ void PopupMenu::set_item_opentype_feature(int p_item, const String &p_name, int
}
void PopupMenu::set_item_language(int p_item, const String &p_language) {
+ if (p_item < 0) {
+ p_item += get_item_count();
+ }
ERR_FAIL_INDEX(p_item, items.size());
if (items[p_item].language != p_language) {
items.write[p_item].language = p_language;
@@ -1021,6 +1080,9 @@ void PopupMenu::set_item_language(int p_item, const String &p_language) {
}
void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon = p_icon;
@@ -1029,6 +1091,9 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
}
void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].checked = p_checked;
@@ -1038,6 +1103,9 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
}
void PopupMenu::set_item_id(int p_idx, int p_id) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].id = p_id;
@@ -1046,6 +1114,9 @@ void PopupMenu::set_item_id(int p_idx, int p_id) {
}
void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].accel = p_accel;
items.write[p_idx].dirty = true;
@@ -1055,6 +1126,9 @@ void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) {
}
void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].metadata = p_meta;
control->update();
@@ -1062,6 +1136,9 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
}
void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].disabled = p_disabled;
control->update();
@@ -1069,6 +1146,9 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
}
void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].submenu = p_submenu;
control->update();
@@ -1177,6 +1257,9 @@ int PopupMenu::get_item_state(int p_idx) const {
}
void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].separator = p_separator;
control->update();
@@ -1188,24 +1271,36 @@ bool PopupMenu::is_item_separator(int p_idx) const {
}
void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
control->update();
}
void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
control->update();
}
void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tooltip = p_tooltip;
control->update();
}
void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].shortcut.is_valid()) {
_unref_shortcut(items[p_idx].shortcut);
@@ -1222,6 +1317,9 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bo
}
void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].h_ofs = p_offset;
control->update();
@@ -1229,12 +1327,18 @@ void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
}
void PopupMenu::set_item_multistate(int p_idx, int p_state) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].state = p_state;
control->update();
}
void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].shortcut_is_disabled = p_disabled;
control->update();
@@ -1269,13 +1373,28 @@ bool PopupMenu::is_item_shortcut_disabled(int p_idx) const {
return items[p_idx].shortcut_is_disabled;
}
+void PopupMenu::set_current_index(int p_idx) {
+ ERR_FAIL_INDEX(p_idx, items.size());
+ mouse_over = p_idx;
+ scroll_to_item(mouse_over);
+ control->update();
+}
+
int PopupMenu::get_current_index() const {
return mouse_over;
}
void PopupMenu::set_item_count(int p_count) {
ERR_FAIL_COND(p_count < 0);
+ int prev_size = items.size();
items.resize(p_count);
+
+ if (prev_size < p_count) {
+ for (int i = prev_size; i < p_count; i++) {
+ items.write[i].id = i;
+ }
+ }
+
control->update();
child_controls_changed();
notify_property_list_changed();
@@ -1285,6 +1404,20 @@ int PopupMenu::get_item_count() const {
return items.size();
}
+void PopupMenu::scroll_to_item(int p_item) {
+ ERR_FAIL_INDEX(p_item, items.size());
+
+ // Scroll item into view (upwards).
+ if (items[p_item]._ofs_cache - scroll_container->get_v_scroll() < -control->get_position().y) {
+ scroll_container->set_v_scroll(items[p_item]._ofs_cache + control->get_position().y);
+ }
+
+ // Scroll item into view (downwards).
+ if (items[p_item]._ofs_cache + items[p_item]._height_cache - scroll_container->get_v_scroll() > -control->get_position().y + scroll_container->get_size().height) {
+ scroll_container->set_v_scroll(items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y);
+ }
+}
+
bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only) {
Key code = Key::NONE;
Ref<InputEventKey> k = p_event;
@@ -1323,7 +1456,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
return true;
}
- if (items[i].submenu != "") {
+ if (!items[i].submenu.is_empty()) {
Node *n = get_node(items[i].submenu);
if (!n) {
continue;
@@ -1388,12 +1521,12 @@ void PopupMenu::activate_item(int p_item) {
need_hide = false;
}
- emit_signal(SNAME("id_pressed"), id);
- emit_signal(SNAME("index_pressed"), p_item);
-
if (need_hide) {
hide();
}
+
+ emit_signal(SNAME("id_pressed"), id);
+ emit_signal(SNAME("index_pressed"), p_item);
}
void PopupMenu::remove_item(int p_idx) {
@@ -1403,7 +1536,7 @@ void PopupMenu::remove_item(int p_idx) {
_unref_shortcut(items[p_idx].shortcut);
}
- items.remove(p_idx);
+ items.remove_at(p_idx);
control->update();
child_controls_changed();
}
@@ -1412,7 +1545,7 @@ void PopupMenu::add_separator(const String &p_text, int p_id) {
Item sep;
sep.separator = true;
sep.id = p_id;
- if (p_text != String()) {
+ if (!p_text.is_empty()) {
sep.text = p_text;
sep.xl_text = atr(p_text);
}
@@ -1510,7 +1643,7 @@ void PopupMenu::set_parent_rect(const Rect2 &p_rect) {
void PopupMenu::get_translatable_strings(List<String> *p_strings) const {
for (int i = 0; i < items.size(); i++) {
- if (items[i].xl_text != "") {
+ if (!items[i].xl_text.is_empty()) {
p_strings->push_back(items[i].xl_text);
}
}
@@ -1558,7 +1691,7 @@ bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) {
} else if (property == "id") {
set_item_id(item_index, p_value);
return true;
- } else if (components[1] == "disabled") {
+ } else if (property == "disabled") {
set_item_disabled(item_index, p_value);
return true;
} else if (property == "separator") {
@@ -1631,7 +1764,7 @@ bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const {
} else if (property == "id") {
r_ret = get_item_id(item_index);
return true;
- } else if (components[1] == "disabled") {
+ } else if (property == "disabled") {
r_ret = is_item_disabled(item_index);
return true;
} else if (property == "separator") {
@@ -1658,7 +1791,7 @@ void PopupMenu::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(!is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
- pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+ pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
p_list->push_back(pi);
pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
@@ -1690,53 +1823,56 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text);
- ClassDB::bind_method(D_METHOD("set_item_text_direction", "idx", "direction"), &PopupMenu::set_item_text_direction);
- ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "idx", "tag", "value"), &PopupMenu::set_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &PopupMenu::set_item_language);
- ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon);
- ClassDB::bind_method(D_METHOD("set_item_checked", "idx", "checked"), &PopupMenu::set_item_checked);
- ClassDB::bind_method(D_METHOD("set_item_id", "idx", "id"), &PopupMenu::set_item_id);
- ClassDB::bind_method(D_METHOD("set_item_accelerator", "idx", "accel"), &PopupMenu::set_item_accelerator);
- ClassDB::bind_method(D_METHOD("set_item_metadata", "idx", "metadata"), &PopupMenu::set_item_metadata);
- ClassDB::bind_method(D_METHOD("set_item_disabled", "idx", "disabled"), &PopupMenu::set_item_disabled);
- ClassDB::bind_method(D_METHOD("set_item_submenu", "idx", "submenu"), &PopupMenu::set_item_submenu);
- ClassDB::bind_method(D_METHOD("set_item_as_separator", "idx", "enable"), &PopupMenu::set_item_as_separator);
- ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable);
- ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "idx", "enable"), &PopupMenu::set_item_as_radio_checkable);
- ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip);
- ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate);
- ClassDB::bind_method(D_METHOD("set_item_shortcut_disabled", "idx", "disabled"), &PopupMenu::set_item_shortcut_disabled);
-
- ClassDB::bind_method(D_METHOD("toggle_item_checked", "idx"), &PopupMenu::toggle_item_checked);
- ClassDB::bind_method(D_METHOD("toggle_item_multistate", "idx"), &PopupMenu::toggle_item_multistate);
-
- ClassDB::bind_method(D_METHOD("get_item_text", "idx"), &PopupMenu::get_item_text);
- ClassDB::bind_method(D_METHOD("get_item_text_direction", "idx"), &PopupMenu::get_item_text_direction);
- ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "idx", "tag"), &PopupMenu::get_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "idx"), &PopupMenu::clear_item_opentype_features);
- ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &PopupMenu::get_item_language);
- ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &PopupMenu::get_item_icon);
- ClassDB::bind_method(D_METHOD("is_item_checked", "idx"), &PopupMenu::is_item_checked);
- ClassDB::bind_method(D_METHOD("get_item_id", "idx"), &PopupMenu::get_item_id);
+ ClassDB::bind_method(D_METHOD("set_item_text", "index", "text"), &PopupMenu::set_item_text);
+ ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction);
+ ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "index", "tag", "value"), &PopupMenu::set_item_opentype_feature);
+ ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language);
+ ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon);
+ ClassDB::bind_method(D_METHOD("set_item_checked", "index", "checked"), &PopupMenu::set_item_checked);
+ ClassDB::bind_method(D_METHOD("set_item_id", "index", "id"), &PopupMenu::set_item_id);
+ ClassDB::bind_method(D_METHOD("set_item_accelerator", "index", "accel"), &PopupMenu::set_item_accelerator);
+ ClassDB::bind_method(D_METHOD("set_item_metadata", "index", "metadata"), &PopupMenu::set_item_metadata);
+ ClassDB::bind_method(D_METHOD("set_item_disabled", "index", "disabled"), &PopupMenu::set_item_disabled);
+ ClassDB::bind_method(D_METHOD("set_item_submenu", "index", "submenu"), &PopupMenu::set_item_submenu);
+ ClassDB::bind_method(D_METHOD("set_item_as_separator", "index", "enable"), &PopupMenu::set_item_as_separator);
+ ClassDB::bind_method(D_METHOD("set_item_as_checkable", "index", "enable"), &PopupMenu::set_item_as_checkable);
+ ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "index", "enable"), &PopupMenu::set_item_as_radio_checkable);
+ ClassDB::bind_method(D_METHOD("set_item_tooltip", "index", "tooltip"), &PopupMenu::set_item_tooltip);
+ ClassDB::bind_method(D_METHOD("set_item_shortcut", "index", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("set_item_multistate", "index", "state"), &PopupMenu::set_item_multistate);
+ ClassDB::bind_method(D_METHOD("set_item_shortcut_disabled", "index", "disabled"), &PopupMenu::set_item_shortcut_disabled);
+
+ ClassDB::bind_method(D_METHOD("toggle_item_checked", "index"), &PopupMenu::toggle_item_checked);
+ ClassDB::bind_method(D_METHOD("toggle_item_multistate", "index"), &PopupMenu::toggle_item_multistate);
+
+ ClassDB::bind_method(D_METHOD("get_item_text", "index"), &PopupMenu::get_item_text);
+ ClassDB::bind_method(D_METHOD("get_item_text_direction", "index"), &PopupMenu::get_item_text_direction);
+ ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "index", "tag"), &PopupMenu::get_item_opentype_feature);
+ ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "index"), &PopupMenu::clear_item_opentype_features);
+ ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language);
+ ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon);
+ ClassDB::bind_method(D_METHOD("is_item_checked", "index"), &PopupMenu::is_item_checked);
+ ClassDB::bind_method(D_METHOD("get_item_id", "index"), &PopupMenu::get_item_id);
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &PopupMenu::get_item_index);
- ClassDB::bind_method(D_METHOD("get_item_accelerator", "idx"), &PopupMenu::get_item_accelerator);
- ClassDB::bind_method(D_METHOD("get_item_metadata", "idx"), &PopupMenu::get_item_metadata);
- ClassDB::bind_method(D_METHOD("is_item_disabled", "idx"), &PopupMenu::is_item_disabled);
- ClassDB::bind_method(D_METHOD("get_item_submenu", "idx"), &PopupMenu::get_item_submenu);
- ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator);
- ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable);
- ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable);
- ClassDB::bind_method(D_METHOD("is_item_shortcut_disabled", "idx"), &PopupMenu::is_item_shortcut_disabled);
- ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip);
- ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
-
+ ClassDB::bind_method(D_METHOD("get_item_accelerator", "index"), &PopupMenu::get_item_accelerator);
+ ClassDB::bind_method(D_METHOD("get_item_metadata", "index"), &PopupMenu::get_item_metadata);
+ ClassDB::bind_method(D_METHOD("is_item_disabled", "index"), &PopupMenu::is_item_disabled);
+ ClassDB::bind_method(D_METHOD("get_item_submenu", "index"), &PopupMenu::get_item_submenu);
+ ClassDB::bind_method(D_METHOD("is_item_separator", "index"), &PopupMenu::is_item_separator);
+ ClassDB::bind_method(D_METHOD("is_item_checkable", "index"), &PopupMenu::is_item_checkable);
+ ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "index"), &PopupMenu::is_item_radio_checkable);
+ ClassDB::bind_method(D_METHOD("is_item_shortcut_disabled", "index"), &PopupMenu::is_item_shortcut_disabled);
+ ClassDB::bind_method(D_METHOD("get_item_tooltip", "index"), &PopupMenu::get_item_tooltip);
+ ClassDB::bind_method(D_METHOD("get_item_shortcut", "index"), &PopupMenu::get_item_shortcut);
+
+ ClassDB::bind_method(D_METHOD("set_current_index", "index"), &PopupMenu::set_current_index);
ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index);
ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count);
ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count);
- ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "index"), &PopupMenu::scroll_to_item);
+
+ ClassDB::bind_method(D_METHOD("remove_item", "index"), &PopupMenu::remove_item);
ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
@@ -1762,7 +1898,7 @@ void PopupMenu::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
- ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
+ ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 22912fb59c..12587b7e73 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -87,9 +87,10 @@ class PopupMenu : public Popup {
};
bool close_allowed = false;
+ bool activated_by_keyboard = false;
Timer *minimum_lifetime_timer = nullptr;
- Timer *submenu_timer;
+ Timer *submenu_timer = nullptr;
List<Rect2> autohide_areas;
Vector<Item> items;
MouseButton initial_button_mask = MouseButton::NONE;
@@ -103,12 +104,11 @@ class PopupMenu : public Popup {
int _get_item_height(int p_item) const;
int _get_items_total_height() const;
- void _scroll_to_item(int p_item);
void _shape_item(int p_item);
virtual void gui_input(const Ref<InputEvent> &p_event);
- void _activate_submenu(int p_over);
+ void _activate_submenu(int p_over, bool p_by_keyboard = false);
void _submenu_timeout();
uint64_t popup_time_msec = 0;
@@ -117,7 +117,7 @@ class PopupMenu : public Popup {
bool hide_on_multistate_item_selection = false;
Vector2 moved;
- Map<Ref<Shortcut>, int> shortcut_refcount;
+ HashMap<Ref<Shortcut>, int> shortcut_refcount;
void _ref_shortcut(Ref<Shortcut> p_sc);
void _unref_shortcut(Ref<Shortcut> p_sc);
@@ -126,9 +126,9 @@ class PopupMenu : public Popup {
uint64_t search_time_msec = 0;
String search_string = "";
- MarginContainer *margin_container;
- ScrollContainer *scroll_container;
- Control *control;
+ MarginContainer *margin_container = nullptr;
+ ScrollContainer *scroll_container = nullptr;
+ Control *control = nullptr;
void _draw_items();
void _draw_background();
@@ -212,11 +212,14 @@ public:
Ref<Shortcut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
+ void set_current_index(int p_idx);
int get_current_index() const;
void set_item_count(int p_count);
int get_item_count() const;
+ void scroll_to_item(int p_item);
+
bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false);
void activate_item(int p_item);
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index 2cfaaa2fde..f36682942f 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "progress_bar.h"
+
#include "scene/resources/text_line.h"
Size2 ProgressBar::get_minimum_size() const {
@@ -52,39 +53,78 @@ Size2 ProgressBar::get_minimum_size() const {
}
void ProgressBar::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
- Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"));
- Ref<Font> font = get_theme_font(SNAME("font"));
- int font_size = get_theme_font_size(SNAME("font_size"));
- Color font_color = get_theme_color(SNAME("font_color"));
-
- draw_style_box(bg, Rect2(Point2(), get_size()));
- float r = get_as_ratio();
- int mp = fg->get_minimum_size().width;
- int p = r * (get_size().width - mp);
- if (p > 0) {
- if (is_layout_rtl()) {
- draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height)));
- } else {
- draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
+ Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"));
+ Ref<Font> font = get_theme_font(SNAME("font"));
+ int font_size = get_theme_font_size(SNAME("font_size"));
+ Color font_color = get_theme_color(SNAME("font_color"));
+
+ draw_style_box(bg, Rect2(Point2(), get_size()));
+
+ float r = get_as_ratio();
+
+ switch (mode) {
+ case FILL_BEGIN_TO_END:
+ case FILL_END_TO_BEGIN: {
+ int mp = fg->get_minimum_size().width;
+ int p = round(r * (get_size().width - mp));
+ // We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL,
+ // and left to right otherwise. And likewise for FILL_END_TO_BEGIN.
+ bool right_to_left = is_layout_rtl() ? (mode == FILL_BEGIN_TO_END) : (mode == FILL_END_TO_BEGIN);
+ if (p > 0) {
+ if (right_to_left) {
+ int p_remaining = round((1.0 - r) * (get_size().width - mp));
+ draw_style_box(fg, Rect2(Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ } else {
+ draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ }
+ }
+ } break;
+ case FILL_TOP_TO_BOTTOM:
+ case FILL_BOTTOM_TO_TOP: {
+ int mp = fg->get_minimum_size().height;
+ int p = round(r * (get_size().height - mp));
+
+ if (p > 0) {
+ if (mode == FILL_TOP_TO_BOTTOM) {
+ draw_style_box(fg, Rect2(Point2(0, 0), Size2(get_size().width, p + fg->get_minimum_size().height)));
+ } else {
+ int p_remaining = round((1.0 - r) * (get_size().height - mp));
+ draw_style_box(fg, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + fg->get_minimum_size().height)));
+ }
+ }
+ } break;
+ case FILL_MODE_MAX:
+ break;
}
- }
-
- if (percent_visible) {
- String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign();
- TextLine tl = TextLine(txt, font, font_size);
- Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round();
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
- if (outline_size > 0 && font_outline_color.a > 0) {
- tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color);
+
+ if (percent_visible) {
+ String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign();
+ TextLine tl = TextLine(txt, font, font_size);
+ Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round();
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color);
+ }
+ tl.draw(get_canvas_item(), text_pos, font_color);
}
- tl.draw(get_canvas_item(), text_pos, font_color);
- }
+ } break;
}
}
+void ProgressBar::set_fill_mode(int p_fill) {
+ ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX);
+ mode = (FillMode)p_fill;
+ update();
+}
+
+int ProgressBar::get_fill_mode() {
+ return mode;
+}
+
void ProgressBar::set_percent_visible(bool p_visible) {
percent_visible = p_visible;
update();
@@ -95,10 +135,18 @@ bool ProgressBar::is_percent_visible() const {
}
void ProgressBar::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &ProgressBar::set_fill_mode);
+ ClassDB::bind_method(D_METHOD("get_fill_mode"), &ProgressBar::get_fill_mode);
ClassDB::bind_method(D_METHOD("set_percent_visible", "visible"), &ProgressBar::set_percent_visible);
ClassDB::bind_method(D_METHOD("is_percent_visible"), &ProgressBar::is_percent_visible);
- ADD_GROUP("Percent", "percent_");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Begin to End,End to Begin,Top to Bottom,Bottom to Top"), "set_fill_mode", "get_fill_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "percent_visible"), "set_percent_visible", "is_percent_visible");
+
+ BIND_ENUM_CONSTANT(FILL_BEGIN_TO_END);
+ BIND_ENUM_CONSTANT(FILL_END_TO_BEGIN);
+ BIND_ENUM_CONSTANT(FILL_TOP_TO_BOTTOM);
+ BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP);
}
ProgressBar::ProgressBar() {
diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h
index fb6060d932..5ba21ad7d5 100644
--- a/scene/gui/progress_bar.h
+++ b/scene/gui/progress_bar.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,11 +43,27 @@ protected:
static void _bind_methods();
public:
+ enum FillMode {
+ FILL_BEGIN_TO_END,
+ FILL_END_TO_BEGIN,
+ FILL_TOP_TO_BOTTOM,
+ FILL_BOTTOM_TO_TOP,
+ FILL_MODE_MAX
+ };
+
+ void set_fill_mode(int p_fill);
+ int get_fill_mode();
+
void set_percent_visible(bool p_visible);
bool is_percent_visible() const;
Size2 get_minimum_size() const override;
ProgressBar();
+
+private:
+ FillMode mode = FILL_BEGIN_TO_END;
};
+VARIANT_ENUM_CAST(ProgressBar::FillMode);
+
#endif // PROGRESS_BAR_H
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index c4f05a7975..73f19a8eda 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,12 +34,15 @@ TypedArray<String> Range::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (shared->exp_ratio && shared->min <= 0) {
- warnings.push_back(TTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."));
+ warnings.push_back(RTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."));
}
return warnings;
}
+void Range::_value_changed(double p_value) {
+ GDVIRTUAL_CALL(_value_changed, p_value);
+}
void Range::_value_changed_notify() {
_value_changed(shared->val);
emit_signal(SNAME("value_changed"), shared->val);
@@ -47,7 +50,7 @@ void Range::_value_changed_notify() {
}
void Range::Shared::emit_value_changed() {
- for (Set<Range *>::Element *E = owners.front(); E; E = E->next()) {
+ for (RBSet<Range *>::Element *E = owners.front(); E; E = E->next()) {
Range *r = E->get();
if (!r->is_inside_tree()) {
continue;
@@ -67,7 +70,7 @@ void Range::_validate_values() {
}
void Range::Shared::emit_changed(const char *p_what) {
- for (Set<Range *>::Element *E = owners.front(); E; E = E->next()) {
+ for (RBSet<Range *>::Element *E = owners.front(); E; E = E->next()) {
Range *r = E->get();
if (!r->is_inside_tree()) {
continue;
@@ -279,6 +282,8 @@ void Range::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_greater"), "set_allow_greater", "is_greater_allowed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_lesser"), "set_allow_lesser", "is_lesser_allowed");
+ GDVIRTUAL_BIND(_value_changed, "new_value");
+
ADD_LINKED_PROPERTY("min_value", "value");
ADD_LINKED_PROPERTY("min_value", "max_value");
ADD_LINKED_PROPERTY("min_value", "page");
diff --git a/scene/gui/range.h b/scene/gui/range.h
index 0dc702b19c..a59bfa9677 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,12 +45,12 @@ class Range : public Control {
bool exp_ratio = false;
bool allow_greater = false;
bool allow_lesser = false;
- Set<Range *> owners;
+ RBSet<Range *> owners;
void emit_value_changed();
void emit_changed(const char *p_what = "");
};
- Shared *shared;
+ Shared *shared = nullptr;
void _ref_shared(Shared *p_shared);
void _unref_shared();
@@ -62,12 +62,14 @@ class Range : public Control {
void _validate_values();
protected:
- virtual void _value_changed(double) {}
+ virtual void _value_changed(double p_value);
static void _bind_methods();
bool _rounded_values = false;
+ GDVIRTUAL1(_value_changed, double)
+
public:
void set_value(double p_val);
void set_min(double p_min);
diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp
index 6d7f2cfd57..ed79da5c22 100644
--- a/scene/gui/reference_rect.cpp
+++ b/scene/gui/reference_rect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,13 +33,15 @@
#include "core/config/engine.h"
void ReferenceRect::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- if (!is_inside_tree()) {
- return;
- }
- if (Engine::get_singleton()->is_editor_hint() || !editor_only) {
- draw_rect(Rect2(Point2(), get_size()), border_color, false, border_width);
- }
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ if (!is_inside_tree()) {
+ return;
+ }
+ if (Engine::get_singleton()->is_editor_hint() || !editor_only) {
+ draw_rect(Rect2(Point2(), get_size()), border_color, false, border_width);
+ }
+ } break;
}
}
diff --git a/scene/gui/reference_rect.h b/scene/gui/reference_rect.h
index 7097e83a15..4a2d328162 100644
--- a/scene/gui/reference_rect.h
+++ b/scene/gui/reference_rect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
index 076fa132c0..c9516ed6b9 100644
--- a/scene/gui/rich_text_effect.cpp
+++ b/scene/gui/rich_text_effect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index 5681f9b193..4532a812ee 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f191dfecb4..fa0c2ce12c 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,11 @@
#include "rich_text_label.h"
+#include "core/input/input_map.h"
#include "core/math/math_defs.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
+#include "label.h"
#include "scene/scene_string_names.h"
#include "servers/display_server.h"
@@ -141,7 +143,7 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item
for (Item *it = p_item_from; it && it != p_item_to; it = _get_next_item(it)) {
switch (it->type) {
case ITEM_TEXT: {
- ItemText *t = (ItemText *)it;
+ ItemText *t = static_cast<ItemText *>(it);
offset += t->text.length();
if (offset > p_position) {
return it;
@@ -165,16 +167,16 @@ String RichTextLabel::_roman(int p_num, bool p_capitalize) const {
};
String s;
if (p_capitalize) {
- String M[] = { "", "M", "MM", "MMM" };
- String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
- String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
- String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
+ const String M[] = { "", "M", "MM", "MMM" };
+ const String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
+ const String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
+ const String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10];
} else {
- String M[] = { "", "m", "mm", "mmm" };
- String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" };
- String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" };
- String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" };
+ const String M[] = { "", "m", "mm", "mmm" };
+ const String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" };
+ const String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" };
+ const String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" };
s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10];
}
return s;
@@ -205,6 +207,49 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const {
return s;
}
+void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) {
+ ERR_FAIL_COND(p_frame == nullptr);
+ ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
+
+ Line &l = p_frame->lines.write[p_line];
+
+ RID t = l.text_buf->get_rid();
+ int spans = TS->shaped_get_span_count(t);
+ 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;
+ }
+ int font_size = _find_font_size(it);
+ if (font_size == -1) {
+ font_size = p_base_font_size;
+ }
+ Dictionary font_ftr = _find_font_features(it);
+ TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font_ftr);
+ }
+ }
+
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ switch (it->type) {
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ for (Item *E : table->subitems) {
+ ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E);
+ for (int i = 0; i < frame->lines.size(); i++) {
+ _update_line_font(frame, i, p_base_font, p_base_font_size);
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) {
ERR_FAIL_COND(p_frame == nullptr);
ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
@@ -225,8 +270,8 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
switch (it->type) {
case ITEM_TABLE: {
ItemTable *table = static_cast<ItemTable *>(it);
- int hseparation = get_theme_constant(SNAME("table_hseparation"));
- int vseparation = get_theme_constant(SNAME("table_vseparation"));
+ int hseparation = get_theme_constant(SNAME("table_h_separation"));
+ int vseparation = get_theme_constant(SNAME("table_v_separation"));
int col_count = table->columns.size();
for (int i = 0; i < col_count; i++) {
@@ -238,7 +283,8 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
ItemFrame *frame = static_cast<ItemFrame *>(E);
for (int i = 0; i < frame->lines.size(); i++) {
- _resize_line(frame, i, p_base_font, p_base_font_size, 1);
+ int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1;
+ _resize_line(frame, i, p_base_font, p_base_font_size, w);
}
idx++;
}
@@ -264,7 +310,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand && total_ratio > 0) {
+ if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
table->total_width += table->columns[i].width + hseparation;
@@ -325,13 +371,16 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
if (i > 0) {
- frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
+ frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + frame->lines[i - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
} else {
frame->lines.write[i].offset.y = 0;
}
frame->lines.write[i].offset += offset;
- float h = frame->lines[i].text_buf->get_size().y;
+ float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation"));
+ if (i > 0) {
+ h += get_theme_constant(SNAME("line_separation"));
+ }
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
@@ -362,7 +411,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
if (p_line > 0) {
- l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
+ l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
} else {
l.offset.y = 0;
}
@@ -374,16 +423,31 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Line &l = p_frame->lines.write[p_line];
+ uint16_t autowrap_flags = TextServer::BREAK_MANDATORY;
+ switch (autowrap_mode) {
+ case AUTOWRAP_WORD_SMART:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_WORD:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_ARBITRARY:
+ autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_OFF:
+ break;
+ }
+
// Clear cache.
l.text_buf->clear();
- l.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
+ l.text_buf->set_flags(autowrap_flags | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
l.char_offset = *r_char_offset;
l.char_count = 0;
// Add indent.
l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size);
l.text_buf->set_width(p_width - l.offset.x);
- l.text_buf->set_align((HAlign)_find_align(l.from));
+ l.text_buf->set_alignment(_find_alignment(l.from));
l.text_buf->set_direction(_find_direction(l.from));
if (tab_size > 0) { // Align inline tabs.
@@ -397,21 +461,17 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
int remaining_characters = visible_characters - l.char_offset;
for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
- if (visible_characters >= 0 && remaining_characters <= 0) {
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters <= 0) {
break;
}
switch (it->type) {
case ITEM_DROPCAP: {
// Add dropcap.
- const ItemDropcap *dc = (ItemDropcap *)it;
- if (dc != nullptr) {
- l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins);
- l.dc_color = dc->color;
- l.dc_ol_size = dc->ol_size;
- l.dc_ol_color = dc->ol_color;
- } else {
- l.text_buf->clear_dropcap();
- }
+ const ItemDropcap *dc = static_cast<ItemDropcap *>(it);
+ l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins);
+ l.dc_color = dc->color;
+ l.dc_ol_size = dc->ol_size;
+ l.dc_ol_color = dc->ol_color;
} break;
case ITEM_NEWLINE: {
Ref<Font> font = _find_font(it);
@@ -428,7 +488,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
remaining_characters--;
} break;
case ITEM_TEXT: {
- ItemText *t = (ItemText *)it;
+ ItemText *t = static_cast<ItemText *>(it);
Ref<Font> font = _find_font(it);
if (font.is_null()) {
font = p_base_font;
@@ -440,26 +500,26 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Dictionary font_ftr = _find_font_features(it);
String lang = _find_language(it);
String tx = t->text;
- if (visible_characters >= 0 && remaining_characters >= 0) {
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters >= 0) {
tx = tx.substr(0, remaining_characters);
}
remaining_characters -= tx.length();
- l.text_buf->add_string(tx, font, font_size, font_ftr, lang);
+ l.text_buf->add_string(tx, font, font_size, font_ftr, lang, (uint64_t)it);
text += tx;
l.char_count += tx.length();
} break;
case ITEM_IMAGE: {
- ItemImage *img = (ItemImage *)it;
- l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1);
+ ItemImage *img = static_cast<ItemImage *>(it);
+ l.text_buf->add_object((uint64_t)it, img->size, img->inline_align, 1);
text += String::chr(0xfffc);
l.char_count++;
remaining_characters--;
} break;
case ITEM_TABLE: {
ItemTable *table = static_cast<ItemTable *>(it);
- int hseparation = get_theme_constant(SNAME("table_hseparation"));
- int vseparation = get_theme_constant(SNAME("table_vseparation"));
+ int hseparation = get_theme_constant(SNAME("table_h_separation"));
+ int vseparation = get_theme_constant(SNAME("table_v_separation"));
int col_count = table->columns.size();
int t_char_count = 0;
// Set minimums to zero.
@@ -479,7 +539,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
int column = idx % col_count;
for (int i = 0; i < frame->lines.size(); i++) {
int char_offset = l.char_offset + l.char_count;
- _shape_line(frame, i, p_base_font, p_base_font_size, 1, &char_offset);
+ int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1;
+ _shape_line(frame, i, p_base_font, p_base_font_size, w, &char_offset);
int cell_ch = (char_offset - (l.char_offset + l.char_count));
l.char_count += cell_ch;
t_char_count += cell_ch;
@@ -509,7 +570,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand && total_ratio > 0) {
+ if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
table->total_width += table->columns[i].width + hseparation;
@@ -570,13 +631,16 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
if (i > 0) {
- frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
+ frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + frame->lines[i - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
} else {
frame->lines.write[i].offset.y = 0;
}
frame->lines.write[i].offset += offset;
- float h = frame->lines[i].text_buf->get_size().y;
+ float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation"));
+ if (i > 0) {
+ h += get_theme_constant(SNAME("line_separation"));
+ }
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
@@ -615,23 +679,23 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
*r_char_offset = l.char_offset + l.char_count;
if (p_line > 0) {
- l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
+ l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
} else {
l.offset.y = 0;
}
}
-int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs) {
- Vector2 off;
-
+int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs) {
ERR_FAIL_COND_V(p_frame == nullptr, 0);
ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), 0);
+ Vector2 off;
+ int line_spacing = get_theme_constant(SNAME("line_separation"));
+
Line &l = p_frame->lines.write[p_line];
Item *it_from = l.from;
Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
- Variant meta;
if (it_from == nullptr) {
return 0;
@@ -641,6 +705,12 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
bool lrtl = is_layout_rtl();
+ bool trim_chars = (visible_characters >= 0) && (visible_chars_behavior == VC_CHARS_AFTER_SHAPING);
+ bool trim_glyphs_ltr = (visible_characters >= 0) && ((visible_chars_behavior == VC_GLYPHS_LTR) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && !lrtl));
+ bool trim_glyphs_rtl = (visible_characters >= 0) && ((visible_chars_behavior == VC_GLYPHS_RTL) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && lrtl));
+ int total_glyphs = (trim_glyphs_ltr || trim_glyphs_rtl) ? get_total_glyph_count() : 0;
+ int visible_glyphs = total_glyphs * percent_visible;
+
Vector<int> list_index;
Vector<ItemList *> list_items;
_find_list(l.from, list_index, list_items);
@@ -670,7 +740,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
prefix = segment + prefix;
}
}
- if (prefix != "") {
+ if (!prefix.is_empty()) {
Ref<Font> font = _find_font(l.from);
if (font.is_null()) {
font = get_theme_font(SNAME("normal_font"));
@@ -684,13 +754,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
if (!lrtl && p_frame == main) { // Skip Scrollbar.
offx -= scroll_w;
}
- font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HALIGN_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color));
+ font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HORIZONTAL_ALIGNMENT_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color));
} else {
float offx = 0.0f;
if (lrtl && p_frame == main) { // Skip Scrollbar.
offx += scroll_w;
}
- font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HALIGN_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color));
+ font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HORIZONTAL_ALIGNMENT_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color));
}
}
@@ -706,6 +776,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Size2 ctrl_size = get_size();
// Draw text.
for (int line = 0; line < l.text_buf->get_line_count(); line++) {
+ if (line > 0) {
+ off.y += line_spacing;
+ }
+
RID rid = l.text_buf->get_line_rid(line);
if (p_ofs.y + off.y >= ctrl_size.height) {
break;
@@ -734,17 +808,17 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
// Draw text.
- switch (l.text_buf->get_align()) {
- case HALIGN_FILL:
- case HALIGN_LEFT: {
+ switch (l.text_buf->get_alignment()) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
off.x += width - length;
}
} break;
- case HALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
off.x += Math::floor((width - length) / 2.0);
} break;
- case HALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (!rtl) {
off.x += width - length;
}
@@ -765,7 +839,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
// Draw inlined objects.
Array objects = TS->shaped_text_get_objects(rid);
for (int i = 0; i < objects.size(); i++) {
- Item *it = (Item *)(uint64_t)objects[i];
+ Item *it = reinterpret_cast<Item *>((uint64_t)objects[i]);
if (it != nullptr) {
Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
//draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS
@@ -779,7 +853,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Color odd_row_bg = get_theme_color(SNAME("table_odd_row_bg"));
Color even_row_bg = get_theme_color(SNAME("table_even_row_bg"));
Color border = get_theme_color(SNAME("table_border"));
- int hseparation = get_theme_constant(SNAME("table_hseparation"));
+ int hseparation = get_theme_constant(SNAME("table_h_separation"));
int col_count = table->columns.size();
int row_count = table->rows.size();
@@ -804,7 +878,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
for (int j = 0; j < frame->lines.size(); j++) {
- _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs);
+ _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs);
}
idx++;
}
@@ -820,12 +894,14 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Vector2 gloff = off;
// Draw oulines and shadow.
+ int processed_glyphs_ol = r_processed_glyphs;
for (int i = 0; i < gl_size; i++) {
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
int size = _find_outline_size(it, p_outline_size);
- Color font_color = _find_outline_color(it, p_outline_color);
+ Color font_color = _find_color(it, p_base_color);
+ Color font_outline_color = _find_outline_color(it, p_outline_color);
Color font_shadow_color = p_font_shadow_color;
- if ((size <= 0 || font_color.a == 0) && (font_shadow_color.a == 0)) {
+ if ((size <= 0 || font_outline_color.a == 0) && (font_shadow_color.a == 0)) {
gloff.x += glyphs[i].advance;
continue;
}
@@ -865,17 +941,17 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
//Apply fx.
- float faded_visibility = 1.0f;
if (fade) {
+ float faded_visibility = 1.0f;
if (glyphs[i].start >= fade->starting_index) {
faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
}
- font_color.a = faded_visibility;
+ font_outline_color.a = faded_visibility;
font_shadow_color.a = faded_visibility;
}
- bool visible = (font_color.a != 0) || (font_shadow_color.a != 0);
+ bool visible = (font_outline_color.a != 0) || (font_shadow_color.a != 0);
for (int j = 0; j < fx_stack.size(); j++) {
ItemFX *item_fx = fx_stack[j];
@@ -945,19 +1021,23 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
// Draw glyph outlines.
+ const Color modulated_outline_color = font_outline_color * Color(1, 1, 1, font_color.a);
+ const Color modulated_shadow_color = font_shadow_color * Color(1, 1, 1, font_color.a);
for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
- if (frid != RID()) {
- if (font_shadow_color.a > 0) {
- TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
+ if (!skip && frid != RID()) {
+ if (modulated_shadow_color.a > 0) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, modulated_shadow_color);
}
- if (font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ if (modulated_shadow_color.a > 0 && p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, modulated_shadow_color);
}
- if (font_color.a != 0.0 && size > 0) {
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
+ if (modulated_outline_color.a != 0.0 && size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, modulated_outline_color);
}
}
+ processed_glyphs_ol++;
}
gloff.x += glyphs[i].advance;
}
@@ -986,22 +1066,60 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
+ Vector2 ul_start;
+ bool ul_started = false;
+ Color ul_color;
+
+ Vector2 dot_ul_start;
+ bool dot_ul_started = false;
+ Color dot_ul_color;
+
+ Vector2 st_start;
+ bool st_started = false;
+ Color st_color;
+
for (int i = 0; i < gl_size; i++) {
bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end);
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
Color font_color = _find_color(it, p_base_color);
- if (_find_underline(it) || (_find_meta(it, &meta) && underline_meta)) {
- Color uc = font_color;
- uc.a *= 0.5;
+ if (_find_underline(it) || (_find_meta(it, nullptr) && underline_meta)) {
+ if (!ul_started) {
+ ul_started = true;
+ ul_start = p_ofs + Vector2(off.x, off.y);
+ ul_color = font_color;
+ ul_color.a *= 0.5;
+ }
+ } else if (ul_started) {
+ ul_started = false;
+ float y_off = TS->shaped_text_get_underline_position(rid);
+ float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
+ draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
+ }
+ if (_find_hint(it, nullptr) && underline_hint) {
+ if (!dot_ul_started) {
+ dot_ul_started = true;
+ dot_ul_start = p_ofs + Vector2(off.x, off.y);
+ dot_ul_color = font_color;
+ dot_ul_color.a *= 0.5;
+ }
+ } else if (dot_ul_started) {
+ dot_ul_started = false;
float y_off = TS->shaped_text_get_underline_position(rid);
float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
- draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
- } else if (_find_strikethrough(it)) {
- Color uc = font_color;
- uc.a *= 0.5;
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, underline_width * 2);
+ }
+ if (_find_strikethrough(it)) {
+ if (!st_started) {
+ st_started = true;
+ st_start = p_ofs + Vector2(off.x, off.y);
+ st_color = font_color;
+ st_color.a *= 0.5;
+ }
+ } else if (st_started) {
+ st_started = false;
float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
- draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
+ draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
}
// Get FX.
@@ -1039,8 +1157,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
//Apply fx.
- float faded_visibility = 1.0f;
if (fade) {
+ float faded_visibility = 1.0f;
if (glyphs[i].start >= fade->starting_index) {
faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
@@ -1124,15 +1242,37 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
// Draw glyphs.
for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
- if (frid != RID()) {
- TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
- } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
+ bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs));
+ if (!skip) {
+ if (frid != RID()) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
+ } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
+ }
}
+ r_processed_glyphs++;
}
off.x += glyphs[i].advance;
}
}
+ if (ul_started) {
+ ul_started = false;
+ float y_off = TS->shaped_text_get_underline_position(rid);
+ float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
+ draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
+ }
+ if (dot_ul_started) {
+ dot_ul_started = false;
+ float y_off = TS->shaped_text_get_underline_position(rid);
+ float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, underline_width * 2);
+ }
+ if (st_started) {
+ st_started = false;
+ float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
+ float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
+ draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
+ }
// Draw foreground color box
_draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 1);
@@ -1163,7 +1303,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
//TODO, change to binary search ?
while (from_line < main->lines.size()) {
- if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) {
+ if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) {
break;
}
from_line++;
@@ -1176,7 +1316,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
while (ofs.y < size.height && from_line < main->lines.size()) {
_find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
- ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
+ ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
if (r_outside != nullptr) {
*r_outside = false;
@@ -1187,14 +1327,22 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
}
}
-float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char) {
+float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool p_table) {
Vector2 off;
int char_pos = -1;
Line &l = p_frame->lines.write[p_line];
bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
bool lrtl = is_layout_rtl();
+
+ // Table hit test results.
bool table_hit = false;
+ Vector2i table_range;
+ float table_offy = 0.f;
+ ItemFrame *table_click_frame = nullptr;
+ int table_click_line = -1;
+ Item *table_click_item = nullptr;
+ int table_click_char = -1;
for (int line = 0; line < l.text_buf->get_line_count(); line++) {
RID rid = l.text_buf->get_line_rid(line);
@@ -1214,17 +1362,17 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
}
- switch (l.text_buf->get_align()) {
- case HALIGN_FILL:
- case HALIGN_LEFT: {
+ switch (l.text_buf->get_alignment()) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
off.x += width - length;
}
} break;
- case HALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
off.x += Math::floor((width - length) / 2.0);
} break;
- case HALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (!rtl) {
off.x += width - length;
}
@@ -1235,19 +1383,18 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
Array objects = TS->shaped_text_get_objects(rid);
for (int i = 0; i < objects.size(); i++) {
- Item *it = (Item *)(uint64_t)objects[i];
+ Item *it = reinterpret_cast<Item *>((uint64_t)objects[i]);
if (it != nullptr) {
Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
- if (rect.has_point(p_click - p_ofs - off)) {
+ rect.position += p_ofs + off;
+ if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) {
switch (it->type) {
case ITEM_TABLE: {
- int hseparation = get_theme_constant(SNAME("table_hseparation"));
- int vseparation = get_theme_constant(SNAME("table_vseparation"));
+ int hseparation = get_theme_constant(SNAME("table_h_separation"));
+ int vseparation = get_theme_constant(SNAME("table_v_separation"));
ItemTable *table = static_cast<ItemTable *>(it);
- table_hit = true;
-
int idx = 0;
int col_count = table->columns.size();
int row_count = table->rows.size();
@@ -1263,7 +1410,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
if (rtl) {
coff.x = rect.size.width - table->columns[col].width - coff.x;
}
- Rect2 crect = Rect2(p_ofs + off + rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size);
+ Rect2 crect = Rect2(rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size);
if (col == col_count - 1) {
if (rtl) {
crect.size.x = crect.position.x + crect.size.x;
@@ -1274,9 +1421,19 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
if (crect.has_point(p_click)) {
for (int j = 0; j < frame->lines.size(); j++) {
- _find_click_in_line(frame, j, p_ofs + off + rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
- if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
- return off.y;
+ _find_click_in_line(frame, j, rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, &table_click_frame, &table_click_line, &table_click_item, &table_click_char, true);
+ if (table_click_frame && table_click_item) {
+ // Save cell detected cell hit data.
+ table_range = Vector2i(INT32_MAX, 0);
+ for (Item *F : table->subitems) {
+ ItemFrame *sub_frame = static_cast<ItemFrame *>(F);
+ for (int k = 0; k < sub_frame->lines.size(); k++) {
+ table_range.x = MIN(table_range.x, sub_frame->lines[k].char_offset);
+ table_range.y = MAX(table_range.y, sub_frame->lines[k].char_offset + sub_frame->lines[k].char_count);
+ }
+ }
+ table_offy = off.y;
+ table_hit = true;
}
}
}
@@ -1290,14 +1447,39 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
}
}
- Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)), Size2(get_size().x, TS->shaped_text_get_size(rid).y));
+ Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)) - p_frame->padding.position, TS->shaped_text_get_size(rid) + p_frame->padding.position + p_frame->padding.size);
+ if (p_table) {
+ rect.size.y += get_theme_constant(SNAME("table_v_separation"));
+ }
- if (rect.has_point(p_click) && !table_hit) {
+ if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) {
char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
}
- off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom();
+
+ // If table hit was detected, and line hit is in the table bounds use table hit.
+ if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) {
+ if (r_click_frame != nullptr) {
+ *r_click_frame = table_click_frame;
+ }
+
+ if (r_click_line != nullptr) {
+ *r_click_line = table_click_line;
+ }
+
+ if (r_click_item != nullptr) {
+ *r_click_item = table_click_item;
+ }
+
+ if (r_click_char != nullptr) {
+ *r_click_char = table_click_char;
+ }
+ return table_offy;
+ }
+
+ off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom() + get_theme_constant(SNAME("line_separation"));
}
+ // Text line hit.
if (char_pos >= 0) {
// Find item.
if (r_click_item != nullptr) {
@@ -1308,7 +1490,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
if (it_to != nullptr) {
*r_click_item = _get_prev_item(it_to);
} else {
- for (Item *i = it; i && i != it_to; i = _get_next_item(i)) {
+ for (Item *i = it; i; i = _get_next_item(i)) {
*r_click_item = i;
}
}
@@ -1416,25 +1598,32 @@ void RichTextLabel::_notification(int p_what) {
update();
}
} break;
+
case NOTIFICATION_RESIZED: {
main->first_resized_line = 0; //invalidate ALL
update();
+ } break;
+ case NOTIFICATION_THEME_CHANGED: {
+ main->first_invalid_font_line = 0; //invalidate ALL
+ update();
} break;
- case NOTIFICATION_THEME_CHANGED:
+
case NOTIFICATION_ENTER_TREE: {
- if (text != "") {
+ if (!text.is_empty()) {
set_text(text);
}
main->first_invalid_line = 0; //invalidate ALL
update();
} break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
main->first_invalid_line = 0; //invalidate ALL
update();
} break;
+
case NOTIFICATION_DRAW: {
_validate_line_caches(main);
_update_scroll();
@@ -1459,7 +1648,7 @@ void RichTextLabel::_notification(int p_what) {
//TODO, change to binary search ?
while (from_line < main->lines.size()) {
- if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) {
+ if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) {
break;
}
from_line++;
@@ -1481,13 +1670,15 @@ void RichTextLabel::_notification(int p_what) {
// New cache draw.
Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
+ int processed_glyphs = 0;
while (ofs.y < size.height && from_line < main->lines.size()) {
visible_paragraph_count++;
- visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs);
- ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
+ visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs, processed_glyphs);
+ ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
from_line++;
}
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
if (is_visible_in_tree()) {
double dt = get_process_delta_time();
@@ -1495,12 +1686,16 @@ void RichTextLabel::_notification(int p_what) {
update();
}
} break;
+
case NOTIFICATION_FOCUS_EXIT: {
if (deselect_on_focus_loss_enabled) {
- selection.active = false;
- update();
+ deselect();
}
} break;
+
+ case NOTIFICATION_DRAG_END: {
+ selection.drag_attempt = false;
+ } break;
}
}
@@ -1517,15 +1712,19 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
return get_default_cursor_shape(); //invalid
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return get_default_cursor_shape(); //invalid
+ }
+
if (main->first_resized_line < main->lines.size()) {
return get_default_cursor_shape(); //invalid
}
Item *item = nullptr;
bool outside = true;
- ((RichTextLabel *)(this))->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside);
+ const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside);
- if (item && !outside && ((RichTextLabel *)(this))->_find_meta(item, nullptr)) {
+ if (item && !outside && const_cast<RichTextLabel *>(this)->_find_meta(item, nullptr)) {
return CURSOR_POINTING_HAND;
}
@@ -1541,6 +1740,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
if (main->first_invalid_line < main->lines.size()) {
return;
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return;
+ }
if (main->first_resized_line < main->lines.size()) {
return;
}
@@ -1554,6 +1756,8 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
int c_index = 0;
bool outside;
+ selection.drag_attempt = false;
+
_find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
if (c_item != nullptr) {
if (selection.enabled) {
@@ -1564,17 +1768,20 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
// Erase previous selection.
if (selection.active) {
- selection.from_frame = nullptr;
- selection.from_line = 0;
- selection.from_item = nullptr;
- selection.from_char = 0;
- selection.to_frame = nullptr;
- selection.to_line = 0;
- selection.to_item = nullptr;
- selection.to_char = 0;
- selection.active = false;
-
- update();
+ if (_is_click_inside_selection()) {
+ selection.drag_attempt = true;
+ selection.click_item = nullptr;
+ } else {
+ selection.from_frame = nullptr;
+ selection.from_line = 0;
+ selection.from_item = nullptr;
+ selection.from_char = 0;
+ selection.to_frame = nullptr;
+ selection.to_line = 0;
+ selection.to_item = nullptr;
+ selection.to_char = 0;
+ deselect();
+ }
}
}
}
@@ -1587,6 +1794,8 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
int c_index = 0;
bool outside;
+ selection.drag_attempt = false;
+
_find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
if (c_frame) {
@@ -1618,6 +1827,20 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
}
selection.click_item = nullptr;
+ if (selection.drag_attempt) {
+ selection.drag_attempt = false;
+ if (_is_click_inside_selection()) {
+ selection.from_frame = nullptr;
+ selection.from_line = 0;
+ selection.from_item = nullptr;
+ selection.from_char = 0;
+ selection.to_frame = nullptr;
+ selection.to_line = 0;
+ selection.to_item = nullptr;
+ selection.to_char = 0;
+ deselect();
+ }
+ }
if (!b->is_double_click() && !scroll_updated) {
Item *c_item = nullptr;
@@ -1646,6 +1869,13 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
}
+ if (b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
+ _generate_context_menu();
+ menu->set_position(get_screen_position() + b->get_position());
+ menu->reset_size();
+ menu->popup();
+ grab_focus();
+ }
}
Ref<InputEventPanGesture> pan_gesture = p_event;
@@ -1687,8 +1917,24 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
vscroll->set_value(vscroll->get_max());
handled = true;
}
- if (k->is_action("ui_copy")) {
- selection_copy();
+ if (is_shortcut_keys_enabled()) {
+ if (k->is_action("ui_text_select_all")) {
+ select_all();
+ handled = true;
+ }
+ if (k->is_action("ui_copy")) {
+ selection_copy();
+ handled = true;
+ }
+ }
+ if (k->is_action("ui_menu", true)) {
+ if (context_menu_enabled) {
+ _generate_context_menu();
+ menu->set_position(get_screen_position());
+ menu->reset_size();
+ menu->popup();
+ menu->grab_focus();
+ }
handled = true;
}
@@ -1704,6 +1950,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
if (main->first_invalid_line < main->lines.size()) {
return;
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return;
+ }
if (main->first_resized_line < main->lines.size()) {
return;
}
@@ -1727,14 +1976,13 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
selection.to_char = c_index;
bool swap = false;
- if (selection.from_item->index > selection.to_item->index) {
- swap = true;
- } else if (selection.from_item->index == selection.to_item->index) {
- if (selection.from_char > selection.to_char) {
+ if (selection.click_frame && c_frame) {
+ const Line &l1 = c_frame->lines[c_line];
+ const Line &l2 = selection.click_frame->lines[selection.click_line];
+ if (l1.char_offset + c_index < l2.char_offset + selection.click_char) {
swap = true;
- } else if (selection.from_char == selection.to_char) {
- selection.active = false;
- update();
+ } else if (l1.char_offset + c_index == l2.char_offset + selection.click_char) {
+ deselect();
return;
}
}
@@ -1769,6 +2017,20 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
}
}
+String RichTextLabel::get_tooltip(const Point2 &p_pos) const {
+ Item *c_item = nullptr;
+ bool outside;
+
+ const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &c_item, nullptr, &outside);
+
+ String description;
+ if (c_item && !outside && const_cast<RichTextLabel *>(this)->_find_hint(c_item, &description)) {
+ return description;
+ } else {
+ return Control::get_tooltip(p_pos);
+ }
+}
+
void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) {
if (r_frame != nullptr) {
*r_frame = nullptr;
@@ -1782,7 +2044,7 @@ void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line)
while (item) {
if (item->parent != nullptr && item->parent->type == ITEM_FRAME) {
if (r_frame != nullptr) {
- *r_frame = (ItemFrame *)item->parent;
+ *r_frame = static_cast<ItemFrame *>(item->parent);
}
if (r_line != nullptr) {
*r_line = item->line;
@@ -1950,19 +2212,19 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int
return margin;
}
-RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) {
+HorizontalAlignment RichTextLabel::_find_alignment(Item *p_item) {
Item *item = p_item;
while (item) {
if (item->type == ITEM_PARAGRAPH) {
ItemParagraph *p = static_cast<ItemParagraph *>(item);
- return p->align;
+ return p->alignment;
}
item = item->parent;
}
- return default_align;
+ return default_alignment;
}
TextServer::Direction RichTextLabel::_find_direction(Item *p_item) {
@@ -1986,7 +2248,7 @@ TextServer::Direction RichTextLabel::_find_direction(Item *p_item) {
}
}
-Control::StructuredTextParser RichTextLabel::_find_stt(Item *p_item) {
+TextServer::StructuredTextParser RichTextLabel::_find_stt(Item *p_item) {
Item *item = p_item;
while (item) {
@@ -2106,6 +2368,24 @@ bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item)
return false;
}
+bool RichTextLabel::_find_hint(Item *p_item, String *r_description) {
+ Item *item = p_item;
+
+ while (item) {
+ if (item->type == ITEM_HINT) {
+ ItemHint *hint = static_cast<ItemHint *>(item);
+ if (r_description) {
+ *r_description = hint->description;
+ }
+ return true;
+ }
+
+ item = item->parent;
+ }
+
+ return false;
+}
+
Color RichTextLabel::_find_bgcolor(Item *p_item) {
Item *item = p_item;
@@ -2156,27 +2436,32 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
if (p_frame->first_invalid_line == p_frame->lines.size()) {
+ Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
+ int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
+
+ // Update fonts.
+ if (p_frame->first_invalid_font_line != p_frame->lines.size()) {
+ for (int i = p_frame->first_invalid_font_line; i < p_frame->lines.size(); i++) {
+ _update_line_font(p_frame, i, base_font, base_font_size);
+ }
+ p_frame->first_resized_line = p_frame->first_invalid_font_line;
+ p_frame->first_invalid_font_line = p_frame->lines.size();
+ }
+
if (p_frame->first_resized_line == p_frame->lines.size()) {
return;
}
// Resize lines without reshaping.
- Size2 size = get_size();
- if (fixed_width != -1) {
- size.width = fixed_width;
- }
Rect2 text_rect = _get_text_rect();
- Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
- int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
-
for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) {
_resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w);
}
int total_height = 0;
if (p_frame->lines.size()) {
- total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y;
+ total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
p_frame->first_resized_line = p_frame->lines.size();
@@ -2185,21 +2470,17 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
vscroll->set_max(total_height);
vscroll->set_page(text_rect.size.height);
if (scroll_follow && scroll_following) {
- vscroll->set_value(total_height - size.height);
+ vscroll->set_value(total_height);
}
updating_scroll = false;
if (fit_content_height) {
- minimum_size_changed();
+ update_minimum_size();
}
return;
}
// Shape invalid lines.
- Size2 size = get_size();
- if (fixed_width != -1) {
- size.width = fixed_width;
- }
Rect2 text_rect = _get_text_rect();
Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
@@ -2212,22 +2493,23 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
int total_height = 0;
if (p_frame->lines.size()) {
- total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y;
+ total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
p_frame->first_invalid_line = p_frame->lines.size();
p_frame->first_resized_line = p_frame->lines.size();
+ p_frame->first_invalid_font_line = p_frame->lines.size();
updating_scroll = true;
vscroll->set_max(total_height);
vscroll->set_page(text_rect.size.height);
if (scroll_follow && scroll_following) {
- vscroll->set_value(total_height - size.height);
+ vscroll->set_value(total_height);
}
updating_scroll = false;
if (fit_content_height) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -2297,7 +2579,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
p_item->index = current_idx++;
p_item->char_ofs = current_char_ofs;
if (p_item->type == ITEM_TEXT) {
- ItemText *t = (ItemText *)p_item;
+ ItemText *t = static_cast<ItemText *>(p_item);
current_char_ofs += t->text.length();
} else if (p_item->type == ITEM_IMAGE) {
current_char_ofs++;
@@ -2324,7 +2606,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
_invalidate_current_line(current_frame);
if (fixed_width != -1) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -2334,7 +2616,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
p_item->parent->subitems.erase(p_item);
// If a newline was erased, all lines AFTER the newline need to be decremented.
if (p_item->type == ITEM_NEWLINE) {
- current_frame->lines.remove(p_line);
+ current_frame->lines.remove_at(p_line);
for (int i = 0; i < current->subitems.size(); i++) {
if (current->subitems[i]->line > p_subitem_line) {
current->subitems[i]->line--;
@@ -2349,9 +2631,10 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
// Then remove the provided item itself.
p_item->parent->subitems.erase(p_item);
}
+ memdelete(p_item);
}
-void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlign p_align) {
+void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment) {
if (current->type == ITEM_TABLE) {
return;
}
@@ -2363,7 +2646,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width,
item->image = p_image;
item->color = p_color;
- item->inline_align = p_align;
+ item->inline_align = p_alignment;
if (p_width > 0) {
// custom width
@@ -2423,7 +2706,7 @@ bool RichTextLabel::remove_line(const int p_line) {
}
if (!had_newline) {
- current_frame->lines.remove(p_line);
+ current_frame->lines.remove_at(p_line);
if (current_frame->lines.size() == 0) {
current_frame->lines.resize(1);
}
@@ -2555,11 +2838,11 @@ void RichTextLabel::push_strikethrough() {
_add_item(item, true);
}
-void RichTextLabel::push_paragraph(Align p_align, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) {
+void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, TextServer::StructuredTextParser p_st_parser) {
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemParagraph *item = memnew(ItemParagraph);
- item->align = p_align;
+ item->alignment = p_alignment;
item->direction = p_direction;
item->language = p_language;
item->st_parser = p_st_parser;
@@ -2595,13 +2878,21 @@ void RichTextLabel::push_meta(const Variant &p_meta) {
_add_item(item, true);
}
-void RichTextLabel::push_table(int p_columns, InlineAlign p_align) {
+void RichTextLabel::push_hint(const String &p_string) {
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
+ ItemHint *item = memnew(ItemHint);
+
+ item->description = p_string;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment) {
ERR_FAIL_COND(p_columns < 1);
ItemTable *item = memnew(ItemTable);
item->columns.resize(p_columns);
item->total_width = 0;
- item->inline_align = p_align;
+ item->inline_align = p_alignment;
for (int i = 0; i < item->columns.size(); i++) {
item->columns.write[i].expand = false;
item->columns.write[i].expand_ratio = 1;
@@ -2742,11 +3033,10 @@ void RichTextLabel::clear() {
main->lines.clear();
main->lines.resize(1);
main->first_invalid_line = 0;
- update();
selection.click_frame = nullptr;
selection.click_item = nullptr;
- selection.active = false;
+ deselect();
current_idx = 1;
current_char_ofs = 0;
@@ -2755,7 +3045,7 @@ void RichTextLabel::clear() {
}
if (fixed_width != -1) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -2772,7 +3062,7 @@ int RichTextLabel::get_tab_size() const {
void RichTextLabel::set_fit_content_height(bool p_enabled) {
if (p_enabled != fit_content_height) {
fit_content_height = p_enabled;
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -2789,6 +3079,15 @@ bool RichTextLabel::is_meta_underlined() const {
return underline_meta;
}
+void RichTextLabel::set_hint_underline(bool p_underline) {
+ underline_hint = p_underline;
+ update();
+}
+
+bool RichTextLabel::is_hint_underlined() const {
+ return underline_hint;
+}
+
void RichTextLabel::set_override_selected_font_color(bool p_override_selected_font_color) {
override_selected_font_color = p_override_selected_font_color;
}
@@ -2878,7 +3177,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
// Find optional parameters.
String bbcode_name;
- typedef Map<String, String> OptionMap;
+ typedef HashMap<String, String> OptionMap;
OptionMap bbcode_options;
if (!split_tag_block.is_empty()) {
bbcode_name = split_tag_block[0];
@@ -2958,35 +3257,35 @@ void RichTextLabel::append_text(const String &p_bbcode) {
columns = 1;
}
- int align = INLINE_ALIGN_TOP;
+ int alignment = INLINE_ALIGNMENT_TOP;
if (subtag.size() > 2) {
if (subtag[1] == "top" || subtag[1] == "t") {
- align = INLINE_ALIGN_TOP_TO;
+ alignment = INLINE_ALIGNMENT_TOP_TO;
} else if (subtag[1] == "center" || subtag[1] == "c") {
- align = INLINE_ALIGN_CENTER_TO;
+ alignment = INLINE_ALIGNMENT_CENTER_TO;
} else if (subtag[1] == "bottom" || subtag[1] == "b") {
- align = INLINE_ALIGN_BOTTOM_TO;
+ alignment = INLINE_ALIGNMENT_BOTTOM_TO;
}
if (subtag[2] == "top" || subtag[2] == "t") {
- align |= INLINE_ALIGN_TO_TOP;
+ alignment |= INLINE_ALIGNMENT_TO_TOP;
} else if (subtag[2] == "center" || subtag[2] == "c") {
- align |= INLINE_ALIGN_TO_CENTER;
+ alignment |= INLINE_ALIGNMENT_TO_CENTER;
} else if (subtag[2] == "baseline" || subtag[2] == "l") {
- align |= INLINE_ALIGN_TO_BASELINE;
+ alignment |= INLINE_ALIGNMENT_TO_BASELINE;
} else if (subtag[2] == "bottom" || subtag[2] == "b") {
- align |= INLINE_ALIGN_TO_BOTTOM;
+ alignment |= INLINE_ALIGNMENT_TO_BOTTOM;
}
} else if (subtag.size() > 1) {
if (subtag[1] == "top" || subtag[1] == "t") {
- align = INLINE_ALIGN_TOP;
+ alignment = INLINE_ALIGNMENT_TOP;
} else if (subtag[1] == "center" || subtag[1] == "c") {
- align = INLINE_ALIGN_CENTER;
+ alignment = INLINE_ALIGNMENT_CENTER;
} else if (subtag[1] == "bottom" || subtag[1] == "b") {
- align = INLINE_ALIGN_BOTTOM;
+ alignment = INLINE_ALIGNMENT_BOTTOM;
}
}
- push_table(columns, (InlineAlign)align);
+ push_table(columns, (InlineAlignment)alignment);
pos = brk_end + 1;
tag_stack.push_front("table");
} else if (tag == "cell") {
@@ -3050,6 +3349,12 @@ void RichTextLabel::append_text(const String &p_bbcode) {
push_strikethrough();
pos = brk_end + 1;
tag_stack.push_front(tag);
+ } else if (tag == "lb") {
+ add_text("[");
+ pos = brk_end + 1;
+ } else if (tag == "rb") {
+ add_text("]");
+ pos = brk_end + 1;
} else if (tag == "lrm") {
add_text(String::chr(0x200E));
pos = brk_end + 1;
@@ -3099,15 +3404,19 @@ void RichTextLabel::append_text(const String &p_bbcode) {
add_text(String::chr(0x00AD));
pos = brk_end + 1;
} else if (tag == "center") {
- push_paragraph(ALIGN_CENTER);
+ push_paragraph(HORIZONTAL_ALIGNMENT_CENTER);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "fill") {
- push_paragraph(ALIGN_FILL);
+ push_paragraph(HORIZONTAL_ALIGNMENT_FILL);
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "left") {
+ push_paragraph(HORIZONTAL_ALIGNMENT_LEFT);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "right") {
- push_paragraph(ALIGN_RIGHT);
+ push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "ul") {
@@ -3119,7 +3428,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
indent_level++;
push_list(indent_level, LIST_NUMBERS, false);
pos = brk_end + 1;
- tag_stack.push_front(tag);
+ tag_stack.push_front("ol");
} else if (tag == "ol type=a") {
indent_level++;
push_list(indent_level, LIST_LETTERS, false);
@@ -3146,27 +3455,27 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "p") {
- push_paragraph(ALIGN_LEFT);
+ push_paragraph(HORIZONTAL_ALIGNMENT_LEFT);
pos = brk_end + 1;
tag_stack.push_front("p");
} else if (tag.begins_with("p ")) {
Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
- Align align = ALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED;
String lang;
- Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
for (int i = 0; i < subtag.size(); i++) {
Vector<String> subtag_a = subtag[i].split("=");
if (subtag_a.size() == 2) {
if (subtag_a[0] == "align") {
if (subtag_a[1] == "l" || subtag_a[1] == "left") {
- align = ALIGN_LEFT;
+ alignment = HORIZONTAL_ALIGNMENT_LEFT;
} else if (subtag_a[1] == "c" || subtag_a[1] == "center") {
- align = ALIGN_CENTER;
+ alignment = HORIZONTAL_ALIGNMENT_CENTER;
} else if (subtag_a[1] == "r" || subtag_a[1] == "right") {
- align = ALIGN_RIGHT;
+ alignment = HORIZONTAL_ALIGNMENT_RIGHT;
} else if (subtag_a[1] == "f" || subtag_a[1] == "fill") {
- align = ALIGN_FILL;
+ alignment = HORIZONTAL_ALIGNMENT_FILL;
}
} else if (subtag_a[0] == "dir" || subtag_a[0] == "direction") {
if (subtag_a[1] == "a" || subtag_a[1] == "auto") {
@@ -3180,24 +3489,24 @@ void RichTextLabel::append_text(const String &p_bbcode) {
lang = subtag_a[1];
} else if (subtag_a[0] == "st" || subtag_a[0] == "bidi_override") {
if (subtag_a[1] == "d" || subtag_a[1] == "default") {
- st_parser = STRUCTURED_TEXT_DEFAULT;
+ st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
} else if (subtag_a[1] == "u" || subtag_a[1] == "uri") {
- st_parser = STRUCTURED_TEXT_URI;
+ st_parser = TextServer::STRUCTURED_TEXT_URI;
} else if (subtag_a[1] == "f" || subtag_a[1] == "file") {
- st_parser = STRUCTURED_TEXT_FILE;
+ st_parser = TextServer::STRUCTURED_TEXT_FILE;
} else if (subtag_a[1] == "e" || subtag_a[1] == "email") {
- st_parser = STRUCTURED_TEXT_EMAIL;
+ st_parser = TextServer::STRUCTURED_TEXT_EMAIL;
} else if (subtag_a[1] == "l" || subtag_a[1] == "list") {
- st_parser = STRUCTURED_TEXT_LIST;
+ st_parser = TextServer::STRUCTURED_TEXT_LIST;
} else if (subtag_a[1] == "n" || subtag_a[1] == "none") {
- st_parser = STRUCTURED_TEXT_NONE;
+ st_parser = TextServer::STRUCTURED_TEXT_NONE;
} else if (subtag_a[1] == "c" || subtag_a[1] == "custom") {
- st_parser = STRUCTURED_TEXT_CUSTOM;
+ st_parser = TextServer::STRUCTURED_TEXT_CUSTOM;
}
}
}
}
- push_paragraph(align, dir, lang, st_parser);
+ push_paragraph(alignment, dir, lang, st_parser);
pos = brk_end + 1;
tag_stack.push_front("p");
} else if (tag == "url") {
@@ -3216,6 +3525,11 @@ void RichTextLabel::append_text(const String &p_bbcode) {
push_meta(url);
pos = brk_end + 1;
tag_stack.push_front("url");
+ } else if (tag.begins_with("hint=")) {
+ String description = tag.substr(5, tag.length());
+ push_hint(description);
+ pos = brk_end + 1;
+ tag_stack.push_front("hint");
} else if (tag.begins_with("dropcap")) {
Vector<String> subtag = tag.substr(5, tag.length()).split(" ");
Ref<Font> f = get_theme_font(SNAME("normal_font"));
@@ -3265,33 +3579,33 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = end;
tag_stack.push_front(bbcode_name);
} else if (tag.begins_with("img")) {
- int align = INLINE_ALIGN_CENTER;
+ int alignment = INLINE_ALIGNMENT_CENTER;
if (tag.begins_with("img=")) {
Vector<String> subtag = tag.substr(4, tag.length()).split(",");
if (subtag.size() > 1) {
if (subtag[0] == "top" || subtag[0] == "t") {
- align = INLINE_ALIGN_TOP_TO;
+ alignment = INLINE_ALIGNMENT_TOP_TO;
} else if (subtag[0] == "center" || subtag[0] == "c") {
- align = INLINE_ALIGN_CENTER_TO;
+ alignment = INLINE_ALIGNMENT_CENTER_TO;
} else if (subtag[0] == "bottom" || subtag[0] == "b") {
- align = INLINE_ALIGN_BOTTOM_TO;
+ alignment = INLINE_ALIGNMENT_BOTTOM_TO;
}
if (subtag[1] == "top" || subtag[1] == "t") {
- align |= INLINE_ALIGN_TO_TOP;
+ alignment |= INLINE_ALIGNMENT_TO_TOP;
} else if (subtag[1] == "center" || subtag[1] == "c") {
- align |= INLINE_ALIGN_TO_CENTER;
+ alignment |= INLINE_ALIGNMENT_TO_CENTER;
} else if (subtag[1] == "baseline" || subtag[1] == "l") {
- align |= INLINE_ALIGN_TO_BASELINE;
+ alignment |= INLINE_ALIGNMENT_TO_BASELINE;
} else if (subtag[1] == "bottom" || subtag[1] == "b") {
- align |= INLINE_ALIGN_TO_BOTTOM;
+ alignment |= INLINE_ALIGNMENT_TO_BOTTOM;
}
} else if (subtag.size() > 0) {
if (subtag[0] == "top" || subtag[0] == "t") {
- align = INLINE_ALIGN_TOP;
+ alignment = INLINE_ALIGNMENT_TOP;
} else if (subtag[0] == "center" || subtag[0] == "c") {
- align = INLINE_ALIGN_CENTER;
+ alignment = INLINE_ALIGNMENT_CENTER;
} else if (subtag[0] == "bottom" || subtag[0] == "b") {
- align = INLINE_ALIGN_BOTTOM;
+ alignment = INLINE_ALIGNMENT_BOTTOM;
}
}
}
@@ -3306,9 +3620,9 @@ void RichTextLabel::append_text(const String &p_bbcode) {
Ref<Texture2D> texture = ResourceLoader::load(image, "Texture2D");
if (texture.is_valid()) {
Color color = Color(1.0, 1.0, 1.0);
- OptionMap::Element *color_option = bbcode_options.find("color");
+ OptionMap::Iterator color_option = bbcode_options.find("color");
if (color_option) {
- color = Color::from_string(color_option->value(), color);
+ color = Color::from_string(color_option->value, color);
}
int width = 0;
@@ -3322,18 +3636,18 @@ void RichTextLabel::append_text(const String &p_bbcode) {
height = bbcode_value.substr(sep + 1).to_int();
}
} else {
- OptionMap::Element *width_option = bbcode_options.find("width");
+ OptionMap::Iterator width_option = bbcode_options.find("width");
if (width_option) {
- width = width_option->value().to_int();
+ width = width_option->value.to_int();
}
- OptionMap::Element *height_option = bbcode_options.find("height");
+ OptionMap::Iterator height_option = bbcode_options.find("height");
if (height_option) {
- height = height_option->value().to_int();
+ height = height_option->value.to_int();
}
}
- add_image(texture, width, height, color, (InlineAlign)align);
+ add_image(texture, width, height, color, (InlineAlignment)alignment);
}
pos = end;
@@ -3415,15 +3729,15 @@ void RichTextLabel::append_text(const String &p_bbcode) {
} else if (bbcode_name == "fade") {
int start_index = 0;
- OptionMap::Element *start_option = bbcode_options.find("start");
+ OptionMap::Iterator start_option = bbcode_options.find("start");
if (start_option) {
- start_index = start_option->value().to_int();
+ start_index = start_option->value.to_int();
}
int length = 10;
- OptionMap::Element *length_option = bbcode_options.find("length");
+ OptionMap::Iterator length_option = bbcode_options.find("length");
if (length_option) {
- length = length_option->value().to_int();
+ length = length_option->value.to_int();
}
push_fade(start_index, length);
@@ -3431,15 +3745,15 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front("fade");
} else if (bbcode_name == "shake") {
int strength = 5;
- OptionMap::Element *strength_option = bbcode_options.find("level");
+ OptionMap::Iterator strength_option = bbcode_options.find("level");
if (strength_option) {
- strength = strength_option->value().to_int();
+ strength = strength_option->value.to_int();
}
float rate = 20.0f;
- OptionMap::Element *rate_option = bbcode_options.find("rate");
+ OptionMap::Iterator rate_option = bbcode_options.find("rate");
if (rate_option) {
- rate = rate_option->value().to_float();
+ rate = rate_option->value.to_float();
}
push_shake(strength, rate);
@@ -3448,15 +3762,15 @@ void RichTextLabel::append_text(const String &p_bbcode) {
set_process_internal(true);
} else if (bbcode_name == "wave") {
float amplitude = 20.0f;
- OptionMap::Element *amplitude_option = bbcode_options.find("amp");
+ OptionMap::Iterator amplitude_option = bbcode_options.find("amp");
if (amplitude_option) {
- amplitude = amplitude_option->value().to_float();
+ amplitude = amplitude_option->value.to_float();
}
float period = 5.0f;
- OptionMap::Element *period_option = bbcode_options.find("freq");
+ OptionMap::Iterator period_option = bbcode_options.find("freq");
if (period_option) {
- period = period_option->value().to_float();
+ period = period_option->value.to_float();
}
push_wave(period, amplitude);
@@ -3465,15 +3779,15 @@ void RichTextLabel::append_text(const String &p_bbcode) {
set_process_internal(true);
} else if (bbcode_name == "tornado") {
float radius = 10.0f;
- OptionMap::Element *radius_option = bbcode_options.find("radius");
+ OptionMap::Iterator radius_option = bbcode_options.find("radius");
if (radius_option) {
- radius = radius_option->value().to_float();
+ radius = radius_option->value.to_float();
}
float frequency = 1.0f;
- OptionMap::Element *frequency_option = bbcode_options.find("freq");
+ OptionMap::Iterator frequency_option = bbcode_options.find("freq");
if (frequency_option) {
- frequency = frequency_option->value().to_float();
+ frequency = frequency_option->value.to_float();
}
push_tornado(frequency, radius);
@@ -3482,21 +3796,21 @@ void RichTextLabel::append_text(const String &p_bbcode) {
set_process_internal(true);
} else if (bbcode_name == "rainbow") {
float saturation = 0.8f;
- OptionMap::Element *saturation_option = bbcode_options.find("sat");
+ OptionMap::Iterator saturation_option = bbcode_options.find("sat");
if (saturation_option) {
- saturation = saturation_option->value().to_float();
+ saturation = saturation_option->value.to_float();
}
float value = 0.8f;
- OptionMap::Element *value_option = bbcode_options.find("val");
+ OptionMap::Iterator value_option = bbcode_options.find("val");
if (value_option) {
- value = value_option->value().to_float();
+ value = value_option->value.to_float();
}
float frequency = 1.0f;
- OptionMap::Element *frequency_option = bbcode_options.find("freq");
+ OptionMap::Iterator frequency_option = bbcode_options.find("freq");
if (frequency_option) {
- frequency = frequency_option->value().to_float();
+ frequency = frequency_option->value.to_float();
}
push_rainbow(saturation, value, frequency);
@@ -3527,7 +3841,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_pos + 1;
} else {
String identifier = expr[0];
- expr.remove(0);
+ expr.remove_at(0);
Dictionary properties = parse_expressions_for_values(expr);
Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier);
@@ -3581,7 +3895,7 @@ void RichTextLabel::scroll_to_line(int p_line) {
if ((line_count <= p_line) && (line_count + main->lines[i].text_buf->get_line_count() >= p_line)) {
float line_offset = 0.f;
for (int j = 0; j < p_line - line_count; j++) {
- line_offset += main->lines[i].text_buf->get_line_size(j).y;
+ line_offset += main->lines[i].text_buf->get_line_size(j).y + get_theme_constant(SNAME("line_separation"));
}
vscroll->set_value(main->lines[i].offset.y + line_offset);
return;
@@ -3590,6 +3904,28 @@ void RichTextLabel::scroll_to_line(int p_line) {
}
}
+float RichTextLabel::get_line_offset(int p_line) {
+ int line_count = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ if ((line_count <= p_line) && (p_line <= line_count + main->lines[i].text_buf->get_line_count())) {
+ float line_offset = 0.f;
+ for (int j = 0; j < p_line - line_count; j++) {
+ line_offset += main->lines[i].text_buf->get_line_size(j).y + get_theme_constant(SNAME("line_separation"));
+ }
+ return main->lines[i].offset.y + line_offset;
+ }
+ line_count += main->lines[i].text_buf->get_line_count();
+ }
+ return 0;
+}
+
+float RichTextLabel::get_paragraph_offset(int p_paragraph) {
+ if (0 <= p_paragraph && p_paragraph < main->lines.size()) {
+ return main->lines[p_paragraph].offset.y;
+ }
+ return 0;
+}
+
int RichTextLabel::get_line_count() const {
int line_count = 0;
for (int i = 0; i < main->lines.size(); i++) {
@@ -3609,8 +3945,7 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) {
selection.enabled = p_enabled;
if (!p_enabled) {
if (selection.active) {
- selection.active = false;
- update();
+ deselect();
}
set_focus_mode(FOCUS_NONE);
} else {
@@ -3621,8 +3956,30 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) {
void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) {
deselect_on_focus_loss_enabled = p_enabled;
if (p_enabled && selection.active && !has_focus()) {
- selection.active = false;
- update();
+ deselect();
+ }
+}
+
+Variant RichTextLabel::get_drag_data(const Point2 &p_point) {
+ if (selection.drag_attempt && selection.enabled) {
+ String t = get_selected_text();
+ Label *l = memnew(Label);
+ l->set_text(t);
+ set_drag_preview(l);
+ return t;
+ }
+
+ return Variant();
+}
+
+bool RichTextLabel::_is_click_inside_selection() const {
+ if (selection.active && selection.enabled && selection.click_frame && selection.from_frame && selection.to_frame) {
+ const Line &l_click = selection.click_frame->lines[selection.click_line];
+ const Line &l_from = selection.from_frame->lines[selection.from_line];
+ const Line &l_to = selection.to_frame->lines[selection.to_line];
+ return (l_click.char_offset + selection.click_char >= l_from.char_offset + selection.from_char) && (l_click.char_offset + selection.click_char <= l_to.char_offset + selection.to_char);
+ } else {
+ return false;
}
}
@@ -3663,7 +4020,7 @@ bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p
text += "\n";
} break;
case ITEM_TEXT: {
- ItemText *t = (ItemText *)it;
+ ItemText *t = static_cast<ItemText *>(it);
text += t->text;
} break;
case ITEM_IMAGE: {
@@ -3796,7 +4153,7 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p
if (it_to != nullptr) {
end_idx = it_to->index;
} else {
- for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ for (Item *it = l.from; it; it = _get_next_item(it)) {
end_idx = it->index + 1;
}
}
@@ -3838,6 +4195,32 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p
return text;
}
+void RichTextLabel::set_context_menu_enabled(bool p_enabled) {
+ context_menu_enabled = p_enabled;
+}
+
+bool RichTextLabel::is_context_menu_enabled() const {
+ return context_menu_enabled;
+}
+
+void RichTextLabel::set_shortcut_keys_enabled(bool p_enabled) {
+ shortcut_keys_enabled = p_enabled;
+}
+
+bool RichTextLabel::is_shortcut_keys_enabled() const {
+ return shortcut_keys_enabled;
+}
+
+// Context menu.
+PopupMenu *RichTextLabel::get_menu() const {
+ const_cast<RichTextLabel *>(this)->_generate_context_menu();
+ return menu;
+}
+
+bool RichTextLabel::is_menu_visible() const {
+ return menu && menu->is_visible();
+}
+
String RichTextLabel::get_selected_text() const {
if (!selection.active || !selection.enabled) {
return "";
@@ -3850,14 +4233,66 @@ String RichTextLabel::get_selected_text() const {
return text;
}
+void RichTextLabel::deselect() {
+ selection.active = false;
+ update();
+}
+
void RichTextLabel::selection_copy() {
String text = get_selected_text();
- if (text != "") {
+ if (!text.is_empty()) {
DisplayServer::get_singleton()->clipboard_set(text);
}
}
+void RichTextLabel::select_all() {
+ if (!selection.enabled) {
+ return;
+ }
+
+ Item *it = main;
+ Item *from_item = nullptr;
+ Item *to_item = nullptr;
+
+ while (it) {
+ if (it->type != ITEM_FRAME) {
+ if (!from_item) {
+ from_item = it;
+ } else {
+ to_item = it;
+ }
+ }
+ it = _get_next_item(it, true);
+ }
+ if (!from_item || !to_item) {
+ return;
+ }
+
+ ItemFrame *from_frame = nullptr;
+ int from_line = 0;
+ _find_frame(from_item, &from_frame, &from_line);
+ if (!from_frame) {
+ return;
+ }
+ ItemFrame *to_frame = nullptr;
+ int to_line = 0;
+ _find_frame(to_item, &to_frame, &to_line);
+ if (!to_frame) {
+ return;
+ }
+ selection.from_line = from_line;
+ selection.from_frame = from_frame;
+ selection.from_char = 0;
+ selection.from_item = from_item;
+ selection.to_line = to_line;
+ selection.to_frame = to_frame;
+ selection.to_char = to_frame->lines[to_line].char_count;
+ selection.to_item = to_item;
+ selection.active = true;
+ update();
+}
+
bool RichTextLabel::is_selection_enabled() const {
return selection.enabled;
}
@@ -3884,7 +4319,7 @@ int RichTextLabel::get_selection_to() const {
void RichTextLabel::set_text(const String &p_bbcode) {
text = p_bbcode;
- if (is_inside_tree() && use_bbcode) {
+ if (use_bbcode) {
parse_bbcode(p_bbcode);
} else { // raw text
clear();
@@ -3914,10 +4349,8 @@ String RichTextLabel::get_parsed_text() const {
Item *it = main;
while (it) {
if (it->type == ITEM_DROPCAP) {
- const ItemDropcap *dc = (ItemDropcap *)it;
- if (dc != nullptr) {
- text += dc->text;
- }
+ ItemDropcap *dc = static_cast<ItemDropcap *>(it);
+ text += dc->text;
} else if (it->type == ITEM_TEXT) {
ItemText *t = static_cast<ItemText *>(it);
text += t->text;
@@ -3943,7 +4376,7 @@ void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction)
}
}
-void RichTextLabel::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
+void RichTextLabel::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
if (st_parser != p_parser) {
st_parser = p_parser;
main->first_invalid_line = 0; //invalidate ALL
@@ -3952,7 +4385,7 @@ void RichTextLabel::set_structured_text_bidi_override(Control::StructuredTextPar
}
}
-Control::StructuredTextParser RichTextLabel::get_structured_text_bidi_override() const {
+TextServer::StructuredTextParser RichTextLabel::get_structured_text_bidi_override() const {
return st_parser;
}
@@ -3984,6 +4417,19 @@ String RichTextLabel::get_language() const {
return language;
}
+void RichTextLabel::set_autowrap_mode(RichTextLabel::AutowrapMode p_mode) {
+ if (autowrap_mode != p_mode) {
+ autowrap_mode = p_mode;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
+ }
+}
+
+RichTextLabel::AutowrapMode RichTextLabel::get_autowrap_mode() const {
+ return autowrap_mode;
+}
+
void RichTextLabel::set_percent_visible(float p_percent) {
if (percent_visible != p_percent) {
if (p_percent < 0 || p_percent >= 1) {
@@ -3993,8 +4439,10 @@ void RichTextLabel::set_percent_visible(float p_percent) {
visible_characters = get_total_character_count() * p_percent;
percent_visible = p_percent;
}
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ }
update();
}
}
@@ -4005,7 +4453,7 @@ float RichTextLabel::get_percent_visible() const {
void RichTextLabel::set_effects(Array p_effects) {
custom_effects = p_effects;
- if ((text != "") && use_bbcode) {
+ if ((!text.is_empty()) && use_bbcode) {
parse_bbcode(text);
}
}
@@ -4020,7 +4468,7 @@ void RichTextLabel::install_effect(const Variant effect) {
if (rteffect.is_valid()) {
custom_effects.push_back(effect);
- if ((text != "") && use_bbcode) {
+ if ((!text.is_empty()) && use_bbcode) {
parse_bbcode(text);
}
}
@@ -4029,14 +4477,22 @@ void RichTextLabel::install_effect(const Variant effect) {
int RichTextLabel::get_content_height() const {
int total_height = 0;
if (main->lines.size()) {
- total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y;
+ total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y + main->lines[main->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
return total_height;
}
+int RichTextLabel::get_content_width() const {
+ int total_width = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ total_width = MAX(total_width, main->lines[i].offset.x + main->lines[i].text_buf->get_size().x);
+ }
+ return total_width;
+}
+
#ifndef DISABLE_DEPRECATED
// People will be very angry, if their texts get erased, because of #39148. (3.x -> 4.0)
-// Altough some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty
+// Although some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty.
bool RichTextLabel::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "bbcode_text" && !((String)p_value).is_empty()) {
set_text(p_value);
@@ -4050,7 +4506,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parsed_text"), &RichTextLabel::get_parsed_text);
ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);
ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text);
- 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_ALIGN_CENTER));
+ 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);
@@ -4064,13 +4520,14 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color);
ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size);
ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color);
- ClassDB::bind_method(D_METHOD("push_paragraph", "align", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT));
+ ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(TextServer::STRUCTURED_TEXT_DEFAULT));
ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent);
ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize"), &RichTextLabel::push_list);
ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta);
+ ClassDB::bind_method(D_METHOD("push_hint", "description"), &RichTextLabel::push_hint);
ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline);
ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough);
- ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGN_TOP));
+ ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGNMENT_TOP));
ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0)));
ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color);
@@ -4093,9 +4550,15 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_language", "language"), &RichTextLabel::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &RichTextLabel::get_language);
+ ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &RichTextLabel::set_autowrap_mode);
+ ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &RichTextLabel::get_autowrap_mode);
+
ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline);
ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined);
+ ClassDB::bind_method(D_METHOD("set_hint_underline", "enable"), &RichTextLabel::set_hint_underline);
+ ClassDB::bind_method(D_METHOD("is_hint_underlined"), &RichTextLabel::is_hint_underlined);
+
ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &RichTextLabel::set_override_selected_font_color);
ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &RichTextLabel::is_overriding_selected_font_color);
@@ -4105,7 +4568,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow);
ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following);
- ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &RichTextLabel::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line);
ClassDB::bind_method(D_METHOD("scroll_to_paragraph", "paragraph"), &RichTextLabel::scroll_to_paragraph);
@@ -4119,13 +4582,21 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled);
ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled);
+ ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enabled"), &RichTextLabel::set_context_menu_enabled);
+ ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &RichTextLabel::is_context_menu_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enabled"), &RichTextLabel::set_shortcut_keys_enabled);
+ ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &RichTextLabel::is_shortcut_keys_enabled);
+
ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &RichTextLabel::set_deselect_on_focus_loss_enabled);
ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &RichTextLabel::is_deselect_on_focus_loss_enabled);
ClassDB::bind_method(D_METHOD("get_selection_from"), &RichTextLabel::get_selection_from);
ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to);
+ ClassDB::bind_method(D_METHOD("select_all"), &RichTextLabel::select_all);
ClassDB::bind_method(D_METHOD("get_selected_text"), &RichTextLabel::get_selected_text);
+ ClassDB::bind_method(D_METHOD("deselect"), &RichTextLabel::deselect);
ClassDB::bind_method(D_METHOD("parse_bbcode", "bbcode"), &RichTextLabel::parse_bbcode);
ClassDB::bind_method(D_METHOD("append_text", "bbcode"), &RichTextLabel::append_text);
@@ -4135,9 +4606,14 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &RichTextLabel::set_visible_characters);
ClassDB::bind_method(D_METHOD("get_visible_characters"), &RichTextLabel::get_visible_characters);
+ ClassDB::bind_method(D_METHOD("get_visible_characters_behavior"), &RichTextLabel::get_visible_characters_behavior);
+ ClassDB::bind_method(D_METHOD("set_visible_characters_behavior", "behavior"), &RichTextLabel::set_visible_characters_behavior);
+
ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &RichTextLabel::set_percent_visible);
ClassDB::bind_method(D_METHOD("get_percent_visible"), &RichTextLabel::get_percent_visible);
+ ClassDB::bind_method(D_METHOD("get_character_line", "character"), &RichTextLabel::get_character_line);
+ ClassDB::bind_method(D_METHOD("get_character_paragraph", "character"), &RichTextLabel::get_character_paragraph);
ClassDB::bind_method(D_METHOD("get_total_character_count"), &RichTextLabel::get_total_character_count);
ClassDB::bind_method(D_METHOD("set_use_bbcode", "enable"), &RichTextLabel::set_use_bbcode);
@@ -4150,6 +4626,10 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_visible_paragraph_count"), &RichTextLabel::get_visible_paragraph_count);
ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
+ ClassDB::bind_method(D_METHOD("get_content_width"), &RichTextLabel::get_content_width);
+
+ ClassDB::bind_method(D_METHOD("get_line_offset", "line"), &RichTextLabel::get_line_offset);
+ ClassDB::bind_method(D_METHOD("get_paragraph_offset", "paragraph"), &RichTextLabel::get_paragraph_offset);
ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values);
@@ -4157,28 +4637,36 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects);
ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
+ ClassDB::bind_method(D_METHOD("get_menu"), &RichTextLabel::get_menu);
+ ClassDB::bind_method(D_METHOD("is_menu_visible"), &RichTextLabel::is_menu_visible);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
+ // Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets.
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled");
-
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following");
-
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
-
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
-
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hint_underlined"), "set_hint_underline", "is_hint_underlined");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
+
+ // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied.
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
+ ADD_GROUP("Locale", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
@@ -4188,10 +4676,10 @@ void RichTextLabel::_bind_methods() {
ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- BIND_ENUM_CONSTANT(ALIGN_FILL);
+ BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
+ BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART);
BIND_ENUM_CONSTANT(LIST_NUMBERS);
BIND_ENUM_CONSTANT(LIST_LETTERS);
@@ -4222,8 +4710,28 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(ITEM_BGCOLOR);
BIND_ENUM_CONSTANT(ITEM_FGCOLOR);
BIND_ENUM_CONSTANT(ITEM_META);
+ BIND_ENUM_CONSTANT(ITEM_HINT);
BIND_ENUM_CONSTANT(ITEM_DROPCAP);
BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
+
+ BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING);
+ BIND_ENUM_CONSTANT(VC_CHARS_AFTER_SHAPING);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_AUTO);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_LTR);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_RTL);
+}
+
+RichTextLabel::VisibleCharactersBehavior RichTextLabel::get_visible_characters_behavior() const {
+ return visible_chars_behavior;
+}
+
+void RichTextLabel::set_visible_characters_behavior(RichTextLabel::VisibleCharactersBehavior p_behavior) {
+ if (visible_chars_behavior != p_behavior) {
+ visible_chars_behavior = p_behavior;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
+ }
}
void RichTextLabel::set_visible_characters(int p_visible) {
@@ -4237,8 +4745,10 @@ void RichTextLabel::set_visible_characters(int p_visible) {
percent_visible = (float)p_visible / (float)total_char_count;
}
}
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ }
update();
}
}
@@ -4247,6 +4757,36 @@ int RichTextLabel::get_visible_characters() const {
return visible_characters;
}
+int RichTextLabel::get_character_line(int p_char) {
+ int line_count = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) {
+ for (int j = 0; j < main->lines[i].text_buf->get_line_count(); j++) {
+ Vector2i range = main->lines[i].text_buf->get_line_range(j);
+ if (main->lines[i].char_offset + range.x < p_char && p_char <= main->lines[i].char_offset + range.y) {
+ return line_count;
+ }
+ line_count++;
+ }
+ } else {
+ line_count += main->lines[i].text_buf->get_line_count();
+ }
+ }
+ return -1;
+}
+
+int RichTextLabel::get_character_paragraph(int p_char) {
+ int para_count = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) {
+ return para_count;
+ } else {
+ para_count++;
+ }
+ }
+ return -1;
+}
+
int RichTextLabel::get_total_character_count() const {
// Note: Do not use line buffer "char_count", it includes only visible characters.
int tc = 0;
@@ -4266,9 +4806,25 @@ int RichTextLabel::get_total_character_count() const {
return tc;
}
+int RichTextLabel::get_total_glyph_count() const {
+ int tg = 0;
+ Item *it = main;
+ while (it) {
+ if (it->type == ITEM_FRAME) {
+ ItemFrame *f = static_cast<ItemFrame *>(it);
+ for (int i = 0; i < f->lines.size(); i++) {
+ tg += TS->shaped_text_get_glyph_count(f->lines[i].text_buf->get_rid());
+ }
+ }
+ it = _get_next_item(it, true);
+ }
+
+ return tg;
+}
+
void RichTextLabel::set_fixed_size_to_width(int p_width) {
fixed_width = p_width;
- minimum_size_changed();
+ update_minimum_size();
}
Size2 RichTextLabel::get_minimum_size() const {
@@ -4279,7 +4835,7 @@ Size2 RichTextLabel::get_minimum_size() const {
size.x += fixed_width;
}
- if (fixed_width != -1 || fit_content_height) {
+ if (fit_content_height) {
const_cast<RichTextLabel *>(this)->_validate_line_caches(main);
size.y += get_content_height();
}
@@ -4287,6 +4843,59 @@ Size2 RichTextLabel::get_minimum_size() const {
return size;
}
+// Context menu.
+void RichTextLabel::_generate_context_menu() {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu, false, INTERNAL_MODE_FRONT);
+
+ menu->connect("id_pressed", callable_mp(this, &RichTextLabel::_menu_option));
+ }
+
+ // Reorganize context menu.
+ menu->clear();
+ if (selection.enabled) {
+ menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE);
+ }
+}
+
+Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) {
+ const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action);
+ if (!events) {
+ return Key::NONE;
+ }
+
+ // Use first event in the list for the accelerator.
+ const List<Ref<InputEvent>>::Element *first_event = events->front();
+ if (!first_event) {
+ return Key::NONE;
+ }
+
+ const Ref<InputEventKey> event = first_event->get();
+ if (event.is_null()) {
+ return Key::NONE;
+ }
+
+ // Use physical keycode if non-zero
+ if (event->get_physical_keycode() != Key::NONE) {
+ return event->get_physical_keycode_with_modifiers();
+ } else {
+ return event->get_keycode_with_modifiers();
+ }
+}
+
+void RichTextLabel::_menu_option(int p_option) {
+ switch (p_option) {
+ case MENU_COPY: {
+ selection_copy();
+ } break;
+ case MENU_SELECT_ALL: {
+ select_all();
+ } break;
+ }
+}
+
void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag) {
Vector2i fbg_index = Vector2i(end, start);
Color last_color = Color(0, 0, 0, 0);
@@ -4294,7 +4903,7 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item
// Draw a box based on color tags associated with glyphs
for (int i = start; i < end; i++) {
Item *it = _get_item_at_pos(it_from, it_to, i);
- Color color = Color(0, 0, 0, 0);
+ Color color;
if (fbg_flag == 0) {
color = _find_bgcolor(it);
@@ -4420,7 +5029,7 @@ Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressi
return d;
}
-RichTextLabel::RichTextLabel() {
+RichTextLabel::RichTextLabel(const String &p_text) {
main = memnew(ItemFrame);
main->index = 0;
current = main;
@@ -4428,6 +5037,7 @@ RichTextLabel::RichTextLabel() {
main->lines.write[0].from = main;
main->first_invalid_line = 0;
main->first_resized_line = 0;
+ main->first_invalid_font_line = 0;
current_frame = main;
vscroll = memnew(VScrollBar);
@@ -4441,6 +5051,8 @@ RichTextLabel::RichTextLabel() {
vscroll->set_step(1);
vscroll->hide();
+ set_text(p_text);
+
set_clip_contents(true);
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 5b58f14d96..c6d0d0875d 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,6 +32,7 @@
#define RICH_TEXT_LABEL_H
#include "rich_text_effect.h"
+#include "scene/gui/popup_menu.h"
#include "scene/gui/scroll_bar.h"
#include "scene/resources/text_paragraph.h"
@@ -39,11 +40,11 @@ class RichTextLabel : public Control {
GDCLASS(RichTextLabel, Control);
public:
- enum Align {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_FILL
+ enum AutowrapMode {
+ AUTOWRAP_OFF,
+ AUTOWRAP_ARBITRARY,
+ AUTOWRAP_WORD,
+ AUTOWRAP_WORD_SMART
};
enum ListType {
@@ -78,10 +79,24 @@ public:
ITEM_BGCOLOR,
ITEM_FGCOLOR,
ITEM_META,
+ ITEM_HINT,
ITEM_DROPCAP,
ITEM_CUSTOMFX
};
+ enum VisibleCharactersBehavior {
+ VC_CHARS_BEFORE_SHAPING,
+ VC_CHARS_AFTER_SHAPING,
+ VC_GLYPHS_AUTO,
+ VC_GLYPHS_LTR,
+ VC_GLYPHS_RTL,
+ };
+
+ enum MenuItems {
+ MENU_COPY,
+ MENU_SELECT_ALL,
+ };
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -128,6 +143,7 @@ private:
Vector<Line> lines;
int first_invalid_line = 0;
+ int first_invalid_font_line = 0;
int first_resized_line = 0;
ItemFrame *parent_frame = nullptr;
@@ -160,7 +176,7 @@ private:
struct ItemImage : public Item {
Ref<Texture2D> image;
- InlineAlign inline_align = INLINE_ALIGN_CENTER;
+ InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
Size2 size;
Color color;
ItemImage() { type = ITEM_IMAGE; }
@@ -209,11 +225,16 @@ private:
ItemMeta() { type = ITEM_META; }
};
+ struct ItemHint : public Item {
+ String description;
+ ItemHint() { type = ITEM_HINT; }
+ };
+
struct ItemParagraph : public Item {
- Align align = ALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
String language;
Control::TextDirection direction = Control::TEXT_DIRECTION_AUTO;
- Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
ItemParagraph() { type = ITEM_PARAGRAPH; }
};
@@ -247,7 +268,7 @@ private:
int total_width = 0;
int total_height = 0;
- InlineAlign inline_align = INLINE_ALIGN_TOP;
+ InlineAlignment inline_align = INLINE_ALIGNMENT_TOP;
ItemTable() { type = ITEM_TABLE; }
};
@@ -344,6 +365,8 @@ private:
VScrollBar *vscroll = nullptr;
+ AutowrapMode autowrap_mode = AUTOWRAP_WORD_SMART;
+
bool scroll_visible = false;
bool scroll_follow = false;
bool scroll_following = false;
@@ -358,9 +381,10 @@ private:
int tab_size = 4;
bool underline_meta = true;
+ bool underline_hint = true;
bool override_selected_font_color = false;
- Align default_align = ALIGN_LEFT;
+ HorizontalAlignment default_alignment = HORIZONTAL_ALIGNMENT_LEFT;
ItemMeta *meta_hovering = nullptr;
Variant current_meta;
@@ -375,7 +399,7 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
- Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
Array st_args;
struct Selection {
@@ -396,14 +420,26 @@ private:
bool active = false; // anything selected? i.e. from, to, etc. valid?
bool enabled = false; // allow selections?
+ bool drag_attempt = false;
};
Selection selection;
bool deselect_on_focus_loss_enabled = true;
+ bool context_menu_enabled = false;
+ bool shortcut_keys_enabled = true;
+
+ // Context menu.
+ PopupMenu *menu = nullptr;
+ void _generate_context_menu();
+ Key _get_menu_action_accelerator(const String &p_action);
+ void _menu_option(int p_option);
+
int visible_characters = -1;
float percent_visible = 1.0;
+ VisibleCharactersBehavior visible_chars_behavior = VC_CHARS_BEFORE_SHAPING;
+ bool _is_click_inside_selection() const;
void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr);
String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel) const;
@@ -412,8 +448,9 @@ private:
void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
- int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs);
- float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
+ void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size);
+ int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs);
+ float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false);
String _roman(int p_num, bool p_capitalize) const;
String _letters(int p_num, bool p_capitalize) const;
@@ -428,15 +465,16 @@ private:
ItemDropcap *_find_dc_item(Item *p_item);
int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list);
int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size);
- Align _find_align(Item *p_item);
+ HorizontalAlignment _find_alignment(Item *p_item);
TextServer::Direction _find_direction(Item *p_item);
- Control::StructuredTextParser _find_stt(Item *p_item);
+ TextServer::StructuredTextParser _find_stt(Item *p_item);
String _find_language(Item *p_item);
Color _find_color(Item *p_item, const Color &p_default_color);
Color _find_outline_color(Item *p_item, const Color &p_default_color);
bool _find_underline(Item *p_item);
bool _find_strikethrough(Item *p_item);
bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr);
+ bool _find_hint(Item *p_item, String *r_description);
Color _find_bgcolor(Item *p_item);
Color _find_fgcolor(Item *p_item);
bool _find_layout_subitem(Item *from, Item *to);
@@ -447,6 +485,7 @@ private:
void _scroll_changed(double);
virtual void gui_input(const Ref<InputEvent> &p_event) override;
+ virtual String get_tooltip(const Point2 &p_pos) const override;
Item *_get_next_item(Item *p_item, bool p_free = false) const;
Item *_get_prev_item(Item *p_item, bool p_free = false) const;
@@ -469,7 +508,7 @@ private:
public:
String get_parsed_text() const;
void add_text(const String &p_text);
- void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlign p_align = INLINE_ALIGN_CENTER);
+ void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER);
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));
@@ -486,11 +525,12 @@ public:
void push_outline_color(const Color &p_color);
void push_underline();
void push_strikethrough();
- void push_paragraph(Align p_align, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", Control::StructuredTextParser p_st_parser = STRUCTURED_TEXT_DEFAULT);
+ void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT);
void push_indent(int p_level);
void push_list(int p_level, ListType p_list, bool p_capitalize);
void push_meta(const Variant &p_meta);
- void push_table(int p_columns, InlineAlign p_align = INLINE_ALIGN_TOP);
+ void push_hint(const String &p_string);
+ void push_table(int p_columns, InlineAlignment p_alignment = INLINE_ALIGNMENT_TOP);
void push_fade(int p_start_index, int p_length);
void push_shake(int p_strength, float p_rate);
void push_wave(float p_frequency, float p_amplitude);
@@ -515,6 +555,9 @@ public:
void set_meta_underline(bool p_underline);
bool is_meta_underlined() const;
+ void set_hint_underline(bool p_underline);
+ bool is_hint_underlined() const;
+
void set_override_selected_font_color(bool p_override_selected_font_color);
bool is_overriding_selected_font_color() const;
@@ -527,6 +570,12 @@ public:
void set_tab_size(int p_spaces);
int get_tab_size() const;
+ void set_context_menu_enabled(bool p_enabled);
+ bool is_context_menu_enabled() const;
+
+ void set_shortcut_keys_enabled(bool p_enabled);
+ bool is_shortcut_keys_enabled() const;
+
void set_fit_content_height(bool p_enabled);
bool is_fit_content_height_enabled() const;
@@ -536,24 +585,35 @@ public:
int get_paragraph_count() const;
int get_visible_paragraph_count() const;
+ float get_line_offset(int p_line);
+ float get_paragraph_offset(int p_paragraph);
+
void scroll_to_line(int p_line);
int get_line_count() const;
int get_visible_line_count() const;
int get_content_height() const;
+ int get_content_width() const;
- VScrollBar *get_v_scroll() { return vscroll; }
+ VScrollBar *get_v_scroll_bar() { return vscroll; }
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
+ virtual Variant get_drag_data(const Point2 &p_point) override;
void set_selection_enabled(bool p_enabled);
bool is_selection_enabled() const;
int get_selection_from() const;
int get_selection_to() const;
String get_selected_text() const;
+ void select_all();
void selection_copy();
void set_deselect_on_focus_loss_enabled(const bool p_enabled);
bool is_deselect_on_focus_loss_enabled() const;
+ void deselect();
+
+ // Context menu.
+ PopupMenu *get_menu() const;
+ bool is_menu_visible() const;
void parse_bbcode(const String &p_bbcode);
void append_text(const String &p_bbcode);
@@ -570,19 +630,28 @@ public:
void set_language(const String &p_language);
String get_language() const;
- void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
- Control::StructuredTextParser get_structured_text_bidi_override() const;
+ void set_autowrap_mode(AutowrapMode p_mode);
+ AutowrapMode get_autowrap_mode() const;
+
+ void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
+ TextServer::StructuredTextParser get_structured_text_bidi_override() const;
void set_structured_text_bidi_override_options(Array p_args);
Array get_structured_text_bidi_override_options() const;
void set_visible_characters(int p_visible);
int get_visible_characters() const;
+ int get_character_line(int p_char);
+ int get_character_paragraph(int p_char);
int get_total_character_count() const;
+ int get_total_glyph_count() const;
void set_percent_visible(float p_percent);
float get_percent_visible() const;
+ VisibleCharactersBehavior get_visible_characters_behavior() const;
+ void set_visible_characters_behavior(VisibleCharactersBehavior p_behavior);
+
void set_effects(Array p_effects);
Array get_effects();
@@ -591,12 +660,13 @@ public:
void set_fixed_size_to_width(int p_width);
virtual Size2 get_minimum_size() const override;
- RichTextLabel();
+ RichTextLabel(const String &p_text = String());
~RichTextLabel();
};
-VARIANT_ENUM_CAST(RichTextLabel::Align);
+VARIANT_ENUM_CAST(RichTextLabel::AutowrapMode);
VARIANT_ENUM_CAST(RichTextLabel::ListType);
VARIANT_ENUM_CAST(RichTextLabel::ItemType);
+VARIANT_ENUM_CAST(RichTextLabel::VisibleCharactersBehavior);
#endif // RICH_TEXT_LABEL_H
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 8c292e663e..e1b0e8cca8 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -218,195 +218,198 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
}
void ScrollBar::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
- Ref<Texture2D> decr, incr;
+ Ref<Texture2D> decr, incr;
- if (decr_active) {
- decr = get_theme_icon(SNAME("decrement_pressed"));
- } else if (highlight == HIGHLIGHT_DECR) {
- decr = get_theme_icon(SNAME("decrement_highlight"));
- } else {
- decr = get_theme_icon(SNAME("decrement"));
- }
+ if (decr_active) {
+ decr = get_theme_icon(SNAME("decrement_pressed"));
+ } else if (highlight == HIGHLIGHT_DECR) {
+ decr = get_theme_icon(SNAME("decrement_highlight"));
+ } else {
+ decr = get_theme_icon(SNAME("decrement"));
+ }
- if (incr_active) {
- incr = get_theme_icon(SNAME("increment_pressed"));
- } else if (highlight == HIGHLIGHT_INCR) {
- incr = get_theme_icon(SNAME("increment_highlight"));
- } else {
- incr = get_theme_icon(SNAME("increment"));
- }
+ if (incr_active) {
+ incr = get_theme_icon(SNAME("increment_pressed"));
+ } else if (highlight == HIGHLIGHT_INCR) {
+ incr = get_theme_icon(SNAME("increment_highlight"));
+ } else {
+ incr = get_theme_icon(SNAME("increment"));
+ }
- Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll"));
+ Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll"));
- Ref<StyleBox> grabber;
- if (drag.active) {
- grabber = get_theme_stylebox(SNAME("grabber_pressed"));
- } else if (highlight == HIGHLIGHT_RANGE) {
- grabber = get_theme_stylebox(SNAME("grabber_highlight"));
- } else {
- grabber = get_theme_stylebox(SNAME("grabber"));
- }
+ Ref<StyleBox> grabber;
+ if (drag.active) {
+ grabber = get_theme_stylebox(SNAME("grabber_pressed"));
+ } else if (highlight == HIGHLIGHT_RANGE) {
+ grabber = get_theme_stylebox(SNAME("grabber_highlight"));
+ } else {
+ grabber = get_theme_stylebox(SNAME("grabber"));
+ }
- Point2 ofs;
+ Point2 ofs;
- decr->draw(ci, Point2());
+ decr->draw(ci, Point2());
- if (orientation == HORIZONTAL) {
- ofs.x += decr->get_width();
- } else {
- ofs.y += decr->get_height();
- }
+ if (orientation == HORIZONTAL) {
+ ofs.x += decr->get_width();
+ } else {
+ ofs.y += decr->get_height();
+ }
- Size2 area = get_size();
+ Size2 area = get_size();
- if (orientation == HORIZONTAL) {
- area.width -= incr->get_width() + decr->get_width();
- } else {
- area.height -= incr->get_height() + decr->get_height();
- }
+ if (orientation == HORIZONTAL) {
+ area.width -= incr->get_width() + decr->get_width();
+ } else {
+ area.height -= incr->get_height() + decr->get_height();
+ }
- bg->draw(ci, Rect2(ofs, area));
+ bg->draw(ci, Rect2(ofs, area));
- if (orientation == HORIZONTAL) {
- ofs.width += area.width;
- } else {
- ofs.height += area.height;
- }
+ if (orientation == HORIZONTAL) {
+ ofs.width += area.width;
+ } else {
+ ofs.height += area.height;
+ }
- incr->draw(ci, ofs);
- Rect2 grabber_rect;
+ incr->draw(ci, ofs);
+ Rect2 grabber_rect;
- if (orientation == HORIZONTAL) {
- grabber_rect.size.width = get_grabber_size();
- grabber_rect.size.height = get_size().height;
- grabber_rect.position.y = 0;
- grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT);
- } else {
- grabber_rect.size.width = get_size().width;
- grabber_rect.size.height = get_grabber_size();
- grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP);
- grabber_rect.position.x = 0;
- }
+ if (orientation == HORIZONTAL) {
+ grabber_rect.size.width = get_grabber_size();
+ grabber_rect.size.height = get_size().height;
+ grabber_rect.position.y = 0;
+ grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT);
+ } else {
+ grabber_rect.size.width = get_size().width;
+ grabber_rect.size.height = get_grabber_size();
+ grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP);
+ grabber_rect.position.x = 0;
+ }
- grabber->draw(ci, grabber_rect);
- }
+ grabber->draw(ci, grabber_rect);
+ } break;
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (has_node(drag_node_path)) {
- Node *n = get_node(drag_node_path);
- drag_node = Object::cast_to<Control>(n);
- }
+ case NOTIFICATION_ENTER_TREE: {
+ if (has_node(drag_node_path)) {
+ Node *n = get_node(drag_node_path);
+ drag_node = Object::cast_to<Control>(n);
+ }
- 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);
- }
- }
- if (p_what == NOTIFICATION_EXIT_TREE) {
- if (drag_node) {
- drag_node->disconnect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input));
- drag_node->disconnect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit));
- }
+ 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);
+ }
+ } break;
- drag_node = nullptr;
- }
+ case NOTIFICATION_EXIT_TREE: {
+ if (drag_node) {
+ drag_node->disconnect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input));
+ drag_node->disconnect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit));
+ }
- if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- if (scrolling) {
- if (get_value() != target_scroll) {
- double target = target_scroll - get_value();
- double dist = sqrt(target * target);
- double vel = ((target / dist) * 500) * get_physics_process_delta_time();
+ drag_node = nullptr;
+ } break;
- if (Math::abs(vel) >= dist) {
- set_value(target_scroll);
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (scrolling) {
+ if (get_value() != target_scroll) {
+ double target = target_scroll - get_value();
+ double dist = sqrt(target * target);
+ double vel = ((target / dist) * 500) * get_physics_process_delta_time();
+
+ if (Math::abs(vel) >= dist) {
+ set_value(target_scroll);
+ scrolling = false;
+ set_physics_process_internal(false);
+ } else {
+ set_value(get_value() + vel);
+ }
+ } else {
scrolling = false;
set_physics_process_internal(false);
- } else {
- set_value(get_value() + vel);
}
- } else {
- scrolling = false;
- set_physics_process_internal(false);
- }
- } else if (drag_node_touching) {
- if (drag_node_touching_deaccel) {
- Vector2 pos = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0);
- pos += drag_node_speed * get_physics_process_delta_time();
+ } else if (drag_node_touching) {
+ if (drag_node_touching_deaccel) {
+ Vector2 pos = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0);
+ pos += drag_node_speed * get_physics_process_delta_time();
- bool turnoff = false;
+ bool turnoff = false;
- if (orientation == HORIZONTAL) {
- if (pos.x < 0) {
- pos.x = 0;
- turnoff = true;
- }
+ if (orientation == HORIZONTAL) {
+ if (pos.x < 0) {
+ pos.x = 0;
+ turnoff = true;
+ }
- if (pos.x > (get_max() - get_page())) {
- pos.x = get_max() - get_page();
- turnoff = true;
- }
+ if (pos.x > (get_max() - get_page())) {
+ pos.x = get_max() - get_page();
+ turnoff = true;
+ }
- set_value(pos.x);
+ set_value(pos.x);
- float sgn_x = drag_node_speed.x < 0 ? -1 : 1;
- float val_x = Math::abs(drag_node_speed.x);
- val_x -= 1000 * get_physics_process_delta_time();
+ float sgn_x = drag_node_speed.x < 0 ? -1 : 1;
+ float val_x = Math::abs(drag_node_speed.x);
+ val_x -= 1000 * get_physics_process_delta_time();
- if (val_x < 0) {
- turnoff = true;
- }
+ if (val_x < 0) {
+ turnoff = true;
+ }
- drag_node_speed.x = sgn_x * val_x;
+ drag_node_speed.x = sgn_x * val_x;
- } else {
- if (pos.y < 0) {
- pos.y = 0;
- turnoff = true;
- }
+ } else {
+ if (pos.y < 0) {
+ pos.y = 0;
+ turnoff = true;
+ }
- if (pos.y > (get_max() - get_page())) {
- pos.y = get_max() - get_page();
- turnoff = true;
- }
+ if (pos.y > (get_max() - get_page())) {
+ pos.y = get_max() - get_page();
+ turnoff = true;
+ }
- set_value(pos.y);
+ set_value(pos.y);
- float sgn_y = drag_node_speed.y < 0 ? -1 : 1;
- float val_y = Math::abs(drag_node_speed.y);
- val_y -= 1000 * get_physics_process_delta_time();
+ float sgn_y = drag_node_speed.y < 0 ? -1 : 1;
+ float val_y = Math::abs(drag_node_speed.y);
+ val_y -= 1000 * get_physics_process_delta_time();
- if (val_y < 0) {
- turnoff = true;
+ if (val_y < 0) {
+ turnoff = true;
+ }
+ drag_node_speed.y = sgn_y * val_y;
}
- drag_node_speed.y = sgn_y * val_y;
- }
- if (turnoff) {
- set_physics_process_internal(false);
- drag_node_touching = false;
- drag_node_touching_deaccel = false;
- }
+ if (turnoff) {
+ set_physics_process_internal(false);
+ drag_node_touching = false;
+ drag_node_touching_deaccel = false;
+ }
- } else {
- if (time_since_motion == 0 || time_since_motion > 0.1) {
- Vector2 diff = drag_node_accum - last_drag_node_accum;
- last_drag_node_accum = drag_node_accum;
- drag_node_speed = diff / get_physics_process_delta_time();
- }
+ } else {
+ if (time_since_motion == 0 || time_since_motion > 0.1) {
+ Vector2 diff = drag_node_accum - last_drag_node_accum;
+ last_drag_node_accum = drag_node_accum;
+ drag_node_speed = diff / get_physics_process_delta_time();
+ }
- time_since_motion += get_physics_process_delta_time();
+ time_since_motion += get_physics_process_delta_time();
+ }
}
- }
- }
+ } break;
- if (p_what == NOTIFICATION_MOUSE_EXIT) {
- highlight = HIGHLIGHT_NONE;
- update();
+ case NOTIFICATION_MOUSE_EXIT: {
+ highlight = HIGHLIGHT_NONE;
+ update();
+ } break;
}
}
@@ -423,11 +426,6 @@ double ScrollBar::get_grabber_size() const {
}
float page = (get_page() > 0) ? get_page() : 0;
- /*
- if (grabber_range < get_step())
- grabber_range=get_step();
- */
-
double area_size = get_area_size();
double grabber_size = page / range * area_size;
return grabber_size + get_grabber_min_size();
diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h
index 574d17ee20..651edd1a74 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 7b2ea46e17..135bad4689 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "scroll_container.h"
+
+#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "scene/main/window.h"
@@ -49,10 +51,10 @@ Size2 ScrollContainer::get_minimum_size() const {
}
Size2 minsize = c->get_combined_minimum_size();
- if (!scroll_h) {
+ if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) {
min_size.x = MAX(min_size.x, minsize.x);
}
- if (!scroll_v) {
+ if (vertical_scroll_mode == SCROLL_MODE_DISABLED) {
min_size.y = MAX(min_size.y, minsize.y);
}
}
@@ -170,7 +172,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
Vector2 motion = mm->get_relative();
drag_accum -= motion;
- if (beyond_deadzone || (scroll_h && Math::abs(drag_accum.x) > deadzone) || (scroll_v && Math::abs(drag_accum.y) > deadzone)) {
+ if (beyond_deadzone || (horizontal_scroll_mode != SCROLL_MODE_DISABLED && Math::abs(drag_accum.x) > deadzone) || (vertical_scroll_mode != SCROLL_MODE_DISABLED && Math::abs(drag_accum.y) > deadzone)) {
if (!beyond_deadzone) {
propagate_notification(NOTIFICATION_SCROLL_BEGIN);
emit_signal(SNAME("scroll_started"));
@@ -180,12 +182,12 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
drag_accum = -motion;
}
Vector2 diff = drag_from + drag_accum;
- if (scroll_h) {
+ if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
h_scroll->set_value(diff.x);
} else {
drag_accum.x = 0;
}
- if (scroll_v) {
+ if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
v_scroll->set_value(diff.y);
} else {
drag_accum.y = 0;
@@ -286,7 +288,7 @@ void ScrollContainer::_update_dimensions() {
child_max_size.y = MAX(child_max_size.y, minsize.y);
Rect2 r = Rect2(-Size2(get_h_scroll(), get_v_scroll()), minsize);
- if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) {
+ if (horizontal_scroll_mode == SCROLL_MODE_DISABLED || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) {
r.position.x = 0;
if (c->get_h_size_flags() & SIZE_EXPAND) {
r.size.width = MAX(size.width, minsize.width);
@@ -294,7 +296,7 @@ void ScrollContainer::_update_dimensions() {
r.size.width = minsize.width;
}
}
- if (!scroll_v || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) {
+ if (vertical_scroll_mode == SCROLL_MODE_DISABLED || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) {
r.position.y = 0;
if (c->get_v_size_flags() & SIZE_EXPAND) {
r.size.height = MAX(size.height, minsize.height);
@@ -314,97 +316,102 @@ void ScrollContainer::_update_dimensions() {
}
void ScrollContainer::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- _updating_scrollbars = true;
- call_deferred(SNAME("_update_scrollbar_position"));
- };
-
- if (p_what == NOTIFICATION_READY) {
- Viewport *viewport = get_viewport();
- ERR_FAIL_COND(!viewport);
- viewport->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed));
- _update_dimensions();
- }
-
- if (p_what == NOTIFICATION_SORT_CHILDREN) {
- _update_dimensions();
- };
-
- if (p_what == NOTIFICATION_DRAW) {
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
- draw_style_box(sb, Rect2(Vector2(), get_size()));
-
- update_scrollbars();
- }
-
- if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- if (drag_touching) {
- if (drag_touching_deaccel) {
- Vector2 pos = Vector2(h_scroll->get_value(), v_scroll->get_value());
- pos += drag_speed * get_physics_process_delta_time();
-
- bool turnoff_h = false;
- bool turnoff_v = false;
-
- if (pos.x < 0) {
- pos.x = 0;
- turnoff_h = true;
- }
- if (pos.x > (h_scroll->get_max() - h_scroll->get_page())) {
- pos.x = h_scroll->get_max() - h_scroll->get_page();
- turnoff_h = true;
- }
-
- if (pos.y < 0) {
- pos.y = 0;
- turnoff_v = true;
- }
- if (pos.y > (v_scroll->get_max() - v_scroll->get_page())) {
- pos.y = v_scroll->get_max() - v_scroll->get_page();
- turnoff_v = true;
- }
-
- if (scroll_h) {
- h_scroll->set_value(pos.x);
- }
- if (scroll_v) {
- v_scroll->set_value(pos.y);
- }
-
- float sgn_x = drag_speed.x < 0 ? -1 : 1;
- float val_x = Math::abs(drag_speed.x);
- val_x -= 1000 * get_physics_process_delta_time();
-
- if (val_x < 0) {
- turnoff_h = true;
- }
-
- float sgn_y = drag_speed.y < 0 ? -1 : 1;
- float val_y = Math::abs(drag_speed.y);
- val_y -= 1000 * get_physics_process_delta_time();
-
- if (val_y < 0) {
- turnoff_v = true;
- }
-
- drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y);
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ _updating_scrollbars = true;
+ call_deferred(SNAME("_update_scrollbar_position"));
+ } break;
+
+ case NOTIFICATION_READY: {
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ viewport->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed));
+ _update_dimensions();
+ } break;
+
+ case NOTIFICATION_SORT_CHILDREN: {
+ _update_dimensions();
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+
+ update_scrollbars();
+ } break;
+
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (drag_touching) {
+ if (drag_touching_deaccel) {
+ Vector2 pos = Vector2(h_scroll->get_value(), v_scroll->get_value());
+ pos += drag_speed * get_physics_process_delta_time();
+
+ bool turnoff_h = false;
+ bool turnoff_v = false;
+
+ if (pos.x < 0) {
+ pos.x = 0;
+ turnoff_h = true;
+ }
+ if (pos.x > (h_scroll->get_max() - h_scroll->get_page())) {
+ pos.x = h_scroll->get_max() - h_scroll->get_page();
+ turnoff_h = true;
+ }
+
+ if (pos.y < 0) {
+ pos.y = 0;
+ turnoff_v = true;
+ }
+ if (pos.y > (v_scroll->get_max() - v_scroll->get_page())) {
+ pos.y = v_scroll->get_max() - v_scroll->get_page();
+ turnoff_v = true;
+ }
+
+ if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
+ h_scroll->set_value(pos.x);
+ }
+ if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
+ v_scroll->set_value(pos.y);
+ }
+
+ float sgn_x = drag_speed.x < 0 ? -1 : 1;
+ float val_x = Math::abs(drag_speed.x);
+ val_x -= 1000 * get_physics_process_delta_time();
+
+ if (val_x < 0) {
+ turnoff_h = true;
+ }
+
+ float sgn_y = drag_speed.y < 0 ? -1 : 1;
+ float val_y = Math::abs(drag_speed.y);
+ val_y -= 1000 * get_physics_process_delta_time();
+
+ if (val_y < 0) {
+ turnoff_v = true;
+ }
+
+ drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y);
+
+ if (turnoff_h && turnoff_v) {
+ _cancel_drag();
+ }
- if (turnoff_h && turnoff_v) {
- _cancel_drag();
- }
+ } else {
+ if (time_since_motion == 0 || time_since_motion > 0.1) {
+ Vector2 diff = drag_accum - last_drag_accum;
+ last_drag_accum = drag_accum;
+ drag_speed = diff / get_physics_process_delta_time();
+ }
- } else {
- if (time_since_motion == 0 || time_since_motion > 0.1) {
- Vector2 diff = drag_accum - last_drag_accum;
- last_drag_accum = drag_accum;
- drag_speed = diff / get_physics_process_delta_time();
+ time_since_motion += get_physics_process_delta_time();
}
-
- time_since_motion += get_physics_process_delta_time();
}
- }
+ } break;
}
-};
+}
void ScrollContainer::update_scrollbars() {
Size2 size = get_size();
@@ -413,17 +420,17 @@ void ScrollContainer::update_scrollbars() {
Size2 hmin;
Size2 vmin;
- if (scroll_h) {
+ if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
hmin = h_scroll->get_combined_minimum_size();
}
- if (scroll_v) {
+ if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
vmin = v_scroll->get_combined_minimum_size();
}
Size2 min = child_max_size;
- bool hide_scroll_h = !scroll_h || min.width <= size.width || !h_scroll_visible;
- bool hide_scroll_v = !scroll_v || min.height <= size.height || !v_scroll_visible;
+ bool hide_scroll_h = horizontal_scroll_mode != SCROLL_MODE_SHOW_ALWAYS && (horizontal_scroll_mode == SCROLL_MODE_DISABLED || horizontal_scroll_mode == SCROLL_MODE_SHOW_NEVER || (horizontal_scroll_mode == SCROLL_MODE_AUTO && min.width <= size.width));
+ bool hide_scroll_v = vertical_scroll_mode != SCROLL_MODE_SHOW_ALWAYS && (vertical_scroll_mode == SCROLL_MODE_DISABLED || vertical_scroll_mode == SCROLL_MODE_SHOW_NEVER || (vertical_scroll_mode == SCROLL_MODE_AUTO && min.height <= size.height));
h_scroll->set_max(min.width);
h_scroll->set_page(size.width - (hide_scroll_v ? 0 : vmin.width));
@@ -461,58 +468,32 @@ int ScrollContainer::get_v_scroll() const {
return v_scroll->get_value();
}
-void ScrollContainer::set_enable_h_scroll(bool p_enable) {
- if (scroll_h == p_enable) {
+void ScrollContainer::set_horizontal_scroll_mode(ScrollMode p_mode) {
+ if (horizontal_scroll_mode == p_mode) {
return;
}
- scroll_h = p_enable;
- minimum_size_changed();
+ horizontal_scroll_mode = p_mode;
+ update_minimum_size();
queue_sort();
}
-bool ScrollContainer::is_h_scroll_enabled() const {
- return scroll_h;
+ScrollContainer::ScrollMode ScrollContainer::get_horizontal_scroll_mode() const {
+ return horizontal_scroll_mode;
}
-void ScrollContainer::set_enable_v_scroll(bool p_enable) {
- if (scroll_v == p_enable) {
+void ScrollContainer::set_vertical_scroll_mode(ScrollMode p_mode) {
+ if (vertical_scroll_mode == p_mode) {
return;
}
- scroll_v = p_enable;
- minimum_size_changed();
+ vertical_scroll_mode = p_mode;
+ update_minimum_size();
queue_sort();
}
-bool ScrollContainer::is_v_scroll_enabled() const {
- return scroll_v;
-}
-
-void ScrollContainer::set_h_scroll_visible(bool p_visible) {
- if (h_scroll_visible == p_visible) {
- return;
- }
-
- h_scroll_visible = p_visible;
- update_scrollbars();
-}
-
-bool ScrollContainer::is_h_scroll_visible() const {
- return h_scroll_visible;
-}
-
-void ScrollContainer::set_v_scroll_visible(bool p_visible) {
- if (v_scroll_visible == p_visible) {
- return;
- }
-
- v_scroll_visible = p_visible;
- update_scrollbars();
-}
-
-bool ScrollContainer::is_v_scroll_visible() const {
- return v_scroll_visible;
+ScrollContainer::ScrollMode ScrollContainer::get_vertical_scroll_mode() const {
+ return vertical_scroll_mode;
}
int ScrollContainer::get_deadzone() const {
@@ -552,17 +533,17 @@ TypedArray<String> ScrollContainer::get_configuration_warnings() const {
}
if (found != 1) {
- warnings.push_back(TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually."));
+ warnings.push_back(RTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually."));
}
return warnings;
}
-HScrollBar *ScrollContainer::get_h_scrollbar() {
+HScrollBar *ScrollContainer::get_h_scroll_bar() {
return h_scroll;
}
-VScrollBar *ScrollContainer::get_v_scrollbar() {
+VScrollBar *ScrollContainer::get_v_scroll_bar() {
return v_scroll;
}
@@ -575,17 +556,11 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll);
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll);
- ClassDB::bind_method(D_METHOD("set_enable_h_scroll", "enable"), &ScrollContainer::set_enable_h_scroll);
- ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &ScrollContainer::is_h_scroll_enabled);
+ ClassDB::bind_method(D_METHOD("set_horizontal_scroll_mode", "enable"), &ScrollContainer::set_horizontal_scroll_mode);
+ ClassDB::bind_method(D_METHOD("get_horizontal_scroll_mode"), &ScrollContainer::get_horizontal_scroll_mode);
- ClassDB::bind_method(D_METHOD("set_enable_v_scroll", "enable"), &ScrollContainer::set_enable_v_scroll);
- ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &ScrollContainer::is_v_scroll_enabled);
-
- ClassDB::bind_method(D_METHOD("set_h_scroll_visible", "visible"), &ScrollContainer::set_h_scroll_visible);
- ClassDB::bind_method(D_METHOD("is_h_scroll_visible"), &ScrollContainer::is_h_scroll_visible);
-
- ClassDB::bind_method(D_METHOD("set_v_scroll_visible", "visible"), &ScrollContainer::set_v_scroll_visible);
- ClassDB::bind_method(D_METHOD("is_v_scroll_visible"), &ScrollContainer::is_v_scroll_visible);
+ ClassDB::bind_method(D_METHOD("set_vertical_scroll_mode", "enable"), &ScrollContainer::set_vertical_scroll_mode);
+ ClassDB::bind_method(D_METHOD("get_vertical_scroll_mode"), &ScrollContainer::get_vertical_scroll_mode);
ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone);
ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone);
@@ -593,8 +568,8 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_follow_focus", "enabled"), &ScrollContainer::set_follow_focus);
ClassDB::bind_method(D_METHOD("is_following_focus"), &ScrollContainer::is_following_focus);
- ClassDB::bind_method(D_METHOD("get_h_scrollbar"), &ScrollContainer::get_h_scrollbar);
- ClassDB::bind_method(D_METHOD("get_v_scrollbar"), &ScrollContainer::get_v_scrollbar);
+ ClassDB::bind_method(D_METHOD("get_h_scroll_bar"), &ScrollContainer::get_h_scroll_bar);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &ScrollContainer::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("ensure_control_visible", "control"), &ScrollContainer::ensure_control_visible);
ADD_SIGNAL(MethodInfo("scroll_started"));
@@ -605,12 +580,15 @@ void ScrollContainer::_bind_methods() {
ADD_GROUP("Scroll", "scroll_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_visible"), "set_h_scroll_visible", "is_h_scroll_visible");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_visible"), "set_v_scroll_visible", "is_v_scroll_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_vertical_scroll_mode", "get_vertical_scroll_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
+ BIND_ENUM_CONSTANT(SCROLL_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(SCROLL_MODE_AUTO);
+ BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_ALWAYS);
+ BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_NEVER);
+
GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
};
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 9f4ec558dc..b9fcf64db6 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,8 +38,17 @@
class ScrollContainer : public Container {
GDCLASS(ScrollContainer, Container);
- HScrollBar *h_scroll;
- VScrollBar *v_scroll;
+public:
+ enum ScrollMode {
+ SCROLL_MODE_DISABLED = 0,
+ SCROLL_MODE_AUTO,
+ SCROLL_MODE_SHOW_ALWAYS,
+ SCROLL_MODE_SHOW_NEVER,
+ };
+
+private:
+ HScrollBar *h_scroll = nullptr;
+ VScrollBar *v_scroll = nullptr;
Size2 child_max_size;
@@ -54,11 +63,8 @@ class ScrollContainer : public Container {
bool drag_touching_deaccel = false;
bool beyond_deadzone = false;
- bool scroll_h = true;
- bool scroll_v = true;
-
- bool h_scroll_visible = true;
- bool v_scroll_visible = true;
+ ScrollMode horizontal_scroll_mode = SCROLL_MODE_AUTO;
+ ScrollMode vertical_scroll_mode = SCROLL_MODE_AUTO;
int deadzone = 0;
bool follow_focus = false;
@@ -68,7 +74,6 @@ class ScrollContainer : public Container {
protected:
Size2 get_minimum_size() const override;
- virtual void gui_input(const Ref<InputEvent> &p_gui_input) override;
void _gui_focus_changed(Control *p_control);
void _update_dimensions();
void _notification(int p_what);
@@ -80,23 +85,19 @@ protected:
void _update_scrollbar_position();
public:
+ virtual void gui_input(const Ref<InputEvent> &p_gui_input) override;
+
void set_h_scroll(int p_pos);
int get_h_scroll() const;
void set_v_scroll(int p_pos);
int get_v_scroll() const;
- void set_enable_h_scroll(bool p_enable);
- bool is_h_scroll_enabled() const;
-
- void set_enable_v_scroll(bool p_enable);
- bool is_v_scroll_enabled() const;
+ void set_horizontal_scroll_mode(ScrollMode p_mode);
+ ScrollMode get_horizontal_scroll_mode() const;
- void set_h_scroll_visible(bool p_visible);
- bool is_h_scroll_visible() const;
-
- void set_v_scroll_visible(bool p_visible);
- bool is_v_scroll_visible() const;
+ void set_vertical_scroll_mode(ScrollMode p_mode);
+ ScrollMode get_vertical_scroll_mode() const;
int get_deadzone() const;
void set_deadzone(int p_deadzone);
@@ -104,8 +105,8 @@ public:
bool is_following_focus() const;
void set_follow_focus(bool p_follow);
- HScrollBar *get_h_scrollbar();
- VScrollBar *get_v_scrollbar();
+ HScrollBar *get_h_scroll_bar();
+ VScrollBar *get_v_scroll_bar();
void ensure_control_visible(Control *p_control);
TypedArray<String> get_configuration_warnings() const override;
@@ -113,4 +114,6 @@ public:
ScrollContainer();
};
+VARIANT_ENUM_CAST(ScrollContainer::ScrollMode);
+
#endif
diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp
index 1f3cb7aa24..e3400d9c8f 100644
--- a/scene/gui/separator.cpp
+++ b/scene/gui/separator.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -52,7 +52,6 @@ void Separator::_notification(int p_what) {
} else {
style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y));
}
-
} break;
}
}
diff --git a/scene/gui/separator.h b/scene/gui/separator.h
index 77162c68fa..1621bb3351 100644
--- a/scene/gui/separator.h
+++ b/scene/gui/separator.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 4cc425aad3..4b680f72cf 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "slider.h"
+
#include "core/os/keyboard.h"
Size2 Slider::get_minimum_size() const {
@@ -70,8 +71,13 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
}
grab.active = true;
grab.uvalue = get_as_ratio();
+
+ emit_signal(SNAME("drag_started"));
} else {
grab.active = false;
+
+ const bool value_changed = !Math::is_equal_approx((double)grab.uvalue, get_as_ratio());
+ emit_signal(SNAME("drag_ended"), value_changed);
}
} else if (scrollable) {
if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
@@ -142,22 +148,26 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
void Slider::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
+
case NOTIFICATION_MOUSE_ENTER: {
mouse_inside = true;
update();
} break;
+
case NOTIFICATION_MOUSE_EXIT: {
mouse_inside = false;
update();
} break;
- case NOTIFICATION_VISIBILITY_CHANGED: // fallthrough
+
+ case NOTIFICATION_VISIBILITY_CHANGED:
case NOTIFICATION_EXIT_TREE: {
mouse_inside = false;
grab.active = false;
} break;
+
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
Size2i size = get_size();
@@ -204,7 +214,6 @@ void Slider::_notification(int p_what) {
}
grabber->draw(ci, Point2i(ratio * areasize, size.height / 2 - grabber->get_size().height / 2));
}
-
} break;
}
}
@@ -264,6 +273,9 @@ void Slider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scrollable", "scrollable"), &Slider::set_scrollable);
ClassDB::bind_method(D_METHOD("is_scrollable"), &Slider::is_scrollable);
+ ADD_SIGNAL(MethodInfo("drag_started"));
+ ADD_SIGNAL(MethodInfo("drag_ended", PropertyInfo(Variant::BOOL, "value_changed")));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks");
diff --git a/scene/gui/slider.h b/scene/gui/slider.h
index 46fa08bbf0..5fbfee2aec 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index f30206c943..e50d7e765c 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,15 +39,16 @@ Size2 SpinBox::get_minimum_size() const {
return ms;
}
-void SpinBox::_value_changed(double) {
+void SpinBox::_value_changed(double p_value) {
String value = TS->format_number(String::num(get_value(), Math::range_step_decimals(get_step())));
- if (prefix != "") {
+ if (!prefix.is_empty()) {
value = prefix + " " + value;
}
- if (suffix != "") {
+ if (!suffix.is_empty()) {
value += " " + suffix;
}
line_edit->set_text(value);
+ Range::_value_changed(p_value);
}
void SpinBox::_text_submitted(const String &p_string) {
@@ -196,43 +197,53 @@ inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) {
}
void SpinBox::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- Ref<Texture2D> updown = get_theme_icon(SNAME("updown"));
-
- _adjust_width_for_icon(updown);
-
- RID ci = get_canvas_item();
- Size2i size = get_size();
-
- if (is_layout_rtl()) {
- updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2));
- } else {
- updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2));
- }
-
- } else if (p_what == NOTIFICATION_FOCUS_EXIT) {
- //_value_changed(0);
- } else if (p_what == NOTIFICATION_ENTER_TREE) {
- _adjust_width_for_icon(get_theme_icon(SNAME("updown")));
- _value_changed(0);
- } else if (p_what == NOTIFICATION_EXIT_TREE) {
- _release_mouse();
- } else if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- _value_changed(0);
- } else if (p_what == NOTIFICATION_THEME_CHANGED) {
- call_deferred(SNAME("minimum_size_changed"));
- get_line_edit()->call_deferred(SNAME("minimum_size_changed"));
- } else if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- update();
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ Ref<Texture2D> updown = get_theme_icon(SNAME("updown"));
+
+ _adjust_width_for_icon(updown);
+
+ RID ci = get_canvas_item();
+ Size2i size = get_size();
+
+ if (is_layout_rtl()) {
+ updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2));
+ } else {
+ updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2));
+ }
+ } break;
+
+ case NOTIFICATION_ENTER_TREE: {
+ _adjust_width_for_icon(get_theme_icon(SNAME("updown")));
+ _value_changed(0);
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ _release_mouse();
+ } break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ _value_changed(0);
+ update();
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ call_deferred(SNAME("update_minimum_size"));
+ get_line_edit()->call_deferred(SNAME("update_minimum_size"));
+ } break;
+
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ update();
+ } break;
}
}
-void SpinBox::set_align(LineEdit::Align p_align) {
- line_edit->set_align(p_align);
+void SpinBox::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ line_edit->set_horizontal_alignment(p_alignment);
}
-LineEdit::Align SpinBox::get_align() const {
- return line_edit->get_align();
+HorizontalAlignment SpinBox::get_horizontal_alignment() const {
+ return line_edit->get_horizontal_alignment();
}
void SpinBox::set_suffix(const String &p_suffix) {
@@ -284,8 +295,8 @@ void SpinBox::apply() {
}
void SpinBox::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_align", "align"), &SpinBox::set_align);
- ClassDB::bind_method(D_METHOD("get_align"), &SpinBox::get_align);
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &SpinBox::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &SpinBox::get_horizontal_alignment);
ClassDB::bind_method(D_METHOD("set_suffix", "suffix"), &SpinBox::set_suffix);
ClassDB::bind_method(D_METHOD("get_suffix"), &SpinBox::get_suffix);
ClassDB::bind_method(D_METHOD("set_prefix", "prefix"), &SpinBox::set_prefix);
@@ -297,7 +308,7 @@ void SpinBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply);
ClassDB::bind_method(D_METHOD("get_line_edit"), &SpinBox::get_line_edit);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "update_on_text_changed"), "set_update_on_text_changed", "get_update_on_text_changed");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix");
@@ -310,7 +321,7 @@ SpinBox::SpinBox() {
line_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
- line_edit->set_align(LineEdit::ALIGN_LEFT);
+ 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);
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index f2299ce1c2..d118b28334 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,16 +38,16 @@
class SpinBox : public Range {
GDCLASS(SpinBox, Range);
- LineEdit *line_edit;
+ LineEdit *line_edit = nullptr;
int last_w = 0;
bool update_on_text_changed = false;
- Timer *range_click_timer;
+ Timer *range_click_timer = nullptr;
void _range_click_timeout();
void _release_mouse();
void _text_submitted(const String &p_string);
- virtual void _value_changed(double) override;
+ virtual void _value_changed(double p_value) override;
void _text_changed(const String &p_string);
String prefix;
@@ -79,8 +79,8 @@ public:
virtual Size2 get_minimum_size() const override;
- void set_align(LineEdit::Align p_align);
- LineEdit::Align get_align() const;
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
void set_editable(bool p_enabled);
bool is_editable() const;
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 6b53c0220e..6845d46721 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -168,15 +168,18 @@ void SplitContainer::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
queue_sort();
} break;
+
case NOTIFICATION_SORT_CHILDREN: {
_resort();
} break;
+
case NOTIFICATION_MOUSE_EXIT: {
mouse_inside = false;
if (get_theme_constant(SNAME("autohide"))) {
update();
}
} break;
+
case NOTIFICATION_DRAW: {
if (!_getch(0) || !_getch(1)) {
return;
@@ -200,8 +203,9 @@ void SplitContainer::_notification(int p_what) {
draw_texture(tex, Point2i(middle_sep + (sep - tex->get_width()) / 2, (size.y - tex->get_height()) / 2));
}
} break;
+
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
} break;
}
}
@@ -336,6 +340,30 @@ bool SplitContainer::is_collapsed() const {
return collapsed;
}
+Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (!vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> SplitContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void SplitContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset);
ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset);
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 47fd30a122..a69ffe4de9 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -79,6 +79,9 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
SplitContainer(bool p_vertical = false);
};
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 53ea32e1b7..68281b6a72 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -54,6 +54,7 @@ Size2 SubViewportContainer::get_minimum_size() const {
void SubViewportContainer::set_stretch(bool p_enable) {
stretch = p_enable;
+ update_minimum_size();
queue_sort();
update();
}
@@ -90,52 +91,81 @@ int SubViewportContainer::get_stretch_shrink() const {
return shrink;
}
-void SubViewportContainer::_notification(int p_what) {
- if (p_what == NOTIFICATION_RESIZED) {
- if (!stretch) {
- return;
- }
+Vector<int> SubViewportContainer::get_allowed_size_flags_horizontal() const {
+ return Vector<int>();
+}
- for (int i = 0; i < get_child_count(); i++) {
- SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
- if (!c) {
- continue;
+Vector<int> SubViewportContainer::get_allowed_size_flags_vertical() const {
+ return Vector<int>();
+}
+
+void SubViewportContainer::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_RESIZED: {
+ if (!stretch) {
+ return;
}
- c->set_size(get_size() / shrink);
- }
- }
+ for (int i = 0; i < get_child_count(); i++) {
+ SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
+ if (!c) {
+ continue;
+ }
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- for (int i = 0; i < get_child_count(); i++) {
- SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
- if (!c) {
- continue;
+ c->set_size(get_size() / shrink);
}
-
- if (is_visible_in_tree()) {
- c->set_update_mode(SubViewport::UPDATE_ALWAYS);
- } else {
- c->set_update_mode(SubViewport::UPDATE_DISABLED);
+ } break;
+
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ for (int i = 0; i < get_child_count(); i++) {
+ SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
+ if (!c) {
+ continue;
+ }
+
+ if (is_visible_in_tree()) {
+ c->set_update_mode(SubViewport::UPDATE_ALWAYS);
+ } else {
+ c->set_update_mode(SubViewport::UPDATE_DISABLED);
+ }
+
+ c->set_handle_input_locally(false); //do not handle input locally here
}
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ for (int i = 0; i < get_child_count(); i++) {
+ SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
+ if (!c) {
+ continue;
+ }
+
+ if (stretch) {
+ draw_texture_rect(c->get_texture(), Rect2(Vector2(), get_size()));
+ } else {
+ draw_texture_rect(c->get_texture(), Rect2(Vector2(), c->get_size()));
+ }
+ }
+ } break;
- c->set_handle_input_locally(false); //do not handle input locally here
- }
- }
+ case NOTIFICATION_MOUSE_ENTER: {
+ _notify_viewports(NOTIFICATION_VP_MOUSE_ENTER);
+ } break;
- if (p_what == NOTIFICATION_DRAW) {
- for (int i = 0; i < get_child_count(); i++) {
- SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
- if (!c) {
- continue;
- }
+ case NOTIFICATION_MOUSE_EXIT: {
+ _notify_viewports(NOTIFICATION_VP_MOUSE_EXIT);
+ } break;
+ }
+}
- if (stretch) {
- draw_texture_rect(c->get_texture(), Rect2(Vector2(), get_size()));
- } else {
- draw_texture_rect(c->get_texture(), Rect2(Vector2(), c->get_size()));
- }
+void SubViewportContainer::_notify_viewports(int p_notification) {
+ for (int i = 0; i < get_child_count(); i++) {
+ SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
+ if (!c) {
+ continue;
}
+ c->notification(p_notification);
}
}
@@ -146,7 +176,7 @@ void SubViewportContainer::input(const Ref<InputEvent> &p_event) {
return;
}
- Transform2D xform = get_global_transform();
+ Transform2D xform = get_global_transform_with_canvas();
if (stretch) {
Transform2D scale_xf;
@@ -173,7 +203,7 @@ void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) {
return;
}
- Transform2D xform = get_global_transform();
+ Transform2D xform = get_global_transform_with_canvas();
if (stretch) {
Transform2D scale_xf;
@@ -193,6 +223,23 @@ void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) {
}
}
+TypedArray<String> SubViewportContainer::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ bool has_viewport = false;
+ for (int i = 0; i < get_child_count(); i++) {
+ if (Object::cast_to<SubViewport>(get_child(i))) {
+ has_viewport = true;
+ break;
+ }
+ }
+ if (!has_viewport) {
+ warnings.push_back(RTR("This node doesn't have a SubViewport as child, so it can't display its intended content.\nConsider adding a SubViewport as a child to provide something displayable."));
+ }
+
+ return warnings;
+}
+
void SubViewportContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stretch", "enable"), &SubViewportContainer::set_stretch);
ClassDB::bind_method(D_METHOD("is_stretch_enabled"), &SubViewportContainer::is_stretch_enabled);
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index 7853f1590e..55b7802aa4 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,6 +38,7 @@ class SubViewportContainer : public Container {
bool stretch = false;
int shrink = 1;
+ void _notify_viewports(int p_notification);
protected:
void _notification(int p_what);
@@ -54,6 +55,11 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
+ TypedArray<String> get_configuration_warnings() const override;
+
SubViewportContainer();
};
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 1dda29f668..b96ba0ebf9 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,54 +32,77 @@
#include "core/object/message_queue.h"
#include "core/string/translation.h"
-
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/texture_rect.h"
+#include "scene/main/viewport.h"
Size2 TabBar::get_minimum_size() const {
+ Size2 ms;
+
+ if (tabs.is_empty()) {
+ return ms;
+ }
+
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
+ Ref<StyleBox> button_highlight = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> close = get_theme_icon(SNAME("close"));
+ int hseparation = get_theme_constant(SNAME("h_separation"));
int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
- Size2 ms(0, 0);
-
for (int i = 0; i < tabs.size(); i++) {
- Ref<Texture2D> tex = tabs[i].icon;
- if (tex.is_valid()) {
- ms.height = MAX(ms.height, tex->get_size().height);
- if (tabs[i].text != "") {
- ms.width += get_theme_constant(SNAME("hseparation"));
- }
+ if (tabs[i].hidden) {
+ continue;
}
- ms.width += Math::ceil(tabs[i].text_buf->get_size().x);
- ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin);
+ int ofs = ms.width;
+ Ref<StyleBox> style;
if (tabs[i].disabled) {
- ms.width += tab_disabled->get_minimum_size().width;
+ style = tab_disabled;
} else if (current == i) {
- ms.width += tab_selected->get_minimum_size().width;
+ style = tab_selected;
} else {
- ms.width += tab_unselected->get_minimum_size().width;
+ style = tab_unselected;
+ }
+ ms.width += style->get_minimum_size().width;
+
+ Ref<Texture2D> tex = tabs[i].icon;
+ if (tex.is_valid()) {
+ ms.height = MAX(ms.height, tex->get_size().height + y_margin);
+ ms.width += tex->get_size().width + hseparation;
+ }
+
+ if (!tabs[i].text.is_empty()) {
+ ms.width += tabs[i].size_text + hseparation;
}
+ ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin);
+
+ bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current);
if (tabs[i].right_button.is_valid()) {
Ref<Texture2D> rb = tabs[i].right_button;
- Size2 bms = rb->get_size();
- bms.width += get_theme_constant(SNAME("hseparation"));
- ms.width += bms.width;
- ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
+
+ if (close_visible) {
+ ms.width += button_highlight->get_minimum_size().width + rb->get_width();
+ } else {
+ ms.width += button_highlight->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+ }
+
+ ms.height = MAX(ms.height, rb->get_height() + y_margin);
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- Size2 bms = cb->get_size();
- bms.width += get_theme_constant(SNAME("hseparation"));
- ms.width += bms.width;
- ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
+ if (close_visible) {
+ ms.width += button_highlight->get_margin(SIDE_LEFT) + close->get_width() + hseparation;
+
+ ms.height = MAX(ms.height, close->get_height() + y_margin);
+ }
+
+ if (ms.width - ofs > style->get_minimum_size().width) {
+ ms.width -= hseparation;
}
}
@@ -136,7 +159,13 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
}
+ if (get_viewport()->gui_is_dragging() && can_drop_data(pos, get_viewport()->gui_get_drag_data())) {
+ dragging_valid_tab = true;
+ update();
+ }
+
_update_hover();
+
return;
}
@@ -147,6 +176,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (scrolling_enabled && buttons_visible) {
if (offset > 0) {
offset--;
+ _update_cache();
update();
}
}
@@ -154,9 +184,9 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_pressed()) {
if (scrolling_enabled && buttons_visible) {
- if (missing_right) {
+ if (missing_right && offset < tabs.size()) {
offset++;
- _ensure_no_over_offset(); // Avoid overreaching when scrolling fast.
+ _update_cache();
update();
}
}
@@ -164,8 +194,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (rb_hover != -1) {
- // pressed
- emit_signal(SNAME("tab_rmb_clicked"), rb_hover);
+ emit_signal(SNAME("tab_button_pressed"), rb_hover);
}
rb_pressing = false;
@@ -174,7 +203,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (cb_hover != -1) {
- // pressed
emit_signal(SNAME("tab_close_pressed"), cb_hover);
}
@@ -183,7 +211,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) {
- // clicks
Point2 pos = mb->get_position();
if (buttons_visible) {
@@ -194,12 +221,14 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x < decr->get_width()) {
if (missing_right) {
offset++;
+ _update_cache();
update();
}
return;
} else if (pos.x < incr->get_width() + decr->get_width()) {
if (offset > 0) {
offset--;
+ _update_cache();
update();
}
return;
@@ -209,12 +238,14 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x > limit + decr->get_width()) {
if (missing_right) {
offset++;
+ _update_cache();
update();
}
return;
} else if (pos.x > limit) {
if (offset > 0) {
offset--;
+ _update_cache();
update();
}
return;
@@ -229,13 +260,17 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
int found = -1;
for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_pressing = true;
update();
return;
}
- if (tabs[i].cb_rect.has_point(pos)) {
+ if (tabs[i].cb_rect.has_point(pos) && (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current))) {
cb_pressing = true;
update();
return;
@@ -251,6 +286,12 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (found != -1) {
set_current_tab(found);
+
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ // Right mouse button clicked.
+ emit_signal(SNAME("tab_rmb_clicked"), found);
+ }
+
emit_signal(SNAME("tab_clicked"), found);
}
}
@@ -270,32 +311,46 @@ void TabBar::_shape(int p_tab) {
tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction);
}
- tabs.write[p_tab].text_buf->add_string(tabs.write[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, (tabs[p_tab].language != "") ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
+ tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, !tabs[p_tab].language.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
}
void TabBar::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- _update_cache();
update();
} break;
+
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
for (int i = 0; i < tabs.size(); ++i) {
_shape(i);
}
- _update_cache();
- minimum_size_changed();
- update();
- } break;
+
+ [[fallthrough]];
+ }
case NOTIFICATION_RESIZED: {
+ int ofs_old = offset;
+ int max_old = max_drawn_tab;
+
_update_cache();
_ensure_no_over_offset();
- ensure_tab_visible(current);
+
+ if (scroll_to_selected && (offset != ofs_old || max_drawn_tab != max_old)) {
+ ensure_tab_visible(current);
+ }
} break;
+
+ case NOTIFICATION_DRAG_END: {
+ if (dragging_valid_tab) {
+ dragging_valid_tab = false;
+ update();
+ }
+ } break;
+
case NOTIFICATION_DRAW: {
- _update_cache();
- RID ci = get_canvas_item();
+ if (tabs.is_empty()) {
+ return;
+ }
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
@@ -303,178 +358,55 @@ void TabBar::_notification(int p_what) {
Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
Color font_unselected_color = get_theme_color(SNAME("font_unselected_color"));
Color font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
- Ref<Texture2D> close = get_theme_icon(SNAME("close"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
-
- Vector2 size = get_size();
- bool rtl = is_layout_rtl();
-
- int h = get_size().height;
- int w = 0;
- int mw = 0;
-
- for (int i = 0; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = mw;
- mw += get_tab_width(i);
- }
-
- if (tab_align == ALIGN_CENTER) {
- w = (get_size().width - mw) / 2;
- } else if (tab_align == ALIGN_RIGHT) {
- w = get_size().width - mw;
- }
-
- if (w < 0) {
- w = 0;
- }
-
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
- Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight"));
- Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight"));
-
- int limit = get_size().width;
- int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
-
- missing_right = false;
-
- for (int i = offset; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = w;
-
- int lsize = tabs[i].size_cache;
-
- Ref<StyleBox> sb;
- Color col;
-
- if (tabs[i].disabled) {
- sb = tab_disabled;
- col = font_disabled_color;
- } else if (i == current) {
- sb = tab_selected;
- col = font_selected_color;
- } else {
- sb = tab_unselected;
- col = font_unselected_color;
- }
-
- int new_width = w + lsize;
- if (new_width > limit || (i < tabs.size() - 1 && new_width > limit_minus_buttons)) { // For the last tab, we accept if the tab covers the buttons.
- max_drawn_tab = i - 1;
- missing_right = true;
- break;
- } else {
- max_drawn_tab = i;
- }
-
- Rect2 sb_rect;
- if (rtl) {
- sb_rect = Rect2(size.width - w - tabs[i].size_cache, 0, tabs[i].size_cache, h);
- } else {
- sb_rect = Rect2(w, 0, tabs[i].size_cache, h);
- }
- sb->draw(ci, sb_rect);
- w += sb->get_margin(SIDE_LEFT);
+ bool rtl = is_layout_rtl();
+ Vector2 size = get_size();
+ int limit_minus_buttons = size.width - incr->get_width() - decr->get_width();
- Size2i sb_ms = sb->get_minimum_size();
- Ref<Texture2D> icon = tabs[i].icon;
- if (icon.is_valid()) {
- if (rtl) {
- icon->draw(ci, Point2i(size.width - w - icon->get_width(), sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
- } else {
- icon->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
- }
- if (tabs[i].text != "") {
- w += icon->get_width() + get_theme_constant(SNAME("hseparation"));
- }
- }
+ int ofs = tabs[offset].ofs_cache;
- if (rtl) {
- Vector2 text_pos = Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- tabs[i].text_buf->draw(ci, text_pos, col);
- } else {
- Vector2 text_pos = Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- tabs[i].text_buf->draw(ci, text_pos, col);
+ // Draw unselected tabs in the back.
+ for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
}
- w += tabs[i].size_text;
-
- if (tabs[i].right_button.is_valid()) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
- Ref<Texture2D> rb = tabs[i].right_button;
+ if (i != current) {
+ Ref<StyleBox> sb;
+ Color col;
- w += get_theme_constant(SNAME("hseparation"));
-
- Rect2 rb_rect;
- rb_rect.size = style->get_minimum_size() + rb->get_size();
- if (rtl) {
- rb_rect.position.x = size.width - w - rb_rect.size.x;
+ if (tabs[i].disabled) {
+ sb = tab_disabled;
+ col = font_disabled_color;
+ } else if (i == current) {
+ sb = tab_selected;
+ col = font_selected_color;
} else {
- rb_rect.position.x = w;
- }
- rb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
-
- if (rb_hover == i) {
- if (rb_pressing) {
- get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
- } else {
- style->draw(ci, rb_rect);
- }
+ sb = tab_unselected;
+ col = font_unselected_color;
}
- if (rtl) {
- rb->draw(ci, Point2i(size.width - w - rb_rect.size.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
- } else {
- rb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
- }
- w += rb->get_width();
- tabs.write[i].rb_rect = rb_rect;
+ _draw_tab(sb, col, i, rtl ? size.width - ofs - tabs[i].size_cache : ofs);
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
- Ref<Texture2D> cb = close;
-
- w += get_theme_constant(SNAME("hseparation"));
-
- Rect2 cb_rect;
- cb_rect.size = style->get_minimum_size() + cb->get_size();
- if (rtl) {
- cb_rect.position.x = size.width - w - cb_rect.size.x;
- } else {
- cb_rect.position.x = w;
- }
- cb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
-
- if (!tabs[i].disabled && cb_hover == i) {
- if (cb_pressing) {
- get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect);
- } else {
- style->draw(ci, cb_rect);
- }
- }
+ ofs += tabs[i].size_cache;
+ }
- if (rtl) {
- cb->draw(ci, Point2i(size.width - w - cb_rect.size.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
- } else {
- cb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
- }
- w += cb->get_width();
- tabs.write[i].cb_rect = cb_rect;
- }
+ // Draw selected tab in the front, but only if it's visible.
+ if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) {
+ Ref<StyleBox> sb = tabs[current].disabled ? tab_disabled : tab_selected;
+ float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache;
- w += sb->get_margin(SIDE_RIGHT);
+ _draw_tab(sb, font_selected_color, current, x);
}
- if (offset > 0 || missing_right) {
- int vofs = (get_size().height - incr->get_size().height) / 2;
+ if (buttons_visible) {
+ Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight"));
+ Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight"));
+
+ int vofs = (size.height - incr->get_size().height) / 2;
if (rtl) {
if (missing_right) {
@@ -501,29 +433,180 @@ void TabBar::_notification(int p_what) {
draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5));
}
}
+ }
- buttons_visible = true;
- } else {
- buttons_visible = false;
+ if (dragging_valid_tab) {
+ int x;
+
+ int tab_hover = get_hovered_tab();
+ if (tab_hover != -1) {
+ Rect2 tab_rect = get_tab_rect(tab_hover);
+
+ x = tab_rect.position.x;
+ if (get_local_mouse_position().x > x + tab_rect.size.width / 2) {
+ x += tab_rect.size.width;
+ }
+ } else {
+ if (rtl ^ (get_local_mouse_position().x < get_tab_rect(0).position.x)) {
+ x = get_tab_rect(0).position.x;
+ if (rtl) {
+ x += get_tab_rect(0).size.width;
+ }
+ } else {
+ Rect2 tab_rect = get_tab_rect(get_tab_count() - 1);
+
+ x = tab_rect.position.x;
+ if (!rtl) {
+ x += tab_rect.size.width;
+ }
+ }
+ }
+
+ Ref<Texture2D> drop_mark = get_theme_icon(SNAME("drop_mark"));
+ Color drop_mark_color = get_theme_color(SNAME("drop_mark_color"));
+
+ drop_mark->draw(get_canvas_item(), Point2(x - drop_mark->get_width() / 2, (size.height - drop_mark->get_height()) / 2), drop_mark_color);
}
} break;
}
}
+void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
+ RID ci = get_canvas_item();
+ bool rtl = is_layout_rtl();
+
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+ int hseparation = get_theme_constant(SNAME("h_separation"));
+
+ Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height);
+ p_tab_style->draw(ci, sb_rect);
+
+ p_x += rtl ? tabs[p_index].size_cache - p_tab_style->get_margin(SIDE_LEFT) : p_tab_style->get_margin(SIDE_LEFT);
+
+ Size2i sb_ms = p_tab_style->get_minimum_size();
+
+ // Draw the icon.
+ Ref<Texture2D> icon = tabs[p_index].icon;
+ if (icon.is_valid()) {
+ icon->draw(ci, Point2i(rtl ? p_x - icon->get_width() : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
+
+ p_x = rtl ? p_x - icon->get_width() - hseparation : p_x + icon->get_width() + hseparation;
+ }
+
+ // Draw the text.
+ if (!tabs[p_index].text.is_empty()) {
+ Point2i text_pos = Point2i(rtl ? p_x - tabs[p_index].size_text : p_x,
+ p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[p_index].text_buf->get_size().y) / 2);
+
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tabs[p_index].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ tabs[p_index].text_buf->draw(ci, text_pos, p_font_color);
+
+ p_x = rtl ? p_x - tabs[p_index].size_text - hseparation : p_x + tabs[p_index].size_text + hseparation;
+ }
+
+ // Draw and calculate rect of the right button.
+ if (tabs[p_index].right_button.is_valid()) {
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> rb = tabs[p_index].right_button;
+
+ Rect2 rb_rect;
+ rb_rect.size = style->get_minimum_size() + rb->get_size();
+ rb_rect.position.x = rtl ? p_x - rb_rect.size.width : p_x;
+ rb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
+
+ tabs.write[p_index].rb_rect = rb_rect;
+
+ if (rb_hover == p_index) {
+ if (rb_pressing) {
+ get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
+ } else {
+ style->draw(ci, rb_rect);
+ }
+ }
+
+ rb->draw(ci, Point2i(rb_rect.position.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
+
+ p_x = rtl ? rb_rect.position.x : rb_rect.position.x + rb_rect.size.width;
+ }
+
+ // Draw and calculate rect of the close button.
+ if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_index == current)) {
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
+
+ Rect2 cb_rect;
+ cb_rect.size = style->get_minimum_size() + cb->get_size();
+ cb_rect.position.x = rtl ? p_x - cb_rect.size.width : p_x;
+ cb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
+
+ tabs.write[p_index].cb_rect = cb_rect;
+
+ if (!tabs[p_index].disabled && cb_hover == p_index) {
+ if (cb_pressing) {
+ get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect);
+ } else {
+ style->draw(ci, cb_rect);
+ }
+ }
+
+ cb->draw(ci, Point2i(cb_rect.position.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
+ }
+}
+
+void TabBar::set_tab_count(int p_count) {
+ if (p_count == tabs.size()) {
+ return;
+ }
+
+ ERR_FAIL_COND(p_count < 0);
+ tabs.resize(p_count);
+
+ if (p_count == 0) {
+ offset = 0;
+ max_drawn_tab = 0;
+ current = 0;
+ previous = 0;
+ } else {
+ offset = MIN(offset, p_count - 1);
+ max_drawn_tab = MIN(max_drawn_tab, p_count - 1);
+ current = MIN(current, p_count - 1);
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ }
+
+ update();
+ update_minimum_size();
+ notify_property_list_changed();
+}
+
int TabBar::get_tab_count() const {
return tabs.size();
}
void TabBar::set_current_tab(int p_current) {
- if (current == p_current) {
- return;
- }
ERR_FAIL_INDEX(p_current, get_tab_count());
previous = current;
current = p_current;
+ if (current == previous) {
+ emit_signal(SNAME("tab_selected"), current);
+ return;
+ }
+
+ emit_signal(SNAME("tab_selected"), current);
+
_update_cache();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
emit_signal(SNAME("tab_changed"), p_current);
@@ -552,9 +635,15 @@ bool TabBar::get_offset_buttons_visible() const {
void TabBar::set_tab_title(int p_tab, const String &p_title) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].text = p_title;
+
_shape(p_tab);
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
- minimum_size_changed();
+ update_minimum_size();
}
String TabBar::get_tab_title(int p_tab) const {
@@ -565,6 +654,7 @@ String TabBar::get_tab_title(int p_tab) const {
void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) {
ERR_FAIL_INDEX(p_tab, tabs.size());
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+
if (tabs[p_tab].text_direction != p_text_direction) {
tabs.write[p_tab].text_direction = p_text_direction;
_shape(p_tab);
@@ -580,22 +670,38 @@ Control::TextDirection TabBar::get_tab_text_direction(int p_tab) const {
void TabBar::clear_tab_opentype_features(int p_tab) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].opentype_features.clear();
+
_shape(p_tab);
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
void TabBar::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) {
ERR_FAIL_INDEX(p_tab, tabs.size());
+
int32_t tag = TS->name_to_tag(p_name);
if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) {
tabs.write[p_tab].opentype_features[tag] = p_value;
+
_shape(p_tab);
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
}
int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1);
+
int32_t tag = TS->name_to_tag(p_name);
if (!tabs[p_tab].opentype_features.has(tag)) {
return -1;
@@ -605,10 +711,17 @@ int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const {
void TabBar::set_tab_language(int p_tab, const String &p_language) {
ERR_FAIL_INDEX(p_tab, tabs.size());
+
if (tabs[p_tab].language != p_language) {
tabs.write[p_tab].language = p_language;
_shape(p_tab);
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
}
@@ -620,8 +733,14 @@ String TabBar::get_tab_language(int p_tab) const {
void TabBar::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].icon = p_icon;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
@@ -632,23 +751,53 @@ Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].disabled = p_disabled;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
-bool TabBar::get_tab_disabled(int p_tab) const {
+bool TabBar::is_tab_disabled(int p_tab) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), false);
return tabs[p_tab].disabled;
}
-void TabBar::set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button) {
+void TabBar::set_tab_hidden(int p_tab, bool p_hidden) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+ tabs.write[p_tab].hidden = p_hidden;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ update();
+ update_minimum_size();
+}
+
+bool TabBar::is_tab_hidden(int p_tab) const {
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), false);
+ return tabs[p_tab].hidden;
+}
+
+void TabBar::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_tab, tabs.size());
- tabs.write[p_tab].right_button = p_right_button;
+ tabs.write[p_tab].right_button = p_icon;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
- minimum_size_changed();
+ update_minimum_size();
}
-Ref<Texture2D> TabBar::get_tab_right_button(int p_tab) const {
+Ref<Texture2D> TabBar::get_tab_button_icon(int p_tab) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture2D>());
return tabs[p_tab].right_button;
}
@@ -658,99 +807,146 @@ void TabBar::_update_hover() {
return;
}
+ ERR_FAIL_COND(tabs.is_empty());
+
const Point2 &pos = get_local_mouse_position();
- // test hovering to display right or close button
+ // Test hovering to display right or close button.
int hover_now = -1;
int hover_buttons = -1;
- for (int i = offset; i < tabs.size(); i++) {
+ for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
Rect2 rect = get_tab_rect(i);
if (rect.has_point(pos)) {
hover_now = i;
}
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_hover = i;
cb_hover = -1;
hover_buttons = i;
- break;
} else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) {
cb_hover = i;
rb_hover = -1;
hover_buttons = i;
+ }
+
+ if (hover_buttons != -1) {
+ update();
break;
}
}
+
if (hover != hover_now) {
hover = hover_now;
- emit_signal(SNAME("tab_hovered"), hover);
+
+ if (hover != -1) {
+ emit_signal(SNAME("tab_hovered"), hover);
+ }
}
- if (hover_buttons == -1) { // no hover
+ if (hover_buttons == -1) { // No hover.
+ int rb_hover_old = rb_hover;
+ int cb_hover_old = cb_hover;
+
rb_hover = hover_buttons;
cb_hover = hover_buttons;
+
+ if (rb_hover != rb_hover_old || cb_hover != cb_hover_old) {
+ update();
+ }
}
}
void TabBar::_update_cache() {
+ if (tabs.is_empty()) {
+ return;
+ }
+
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
- int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
+
+ int limit = get_size().width;
+ int limit_minus_buttons = limit - incr->get_width() - decr->get_width();
int w = 0;
- int mw = 0;
- int size_fixed = 0;
- int count_resize = 0;
+
+ max_drawn_tab = tabs.size() - 1;
+
for (int i = 0; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = mw;
- tabs.write[i].size_cache = get_tab_width(i);
- tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x);
tabs.write[i].text_buf->set_width(-1);
- mw += tabs[i].size_cache;
- if (tabs[i].size_cache <= min_width || i == current) {
- size_fixed += tabs[i].size_cache;
- } else {
- count_resize++;
+ tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x);
+ tabs.write[i].size_cache = get_tab_width(i);
+
+ if (max_width > 0 && tabs[i].size_cache > max_width) {
+ int size_textless = tabs[i].size_cache - tabs[i].size_text;
+ int mw = MAX(size_textless, max_width);
+
+ tabs.write[i].size_text = MAX(mw - size_textless, 1);
+ tabs.write[i].text_buf->set_width(tabs[i].size_text);
+ tabs.write[i].size_cache = size_textless + tabs[i].size_text;
}
- }
- int m_width = min_width;
- if (count_resize > 0) {
- m_width = MAX((limit_minus_buttons - size_fixed) / count_resize, min_width);
- }
- for (int i = offset; i < tabs.size(); i++) {
- Ref<StyleBox> sb;
- if (tabs[i].disabled) {
- sb = tab_disabled;
- } else if (i == current) {
- sb = tab_selected;
- } else {
- sb = tab_unselected;
+
+ if (i < offset || i > max_drawn_tab) {
+ tabs.write[i].ofs_cache = 0;
+ continue;
}
- int lsize = tabs[i].size_cache;
- int slen = tabs[i].size_text;
- if (min_width > 0 && mw > limit_minus_buttons && i != current) {
- if (lsize > m_width) {
- slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT));
- if (tabs[i].icon.is_valid()) {
- slen -= tabs[i].icon->get_width();
- slen -= get_theme_constant(SNAME("hseparation"));
- }
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- slen -= cb->get_width();
- slen -= get_theme_constant(SNAME("hseparation"));
+
+ tabs.write[i].ofs_cache = w;
+
+ if (tabs[i].hidden) {
+ continue;
+ }
+
+ w += tabs[i].size_cache;
+
+ // Check if all tabs would fit inside the area.
+ if (clip_tabs && i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) {
+ tabs.write[i].ofs_cache = 0;
+
+ w -= tabs[i].size_cache;
+ max_drawn_tab = i - 1;
+
+ while (w > limit_minus_buttons && max_drawn_tab > offset) {
+ tabs.write[max_drawn_tab].ofs_cache = 0;
+
+ if (!tabs[max_drawn_tab].hidden) {
+ w -= tabs[max_drawn_tab].size_cache;
}
- slen = MAX(slen, 1);
- lsize = m_width;
+
+ max_drawn_tab--;
}
}
+ }
+
+ missing_right = max_drawn_tab < tabs.size() - 1;
+ buttons_visible = offset > 0 || missing_right;
+
+ if (tab_alignment == ALIGNMENT_LEFT) {
+ _update_hover();
+ return;
+ }
+
+ if (tab_alignment == ALIGNMENT_CENTER) {
+ w = ((buttons_visible ? limit_minus_buttons : limit) - w) / 2;
+ } else if (tab_alignment == ALIGNMENT_RIGHT) {
+ w = (buttons_visible ? limit_minus_buttons : limit) - w;
+ }
+
+ for (int i = offset; i <= max_drawn_tab; i++) {
tabs.write[i].ofs_cache = w;
- tabs.write[i].size_cache = lsize;
- tabs.write[i].size_text = slen;
- tabs.write[i].text_buf->set_width(slen);
- w += lsize;
+
+ if (!tabs[i].hidden) {
+ w += tabs[i].size_cache;
+ }
}
+
+ _update_hover();
}
void TabBar::_on_mouse_exited() {
@@ -758,65 +954,87 @@ void TabBar::_on_mouse_exited() {
cb_hover = -1;
hover = -1;
highlight_arrow = -1;
+ dragging_valid_tab = false;
+
update();
}
void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
Tab t;
t.text = p_str;
- t.xl_text = atr(p_str);
- t.text_buf.instantiate();
t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
- t.text_buf->add_string(t.xl_text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
t.icon = p_icon;
- t.disabled = false;
- t.ofs_cache = 0;
- t.size_cache = 0;
-
tabs.push_back(t);
+
+ _shape(tabs.size() - 1);
_update_cache();
- call_deferred(SNAME("_update_hover"));
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
- minimum_size_changed();
+ update_minimum_size();
+
+ if (tabs.size() == 1 && is_inside_tree()) {
+ emit_signal(SNAME("tab_changed"), 0);
+ }
}
void TabBar::clear_tabs() {
+ if (tabs.is_empty()) {
+ return;
+ }
+
tabs.clear();
+ offset = 0;
+ max_drawn_tab = 0;
current = 0;
previous = 0;
- call_deferred(SNAME("_update_hover"));
+
update();
+ update_minimum_size();
+ notify_property_list_changed();
}
void TabBar::remove_tab(int p_idx) {
ERR_FAIL_INDEX(p_idx, tabs.size());
- tabs.remove(p_idx);
- if (current >= p_idx) {
+ tabs.remove_at(p_idx);
+
+ bool is_tab_changing = current == p_idx && !tabs.is_empty();
+
+ if (current >= p_idx && current > 0) {
current--;
}
- _update_cache();
- call_deferred(SNAME("_update_hover"));
- update();
- minimum_size_changed();
- if (current < 0) {
- current = 0;
+ if (tabs.is_empty()) {
+ offset = 0;
+ max_drawn_tab = 0;
previous = 0;
- }
- if (current >= tabs.size()) {
- current = tabs.size() - 1;
+ } else {
+ offset = MIN(offset, tabs.size() - 1);
+ max_drawn_tab = MIN(max_drawn_tab, tabs.size() - 1);
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
}
- _ensure_no_over_offset();
+ update();
+ update_minimum_size();
+ notify_property_list_changed();
+
+ if (is_tab_changing && is_inside_tree()) {
+ emit_signal(SNAME("tab_changed"), current);
+ }
}
Variant TabBar::get_drag_data(const Point2 &p_point) {
if (!drag_to_rearrange_enabled) {
- return Variant();
+ return Control::get_drag_data(p_point); // Allow stuff like TabContainer to override it.
}
int tab_over = get_tab_idx_at_point(p_point);
-
if (tab_over < 0) {
return Variant();
}
@@ -826,27 +1044,26 @@ Variant TabBar::get_drag_data(const Point2 &p_point) {
if (!tabs[tab_over].icon.is_null()) {
TextureRect *tf = memnew(TextureRect);
tf->set_texture(tabs[tab_over].icon);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
drag_preview->add_child(tf);
}
+
Label *label = memnew(Label(tabs[tab_over].xl_text));
drag_preview->add_child(label);
- if (!tabs[tab_over].right_button.is_null()) {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(tabs[tab_over].right_button);
- drag_preview->add_child(tf);
- }
+
set_drag_preview(drag_preview);
Dictionary drag_data;
drag_data["type"] = "tab_element";
drag_data["tab_element"] = tab_over;
drag_data["from_path"] = get_path();
+
return drag_data;
}
bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
if (!drag_to_rearrange_enabled) {
- return false;
+ return Control::can_drop_data(p_point, p_data); // Allow stuff like TabContainer to override it.
}
Dictionary d = p_data;
@@ -860,7 +1077,7 @@ bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
if (from_path == to_path) {
return true;
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between other TabBars
+ // Drag and drop between other TabBars.
Node *from_node = get_node(from_path);
TabBar *from_tabs = Object::cast_to<TabBar>(from_node);
if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
@@ -868,16 +1085,16 @@ bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
}
}
}
+
return false;
}
void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
if (!drag_to_rearrange_enabled) {
+ Control::drop_data(p_point, p_data); // Allow stuff like TabContainer to override it.
return;
}
- int hover_now = get_tab_idx_at_point(p_point);
-
Dictionary d = p_data;
if (!d.has("type")) {
return;
@@ -885,36 +1102,84 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
if (String(d["type"]) == "tab_element") {
int tab_from_id = d["tab_element"];
+ int hover_now = get_tab_idx_at_point(p_point);
NodePath from_path = d["from_path"];
NodePath to_path = get_path();
+
if (from_path == to_path) {
- if (hover_now < 0) {
- hover_now = get_tab_count() - 1;
+ if (tab_from_id == hover_now) {
+ return;
}
+
+ // Drop the new tab to the left or right depending on where the target tab is being hovered.
+ if (hover_now != -1) {
+ Rect2 tab_rect = get_tab_rect(hover_now);
+ if (is_layout_rtl() ^ (p_point.x <= tab_rect.position.x + tab_rect.size.width / 2)) {
+ if (hover_now > tab_from_id) {
+ hover_now -= 1;
+ }
+ } else if (tab_from_id > hover_now) {
+ hover_now += 1;
+ }
+ } else {
+ hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1;
+ }
+
move_tab(tab_from_id, hover_now);
- emit_signal(SNAME("active_tab_rearranged"), hover_now);
- set_current_tab(hover_now);
+ if (!is_tab_disabled(hover_now)) {
+ emit_signal(SNAME("active_tab_rearranged"), hover_now);
+ set_current_tab(hover_now);
+ }
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between Tabs
+ // Drag and drop between Tabs.
+
Node *from_node = get_node(from_path);
TabBar *from_tabs = Object::cast_to<TabBar>(from_node);
+
if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
if (tab_from_id >= from_tabs->get_tab_count()) {
return;
}
- Tab moving_tab = from_tabs->tabs[tab_from_id];
- if (hover_now < 0) {
- hover_now = get_tab_count();
+
+ // Drop the new tab to the left or right depending on where the target tab is being hovered.
+ if (hover_now != -1) {
+ Rect2 tab_rect = get_tab_rect(hover_now);
+ if (is_layout_rtl() ^ (p_point.x > tab_rect.position.x + tab_rect.size.width / 2)) {
+ hover_now += 1;
+ }
+ } else {
+ hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count();
}
- tabs.insert(hover_now, moving_tab);
+
+ Tab moving_tab = from_tabs->tabs[tab_from_id];
from_tabs->remove_tab(tab_from_id);
- set_current_tab(hover_now);
- emit_signal(SNAME("tab_changed"), hover_now);
- _update_cache();
+ tabs.insert(hover_now, moving_tab);
+
+ if (tabs.size() > 1) {
+ if (current >= hover_now) {
+ current++;
+ }
+ if (previous >= hover_now) {
+ previous++;
+ }
+ }
+
+ if (!is_tab_disabled(hover_now)) {
+ set_current_tab(hover_now);
+ } else {
+ _update_cache();
+ update();
+ }
+
+ update_minimum_size();
+
+ if (tabs.size() == 1) {
+ emit_signal(SNAME("tab_selected"), 0);
+ emit_signal(SNAME("tab_changed"), 0);
+ }
}
}
}
- update();
}
int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
@@ -929,14 +1194,16 @@ int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
return hover_now;
}
-void TabBar::set_tab_align(TabAlign p_align) {
- ERR_FAIL_INDEX(p_align, ALIGN_MAX);
- tab_align = p_align;
+void TabBar::set_tab_alignment(AlignmentMode p_alignment) {
+ ERR_FAIL_INDEX(p_alignment, ALIGNMENT_MAX);
+ tab_alignment = p_alignment;
+
+ _update_cache();
update();
}
-TabBar::TabAlign TabBar::get_tab_align() const {
- return tab_align;
+TabBar::AlignmentMode TabBar::get_tab_alignment() const {
+ return tab_alignment;
}
void TabBar::set_clip_tabs(bool p_clip_tabs) {
@@ -944,28 +1211,59 @@ void TabBar::set_clip_tabs(bool p_clip_tabs) {
return;
}
clip_tabs = p_clip_tabs;
+
+ if (!clip_tabs) {
+ offset = 0;
+ max_drawn_tab = 0;
+ }
+
+ _update_cache();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
- minimum_size_changed();
+ update_minimum_size();
}
bool TabBar::get_clip_tabs() const {
return clip_tabs;
}
-void TabBar::move_tab(int from, int to) {
- if (from == to) {
+void TabBar::move_tab(int p_from, int p_to) {
+ if (p_from == p_to) {
return;
}
- ERR_FAIL_INDEX(from, tabs.size());
- ERR_FAIL_INDEX(to, tabs.size());
+ ERR_FAIL_INDEX(p_from, tabs.size());
+ ERR_FAIL_INDEX(p_to, tabs.size());
+
+ Tab tab_from = tabs[p_from];
+ tabs.remove_at(p_from);
+ tabs.insert(p_to, tab_from);
+
+ if (current == p_from) {
+ current = p_to;
+ } else if (current > p_from && current <= p_to) {
+ current--;
+ } else if (current < p_from && current >= p_to) {
+ current++;
+ }
- Tab tab_from = tabs[from];
- tabs.remove(from);
- tabs.insert(to, tab_from);
+ if (previous == p_from) {
+ previous = p_to;
+ } else if (previous > p_from && previous >= p_to) {
+ previous--;
+ } else if (previous < p_from && previous <= p_to) {
+ previous++;
+ }
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ notify_property_list_changed();
}
int TabBar::get_tab_width(int p_idx) const {
@@ -974,101 +1272,134 @@ int TabBar::get_tab_width(int p_idx) const {
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
+ int hseparation = get_theme_constant(SNAME("h_separation"));
- int x = 0;
+ Ref<StyleBox> style;
+
+ if (tabs[p_idx].disabled) {
+ style = tab_disabled;
+ } else if (current == p_idx) {
+ style = tab_selected;
+ } else {
+ style = tab_unselected;
+ }
+ int x = style->get_minimum_size().width;
Ref<Texture2D> tex = tabs[p_idx].icon;
if (tex.is_valid()) {
- x += tex->get_width();
- if (tabs[p_idx].text != "") {
- x += get_theme_constant(SNAME("hseparation"));
- }
+ x += tex->get_width() + hseparation;
}
- x += Math::ceil(tabs[p_idx].text_buf->get_size().x);
-
- if (tabs[p_idx].disabled) {
- x += tab_disabled->get_minimum_size().width;
- } else if (current == p_idx) {
- x += tab_selected->get_minimum_size().width;
- } else {
- x += tab_unselected->get_minimum_size().width;
+ if (!tabs[p_idx].text.is_empty()) {
+ x += tabs[p_idx].size_text + hseparation;
}
+ bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current);
+
if (tabs[p_idx].right_button.is_valid()) {
+ Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> rb = tabs[p_idx].right_button;
- x += rb->get_width();
- x += get_theme_constant(SNAME("hseparation"));
+
+ if (close_visible) {
+ x += btn_style->get_minimum_size().width + rb->get_width();
+ } else {
+ x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+ }
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current)) {
+ if (close_visible) {
+ Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- x += cb->get_width();
- x += get_theme_constant(SNAME("hseparation"));
+ x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + hseparation;
+ }
+
+ if (x > style->get_minimum_size().width) {
+ x -= hseparation;
}
return x;
}
void TabBar::_ensure_no_over_offset() {
- if (!is_inside_tree()) {
+ if (!is_inside_tree() || !buttons_visible) {
return;
}
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-
- int limit = get_size().width;
int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
- while (offset > 0) {
- int total_w = 0;
- for (int i = offset - 1; i < tabs.size(); i++) {
- total_w += tabs[i].size_cache;
+ int prev_offset = offset;
+
+ int total_w = tabs[max_drawn_tab].ofs_cache + tabs[max_drawn_tab].size_cache - tabs[offset].ofs_cache;
+ for (int i = offset; i > 0; i--) {
+ if (tabs[i - 1].hidden) {
+ continue;
}
- if ((buttons_visible && total_w < limit_minus_buttons) || total_w < limit) { // For the last tab, we accept if the tab covers the buttons.
+ total_w += tabs[i - 1].size_cache;
+
+ if (total_w < limit_minus_buttons) {
offset--;
- update();
} else {
break;
}
}
-}
-void TabBar::ensure_tab_visible(int p_idx) {
- if (!is_inside_tree()) {
- return;
+ if (prev_offset != offset) {
+ _update_cache();
+ update();
}
+}
- if (tabs.size() == 0) {
+void TabBar::ensure_tab_visible(int p_idx) {
+ if (!is_inside_tree() || !buttons_visible) {
return;
}
ERR_FAIL_INDEX(p_idx, tabs.size());
- if (p_idx == offset) {
+ if (tabs[p_idx].hidden || (p_idx >= offset && p_idx <= max_drawn_tab)) {
return;
}
+
if (p_idx < offset) {
offset = p_idx;
+ _update_cache();
update();
+
return;
}
- int prev_offset = offset;
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
- int limit = get_size().width;
int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
- for (int i = offset; i <= p_idx; i++) {
- int total_w = tabs[i].ofs_cache + tabs[i].size_cache;
- if (total_w > limit || (buttons_visible && total_w > limit_minus_buttons)) {
+ int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache;
+ for (int i = max_drawn_tab; i <= p_idx; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
+ total_w += tabs[i].size_cache;
+ }
+
+ int prev_offset = offset;
+
+ for (int i = offset; i < p_idx; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
+ if (total_w > limit_minus_buttons) {
+ total_w -= tabs[i].size_cache;
offset++;
+ } else {
+ break;
}
}
if (prev_offset != offset) {
+ _update_cache();
update();
}
}
@@ -1085,15 +1416,35 @@ Rect2 TabBar::get_tab_rect(int p_tab) const {
void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX);
cb_displaypolicy = p_policy;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
TabBar::CloseButtonDisplayPolicy TabBar::get_tab_close_display_policy() const {
return cb_displaypolicy;
}
-void TabBar::set_min_width(int p_width) {
- min_width = p_width;
+void TabBar::set_max_tab_width(int p_width) {
+ ERR_FAIL_COND(p_width < 0);
+ max_width = p_width;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ update();
+ update_minimum_size();
+}
+
+int TabBar::get_max_tab_width() const {
+ return max_width;
}
void TabBar::set_scrolling_enabled(bool p_enabled) {
@@ -1120,6 +1471,17 @@ int TabBar::get_tabs_rearrange_group() const {
return tabs_rearrange_group;
}
+void TabBar::set_scroll_to_selected(bool p_enabled) {
+ scroll_to_selected = p_enabled;
+ if (p_enabled) {
+ ensure_tab_visible(current);
+ }
+}
+
+bool TabBar::get_scroll_to_selected() const {
+ return scroll_to_selected;
+}
+
void TabBar::set_select_with_rmb(bool p_enabled) {
select_with_rmb = p_enabled;
}
@@ -1128,8 +1490,60 @@ bool TabBar::get_select_with_rmb() const {
return select_with_rmb;
}
+bool TabBar::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("tab_") && components[0].trim_prefix("tab_").is_valid_int()) {
+ int tab_index = components[0].trim_prefix("tab_").to_int();
+ String property = components[1];
+ if (property == "title") {
+ set_tab_title(tab_index, p_value);
+ return true;
+ } else if (property == "icon") {
+ set_tab_icon(tab_index, p_value);
+ return true;
+ } else if (components[1] == "disabled") {
+ set_tab_disabled(tab_index, p_value);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TabBar::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("tab_") && components[0].trim_prefix("tab_").is_valid_int()) {
+ int tab_index = components[0].trim_prefix("tab_").to_int();
+ String property = components[1];
+ if (property == "title") {
+ r_ret = get_tab_title(tab_index);
+ return true;
+ } else if (property == "icon") {
+ r_ret = get_tab_icon(tab_index);
+ return true;
+ } else if (components[1] == "disabled") {
+ r_ret = is_tab_disabled(tab_index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void TabBar::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < tabs.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("tab_%d/title", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("tab_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ pi.usage &= ~(get_tab_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("tab_%d/disabled", i));
+ pi.usage &= ~(!is_tab_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
void TabBar::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_update_hover"), &TabBar::_update_hover);
+ ClassDB::bind_method(D_METHOD("set_tab_count", "count"), &TabBar::set_tab_count);
ClassDB::bind_method(D_METHOD("get_tab_count"), &TabBar::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabBar::set_current_tab);
ClassDB::bind_method(D_METHOD("get_current_tab"), &TabBar::get_current_tab);
@@ -1145,12 +1559,17 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language);
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon);
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabBar::get_tab_icon);
+ ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabBar::set_tab_button_icon);
+ ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabBar::get_tab_button_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabBar::set_tab_disabled);
- ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabBar::get_tab_disabled);
+ ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabBar::is_tab_disabled);
+ ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabBar::set_tab_hidden);
+ ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabBar::is_tab_hidden);
ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &TabBar::remove_tab);
ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &TabBar::add_tab, DEFVAL(""), DEFVAL(Ref<Texture2D>()));
- ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &TabBar::set_tab_align);
- ClassDB::bind_method(D_METHOD("get_tab_align"), &TabBar::get_tab_align);
+ ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabBar::get_tab_idx_at_point);
+ ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabBar::set_tab_alignment);
+ ClassDB::bind_method(D_METHOD("get_tab_alignment"), &TabBar::get_tab_alignment);
ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabBar::set_clip_tabs);
ClassDB::bind_method(D_METHOD("get_clip_tabs"), &TabBar::get_clip_tabs);
ClassDB::bind_method(D_METHOD("get_tab_offset"), &TabBar::get_tab_offset);
@@ -1160,34 +1579,45 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_tab", "from", "to"), &TabBar::move_tab);
ClassDB::bind_method(D_METHOD("set_tab_close_display_policy", "policy"), &TabBar::set_tab_close_display_policy);
ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &TabBar::get_tab_close_display_policy);
+ ClassDB::bind_method(D_METHOD("set_max_tab_width", "width"), &TabBar::set_max_tab_width);
+ ClassDB::bind_method(D_METHOD("get_max_tab_width"), &TabBar::get_max_tab_width);
ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &TabBar::set_scrolling_enabled);
ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &TabBar::get_scrolling_enabled);
ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabBar::set_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabBar::get_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabBar::set_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabBar::get_tabs_rearrange_group);
-
+ ClassDB::bind_method(D_METHOD("set_scroll_to_selected", "enabled"), &TabBar::set_scroll_to_selected);
+ ClassDB::bind_method(D_METHOD("get_scroll_to_selected"), &TabBar::get_scroll_to_selected);
ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &TabBar::set_select_with_rmb);
ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &TabBar::get_select_with_rmb);
+ ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_close_pressed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to")));
- ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_tab_width", PROPERTY_HINT_RANGE, "0,99999,1"), "set_max_tab_width", "get_max_tab_width");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tabs_rearrange_group"), "set_tabs_rearrange_group", "get_tabs_rearrange_group");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_to_selected"), "set_scroll_to_selected", "get_scroll_to_selected");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_with_rmb"), "set_select_with_rmb", "get_select_with_rmb");
+
+ ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_");
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- BIND_ENUM_CONSTANT(ALIGN_MAX);
+ BIND_ENUM_CONSTANT(ALIGNMENT_LEFT);
+ BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
+ BIND_ENUM_CONSTANT(ALIGNMENT_RIGHT);
+ BIND_ENUM_CONSTANT(ALIGNMENT_MAX);
BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER);
BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ACTIVE_ONLY);
@@ -1196,5 +1626,6 @@ void TabBar::_bind_methods() {
}
TabBar::TabBar() {
+ set_size(Size2(get_size().width, get_minimum_size().height));
connect("mouse_exited", callable_mp(this, &TabBar::_on_mouse_exited));
}
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index 411a62b1d9..548a2e62af 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,11 +38,11 @@ class TabBar : public Control {
GDCLASS(TabBar, Control);
public:
- enum TabAlign {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_MAX
+ enum AlignmentMode {
+ ALIGNMENT_LEFT,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_RIGHT,
+ ALIGNMENT_MAX,
};
enum CloseButtonDisplayPolicy {
@@ -63,16 +63,19 @@ private:
Ref<TextLine> text_buf;
Ref<Texture2D> icon;
- int ofs_cache = 0;
bool disabled = false;
+ bool hidden = false;
+ int ofs_cache = 0;
int size_cache = 0;
int size_text = 0;
- int x_cache = 0;
- int x_size_cache = 0;
Ref<Texture2D> right_button;
Rect2 rb_rect;
Rect2 cb_rect;
+
+ Tab() {
+ text_buf.instantiate();
+ }
};
int offset = 0;
@@ -83,7 +86,7 @@ private:
Vector<Tab> tabs;
int current = 0;
int previous = 0;
- TabAlign tab_align = ALIGN_CENTER;
+ AlignmentMode tab_alignment = ALIGNMENT_LEFT;
bool clip_tabs = true;
int rb_hover = -1;
bool rb_pressing = false;
@@ -95,9 +98,11 @@ private:
CloseButtonDisplayPolicy cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
int hover = -1; // Hovered tab.
- int min_width = 0;
+ int max_width = 0;
bool scrolling_enabled = true;
bool drag_to_rearrange_enabled = false;
+ bool dragging_valid_tab = false;
+ bool scroll_to_selected = true;
int tabs_rearrange_group = -1;
int get_tab_width(int p_idx) const;
@@ -109,16 +114,19 @@ private:
void _on_mouse_exited();
void _shape(int p_tab);
+ void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x);
protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
+ 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;
void _notification(int p_what);
static void _bind_methods();
Variant get_drag_data(const Point2 &p_point) override;
bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
void drop_data(const Point2 &p_point, const Variant &p_data) override;
- int get_tab_idx_at_point(const Point2 &p_point) const;
public:
void add_tab(const String &p_str = "", const Ref<Texture2D> &p_icon = Ref<Texture2D>());
@@ -140,23 +148,30 @@ public:
Ref<Texture2D> get_tab_icon(int p_tab) const;
void set_tab_disabled(int p_tab, bool p_disabled);
- bool get_tab_disabled(int p_tab) const;
+ bool is_tab_disabled(int p_tab) const;
+
+ void set_tab_hidden(int p_tab, bool p_hidden);
+ bool is_tab_hidden(int p_tab) const;
- void set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button);
- Ref<Texture2D> get_tab_right_button(int p_tab) const;
+ void set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon);
+ Ref<Texture2D> get_tab_button_icon(int p_tab) const;
- void set_tab_align(TabAlign p_align);
- TabAlign get_tab_align() const;
+ int get_tab_idx_at_point(const Point2 &p_point) const;
+
+ void set_tab_alignment(AlignmentMode p_alignment);
+ AlignmentMode get_tab_alignment() const;
void set_clip_tabs(bool p_clip_tabs);
bool get_clip_tabs() const;
- void move_tab(int from, int to);
+ void move_tab(int p_from, int p_to);
void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy);
CloseButtonDisplayPolicy get_tab_close_display_policy() const;
+ void set_tab_count(int p_count);
int get_tab_count() const;
+
void set_current_tab(int p_current);
int get_current_tab() const;
int get_previous_tab() const;
@@ -177,11 +192,16 @@ public:
void set_tabs_rearrange_group(int p_group_id);
int get_tabs_rearrange_group() const;
+ void set_scroll_to_selected(bool p_enabled);
+ bool get_scroll_to_selected() const;
+
void set_select_with_rmb(bool p_enabled);
bool get_select_with_rmb() const;
void ensure_tab_visible(int p_idx);
- void set_min_width(int p_width);
+
+ void set_max_tab_width(int p_width);
+ int get_max_tab_width() const;
Rect2 get_tab_rect(int p_tab) const;
Size2 get_minimum_size() const override;
@@ -189,7 +209,7 @@ public:
TabBar();
};
-VARIANT_ENUM_CAST(TabBar::TabAlign);
+VARIANT_ENUM_CAST(TabBar::AlignmentMode);
VARIANT_ENUM_CAST(TabBar::CloseButtonDisplayPolicy);
#endif // TAB_BAR_H
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index ff53d91ea3..8299d73b68 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,45 +30,17 @@
#include "tab_container.h"
-#include "core/object/message_queue.h"
-#include "core/string/translation.h"
-
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/texture_rect.h"
int TabContainer::_get_top_margin() const {
- if (!tabs_visible) {
- return 0;
- }
-
- // Respect the minimum tab height.
- Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
- Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
- Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
-
- int tab_height = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
-
- // Font height or higher icon wins.
- int content_height = 0;
-
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- content_height = MAX(content_height, text_buf[i]->get_size().y);
-
- Control *c = tabs[i];
- if (!c->has_meta("_tab_icon")) {
- continue;
- }
-
- Ref<Texture2D> tex = c->get_meta("_tab_icon");
- if (!tex.is_valid()) {
- continue;
- }
- content_height = MAX(content_height, tex->get_size().height);
+ int height = 0;
+ if (tabs_visible && get_tab_count() > 0) {
+ height = tab_bar->get_minimum_size().height;
}
- return tab_height + content_height;
+ return height;
}
void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
@@ -114,77 +86,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
return;
}
}
-
- // Do not activate tabs when tabs is empty.
- if (get_tab_count() == 0) {
- return;
- }
-
- Vector<Control *> tabs = _get_tabs();
-
- // Handle navigation buttons.
- if (buttons_visible_cache) {
- int popup_ofs = 0;
- if (popup) {
- popup_ofs = menu->get_width();
- }
-
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
- if (is_layout_rtl()) {
- if (pos.x < popup_ofs + decrement->get_width()) {
- if (last_tab_cache < tabs.size() - 1) {
- first_tab_cache += 1;
- update();
- }
- return;
- } else if (pos.x < popup_ofs + increment->get_width() + decrement->get_width()) {
- if (first_tab_cache > 0) {
- first_tab_cache -= 1;
- update();
- }
- return;
- }
- } else {
- if (pos.x > size.width - increment->get_width() - popup_ofs && pos.x) {
- if (last_tab_cache < tabs.size() - 1) {
- first_tab_cache += 1;
- update();
- }
- return;
- } else if (pos.x > size.width - increment->get_width() - decrement->get_width() - popup_ofs) {
- if (first_tab_cache > 0) {
- first_tab_cache -= 1;
- update();
- }
- return;
- }
- }
- }
-
- // Activate the clicked tab.
- if (is_layout_rtl()) {
- pos.x = size.width - pos.x;
- }
-
- if (pos.x < tabs_ofs_cache) {
- return;
- }
-
- pos.x -= tabs_ofs_cache;
- for (int i = first_tab_cache; i <= last_tab_cache; i++) {
- if (get_tab_hidden(i)) {
- continue;
- }
- int tab_width = _get_tab_width(i);
- if (pos.x < tab_width) {
- if (!get_tab_disabled(i)) {
- set_current_tab(i);
- }
- break;
- }
- pos.x -= tab_width;
- }
}
Ref<InputEventMouseMotion> mm = p_event;
@@ -195,9 +96,8 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
// Mouse must be on tabs in the tab header area.
if (pos.y > _get_top_margin()) {
- if (menu_hovered || highlight_arrow > -1) {
+ if (menu_hovered) {
menu_hovered = false;
- highlight_arrow = -1;
update();
}
return;
@@ -209,7 +109,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x <= menu->get_width()) {
if (!menu_hovered) {
menu_hovered = true;
- highlight_arrow = -1;
update();
return;
}
@@ -221,7 +120,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x >= size.width - menu->get_width()) {
if (!menu_hovered) {
menu_hovered = true;
- highlight_arrow = -1;
update();
return;
}
@@ -235,101 +133,26 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
return;
}
}
-
- // Do not activate tabs when tabs is empty.
- if ((get_tab_count() == 0 || !buttons_visible_cache) && menu_hovered) {
- highlight_arrow = -1;
- update();
- return;
- }
-
- int popup_ofs = 0;
- if (popup) {
- popup_ofs = menu->get_width();
- }
-
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
-
- if (is_layout_rtl()) {
- if (pos.x <= popup_ofs + decrement->get_width()) {
- if (highlight_arrow != 1) {
- highlight_arrow = 1;
- update();
- }
- } else if (pos.x <= popup_ofs + increment->get_width() + decrement->get_width()) {
- if (highlight_arrow != 0) {
- highlight_arrow = 0;
- update();
- }
- } else if (highlight_arrow > -1) {
- highlight_arrow = -1;
- update();
- }
- } else {
- if (pos.x >= size.width - increment->get_width() - popup_ofs) {
- if (highlight_arrow != 1) {
- highlight_arrow = 1;
- update();
- }
- } else if (pos.x >= size.width - increment->get_width() - decrement->get_width() - popup_ofs) {
- if (highlight_arrow != 0) {
- highlight_arrow = 0;
- update();
- }
- } else if (highlight_arrow > -1) {
- highlight_arrow = -1;
- update();
- }
- }
}
}
void TabContainer::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_RESIZED: {
- Vector<Control *> tabs = _get_tabs();
- int side_margin = get_theme_constant(SNAME("side_margin"));
- Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
- int header_width = get_size().width - side_margin * 2;
-
- // Find the width of the header area.
- Popup *popup = get_popup();
- if (popup) {
- header_width -= menu->get_width();
- }
- if (buttons_visible_cache) {
- header_width -= increment->get_width() + decrement->get_width();
+ case NOTIFICATION_ENTER_TREE: {
+ // If some nodes happen to be renamed outside the tree, the tab names need to be updated manually.
+ if (get_tab_count() > 0) {
+ _refresh_tab_names();
}
- if (popup || buttons_visible_cache) {
- header_width += side_margin;
- }
-
- // Find the width of all tabs after first_tab_cache.
- int all_tabs_width = 0;
- for (int i = first_tab_cache; i < tabs.size(); i++) {
- int tab_width = _get_tab_width(i);
- all_tabs_width += tab_width;
- }
-
- // Check if tabs before first_tab_cache would fit into the header area.
- for (int i = first_tab_cache - 1; i >= 0; i--) {
- int tab_width = _get_tab_width(i);
-
- if (all_tabs_width + tab_width > header_width) {
- break;
- }
+ } break;
- all_tabs_width += tab_width;
- first_tab_cache--;
- }
+ case NOTIFICATION_READY:
+ case NOTIFICATION_RESIZED: {
+ _update_margins();
} break;
+
case NOTIFICATION_DRAW: {
RID canvas = get_canvas_item();
Size2 size = get_size();
- bool rtl = is_layout_rtl();
// Draw only the tab area if the header is hidden.
Ref<StyleBox> panel = get_theme_stylebox(SNAME("panel"));
@@ -338,494 +161,173 @@ void TabContainer::_notification(int p_what) {
return;
}
- Vector<Control *> tabs = _get_tabs();
- Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
- Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
- Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> increment_hl = get_theme_icon(SNAME("increment_highlight"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
- Ref<Texture2D> decrement_hl = get_theme_icon(SNAME("decrement_highlight"));
- Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
- Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight"));
- Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
- Color font_unselected_color = get_theme_color(SNAME("font_unselected_color"));
- Color font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
- int side_margin = get_theme_constant(SNAME("side_margin"));
-
- // Find out start and width of the header area.
- int header_x = side_margin;
- int header_width = size.width - side_margin * 2;
int header_height = _get_top_margin();
- Popup *popup = get_popup();
- if (popup) {
- header_width -= menu->get_width();
- }
-
- // Check if all tabs would fit into the header area.
- int all_tabs_width = 0;
- for (int i = 0; i < tabs.size(); i++) {
- if (get_tab_hidden(i)) {
- continue;
- }
- int tab_width = _get_tab_width(i);
- all_tabs_width += tab_width;
-
- if (all_tabs_width > header_width) {
- // Not all tabs are visible at the same time - reserve space for navigation buttons.
- buttons_visible_cache = true;
- header_width -= decrement->get_width() + increment->get_width();
- break;
- } else {
- buttons_visible_cache = false;
- }
- }
- // With buttons, a right side margin does not need to be respected.
- if (popup || buttons_visible_cache) {
- header_width += side_margin;
- }
-
- if (!buttons_visible_cache) {
- first_tab_cache = 0;
- }
-
- // Go through the visible tabs to find the width they occupy.
- all_tabs_width = 0;
- Vector<int> tab_widths;
- for (int i = first_tab_cache; i < tabs.size(); i++) {
- if (get_tab_hidden(i)) {
- tab_widths.push_back(0);
- continue;
- }
- int tab_width = _get_tab_width(i);
- if (all_tabs_width + tab_width > header_width && tab_widths.size() > 0) {
- break;
- }
- all_tabs_width += tab_width;
- tab_widths.push_back(tab_width);
- }
-
- // Find the offset at which to draw tabs, according to the alignment.
- switch (align) {
- case ALIGN_LEFT:
- tabs_ofs_cache = header_x;
- break;
- case ALIGN_CENTER:
- tabs_ofs_cache = header_x + (header_width / 2) - (all_tabs_width / 2);
- break;
- case ALIGN_RIGHT:
- tabs_ofs_cache = header_x + header_width - all_tabs_width;
- break;
- }
-
- if (all_tabs_in_front) {
- // Draw the tab area.
- panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
- }
-
- // Draw unselected tabs in back
- int x = 0;
- int x_current = 0;
- int index = 0;
- for (int i = 0; i < tab_widths.size(); i++) {
- index = i + first_tab_cache;
- if (get_tab_hidden(index)) {
- continue;
- }
-
- int tab_width = tab_widths[i];
- if (index == current) {
- x_current = x;
- } else if (get_tab_disabled(index)) {
- if (rtl) {
- _draw_tab(tab_disabled, font_disabled_color, index, size.width - (tabs_ofs_cache + x) - tab_width);
- } else {
- _draw_tab(tab_disabled, font_disabled_color, index, tabs_ofs_cache + x);
- }
- } else {
- if (rtl) {
- _draw_tab(tab_unselected, font_unselected_color, index, size.width - (tabs_ofs_cache + x) - tab_width);
- } else {
- _draw_tab(tab_unselected, font_unselected_color, index, tabs_ofs_cache + x);
- }
- }
-
- x += tab_width;
- last_tab_cache = index;
- }
-
- if (!all_tabs_in_front) {
- // Draw the tab area.
- panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
- }
- // Draw selected tab in front. Only draw selected tab when it's in visible range.
- if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) {
- Ref<StyleBox> current_style_box = get_tab_disabled(current) ? tab_disabled : tab_selected;
- if (rtl) {
- _draw_tab(current_style_box, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]);
- } else {
- _draw_tab(current_style_box, font_selected_color, current, tabs_ofs_cache + x_current);
- }
- }
+ panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
// Draw the popup menu.
- if (rtl) {
- x = 0;
- } else {
- x = get_size().width;
- }
- if (popup) {
- if (!rtl) {
- x -= menu->get_width();
- }
- if (menu_hovered) {
- menu_hl->draw(get_canvas_item(), Size2(x, (header_height - menu_hl->get_height()) / 2));
- } else {
- menu->draw(get_canvas_item(), Size2(x, (header_height - menu->get_height()) / 2));
- }
- if (rtl) {
- x += menu->get_width();
- }
- }
+ if (get_popup()) {
+ Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
+ Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight"));
- // Draw the navigation buttons.
- if (buttons_visible_cache) {
- if (rtl) {
- if (last_tab_cache < tabs.size() - 1) {
- draw_texture(highlight_arrow == 1 ? decrement_hl : decrement, Point2(x, (header_height - increment->get_height()) / 2));
- } else {
- draw_texture(decrement, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5));
- }
- x += increment->get_width();
+ int x = is_layout_rtl() ? 0 : get_size().width - menu->get_width();
- if (first_tab_cache > 0) {
- draw_texture(highlight_arrow == 0 ? increment_hl : increment, Point2(x, (header_height - decrement->get_height()) / 2));
- } else {
- draw_texture(increment, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5));
- }
- x += decrement->get_width();
+ if (menu_hovered) {
+ menu_hl->draw(get_canvas_item(), Point2(x, (header_height - menu_hl->get_height()) / 2));
} else {
- x -= increment->get_width();
- if (last_tab_cache < tabs.size() - 1) {
- draw_texture(highlight_arrow == 1 ? increment_hl : increment, Point2(x, (header_height - increment->get_height()) / 2));
- } else {
- draw_texture(increment, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5));
- }
-
- x -= decrement->get_width();
- if (first_tab_cache > 0) {
- draw_texture(highlight_arrow == 0 ? decrement_hl : decrement, Point2(x, (header_height - decrement->get_height()) / 2));
- } else {
- draw_texture(decrement, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5));
- }
+ menu->draw(get_canvas_item(), Point2(x, (header_height - menu->get_height()) / 2));
}
}
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- text_buf.write[i]->clear();
- }
- _theme_changing = true;
+ theme_changing = true;
call_deferred(SNAME("_on_theme_changed")); // Wait until all changed theme.
} break;
}
}
-void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
- Control *control = get_tab_control(p_index);
- RID canvas = get_canvas_item();
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
- int icon_text_distance = get_theme_constant(SNAME("icon_separation"));
- int tab_width = _get_tab_width(p_index);
- int header_height = _get_top_margin();
-
- // Draw the tab background.
- Rect2 tab_rect(p_x, 0, tab_width, header_height);
- p_tab_style->draw(canvas, tab_rect);
-
- // Draw the tab contents.
- String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name()));
-
- int x_content = tab_rect.position.x + p_tab_style->get_margin(SIDE_LEFT);
- int top_margin = p_tab_style->get_margin(SIDE_TOP);
- int y_center = top_margin + (tab_rect.size.y - p_tab_style->get_minimum_size().y) / 2;
-
- // Draw the tab icon.
- if (control->has_meta("_tab_icon")) {
- Ref<Texture2D> icon = control->get_meta("_tab_icon");
- if (icon.is_valid()) {
- int y = y_center - (icon->get_height() / 2);
- icon->draw(canvas, Point2i(x_content, y));
- if (text != "") {
- x_content += icon->get_width() + icon_text_distance;
- }
- }
- }
-
- // Draw the tab text.
- Point2i text_pos(x_content, y_center - text_buf[p_index]->get_size().y / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- text_buf[p_index]->draw_outline(canvas, text_pos, outline_size, font_outline_color);
- }
- text_buf[p_index]->draw(canvas, text_pos, p_font_color);
-}
-
-void TabContainer::_refresh_texts() {
- text_buf.clear();
- Vector<Control *> tabs = _get_tabs();
- bool rtl = is_layout_rtl();
- Ref<Font> font = get_theme_font(SNAME("font"));
- int font_size = get_theme_font_size(SNAME("font_size"));
- for (int i = 0; i < tabs.size(); i++) {
- Control *control = Object::cast_to<Control>(tabs[i]);
- String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name()));
-
- Ref<TextLine> name;
- name.instantiate();
- name->set_direction(rtl ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
- name->add_string(text, font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
- text_buf.push_back(name);
- }
-}
-
void TabContainer::_on_theme_changed() {
- if (!_theme_changing) {
+ if (!theme_changing) {
return;
}
- _refresh_texts();
-
- minimum_size_changed();
+ tab_bar->add_theme_style_override(SNAME("tab_unselected"), get_theme_stylebox(SNAME("tab_unselected")));
+ tab_bar->add_theme_style_override(SNAME("tab_selected"), get_theme_stylebox(SNAME("tab_selected")));
+ tab_bar->add_theme_style_override(SNAME("tab_disabled"), get_theme_stylebox(SNAME("tab_disabled")));
+ tab_bar->add_theme_icon_override(SNAME("increment"), get_theme_icon(SNAME("increment")));
+ tab_bar->add_theme_icon_override(SNAME("increment_highlight"), get_theme_icon(SNAME("increment_highlight")));
+ tab_bar->add_theme_icon_override(SNAME("decrement"), get_theme_icon(SNAME("decrement")));
+ tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), get_theme_icon(SNAME("decrement_highlight")));
+ tab_bar->add_theme_icon_override(SNAME("drop_mark"), get_theme_icon(SNAME("drop_mark")));
+ tab_bar->add_theme_color_override(SNAME("drop_mark_color"), get_theme_color(SNAME("drop_mark_color")));
+ tab_bar->add_theme_color_override(SNAME("font_selected_color"), get_theme_color(SNAME("font_selected_color")));
+ tab_bar->add_theme_color_override(SNAME("font_unselected_color"), get_theme_color(SNAME("font_unselected_color")));
+ tab_bar->add_theme_color_override(SNAME("font_disabled_color"), get_theme_color(SNAME("font_disabled_color")));
+ tab_bar->add_theme_color_override(SNAME("font_outline_color"), get_theme_color(SNAME("font_outline_color")));
+ tab_bar->add_theme_font_override(SNAME("font"), get_theme_font(SNAME("font")));
+ tab_bar->add_theme_font_size_override(SNAME("font_size"), get_theme_font_size(SNAME("font_size")));
+ tab_bar->add_theme_constant_override(SNAME("h_separation"), get_theme_constant(SNAME("icon_separation")));
+ tab_bar->add_theme_constant_override(SNAME("outline_size"), get_theme_constant(SNAME("outline_size")));
+
+ _update_margins();
if (get_tab_count() > 0) {
_repaint();
- update();
+ } else {
+ update_minimum_size();
}
- _theme_changing = false;
+ update();
+
+ theme_changing = false;
}
void TabContainer::_repaint() {
Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"));
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
+ Vector<Control *> controls = _get_tab_controls();
+ int current = get_current_tab();
+
+ for (int i = 0; i < controls.size(); i++) {
+ Control *c = controls[i];
+
if (i == current) {
c->show();
c->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+
if (tabs_visible) {
c->set_offset(SIDE_TOP, _get_top_margin());
}
+
c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP));
c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT));
c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT));
c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM));
-
} else {
c->hide();
}
}
-}
-void TabContainer::_on_mouse_exited() {
- if (menu_hovered || highlight_arrow > -1) {
- menu_hovered = false;
- highlight_arrow = -1;
- update();
- }
+ update_minimum_size();
}
-int TabContainer::_get_tab_width(int p_index) const {
- ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0);
- Control *control = get_tab_control(p_index);
- if (!control || get_tab_hidden(p_index)) {
- return 0;
- }
-
- // Get the width of the text displayed on the tab.
- Ref<Font> font = get_theme_font(SNAME("font"));
- int font_size = get_theme_font_size(SNAME("font_size"));
- String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name()));
- int width = font->get_string_size(text, font_size).width;
-
- // Add space for a tab icon.
- if (control->has_meta("_tab_icon")) {
- Ref<Texture2D> icon = control->get_meta("_tab_icon");
- if (icon.is_valid()) {
- width += icon->get_width();
- if (text != "") {
- width += get_theme_constant(SNAME("icon_separation"));
- }
- }
- }
-
- // Respect a minimum size.
- Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
- Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
- Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
- if (get_tab_disabled(p_index)) {
- width += tab_disabled->get_minimum_size().width;
- } else if (p_index == current) {
- width += tab_selected->get_minimum_size().width;
- } else {
- width += tab_unselected->get_minimum_size().width;
- }
+void TabContainer::_update_margins() {
+ int menu_width = get_theme_icon(SNAME("menu"))->get_width();
+ int side_margin = get_theme_constant(SNAME("side_margin"));
- return width;
-}
+ // Directly check for validity, to avoid errors when quitting.
+ bool has_popup = popup_obj_id.is_valid();
-Vector<Control *> TabContainer::_get_tabs() const {
- Vector<Control *> controls;
- for (int i = 0; i < get_child_count(); i++) {
- Control *control = Object::cast_to<Control>(get_child(i));
- if (!control || control->is_set_as_top_level()) {
- continue;
- }
-
- controls.push_back(control);
- }
- return controls;
-}
-
-void TabContainer::_child_renamed_callback() {
- _refresh_texts();
- update();
-}
-
-void TabContainer::add_child_notify(Node *p_child) {
- Container::add_child_notify(p_child);
+ if (get_tab_count() == 0) {
+ tab_bar->set_offset(SIDE_LEFT, 0);
+ tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
- Control *c = Object::cast_to<Control>(p_child);
- if (!c || c->is_set_as_top_level()) {
return;
}
- Vector<Control *> tabs = _get_tabs();
- _refresh_texts();
-
- bool first = false;
-
- if (tabs.size() != 1) {
- c->hide();
- } else {
- c->show();
- //call_deferred(SNAME("set_current_tab"),0);
- first = true;
- current = 0;
- previous = 0;
- }
- c->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- if (tabs_visible) {
- c->set_offset(SIDE_TOP, _get_top_margin());
- }
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"));
- c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP));
- c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT));
- c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT));
- c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM));
- update();
- p_child->connect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback));
- if (first && is_inside_tree()) {
- emit_signal(SNAME("tab_changed"), current);
- }
-}
-
-void TabContainer::move_child_notify(Node *p_child) {
- Container::move_child_notify(p_child);
-
- Control *c = Object::cast_to<Control>(p_child);
- if (!c || c->is_set_as_top_level()) {
- return;
- }
+ switch (get_tab_alignment()) {
+ case TabBar::ALIGNMENT_LEFT: {
+ tab_bar->set_offset(SIDE_LEFT, side_margin);
+ tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
+ } break;
- _update_current_tab();
- update();
-}
+ case TabBar::ALIGNMENT_CENTER: {
+ tab_bar->set_offset(SIDE_LEFT, 0);
+ tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
+ } break;
-int TabContainer::get_tab_count() const {
- return _get_tabs().size();
-}
+ case TabBar::ALIGNMENT_RIGHT: {
+ tab_bar->set_offset(SIDE_LEFT, 0);
-void TabContainer::set_current_tab(int p_current) {
- ERR_FAIL_INDEX(p_current, get_tab_count());
+ if (has_popup) {
+ tab_bar->set_offset(SIDE_RIGHT, -menu_width);
+ return;
+ }
- int pending_previous = current;
- current = p_current;
+ int first_tab_pos = tab_bar->get_tab_rect(0).position.x;
+ Rect2 last_tab_rect = tab_bar->get_tab_rect(get_tab_count() - 1);
+ int total_tabs_width = last_tab_rect.position.x - first_tab_pos + last_tab_rect.size.width;
- _repaint();
+ // Calculate if all the tabs would still fit if the margin was present.
+ if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + side_margin) > get_size().width))) {
+ tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
+ } else {
+ tab_bar->set_offset(SIDE_RIGHT, -side_margin);
+ }
+ } break;
- if (pending_previous == current) {
- emit_signal(SNAME("tab_selected"), current);
- } else {
- previous = pending_previous;
- emit_signal(SNAME("tab_selected"), current);
- emit_signal(SNAME("tab_changed"), current);
+ case TabBar::ALIGNMENT_MAX:
+ break; // Can't happen, but silences warning.
}
-
- update();
-}
-
-int TabContainer::get_current_tab() const {
- return current;
}
-int TabContainer::get_previous_tab() const {
- return previous;
-}
-
-Control *TabContainer::get_tab_control(int p_idx) const {
- Vector<Control *> tabs = _get_tabs();
- if (p_idx >= 0 && p_idx < tabs.size()) {
- return tabs[p_idx];
- } else {
- return nullptr;
+void TabContainer::_on_mouse_exited() {
+ if (menu_hovered) {
+ menu_hovered = false;
+ update();
}
}
-Control *TabContainer::get_current_tab_control() const {
- return get_tab_control(current);
-}
-
-void TabContainer::remove_child_notify(Node *p_child) {
- Container::remove_child_notify(p_child);
+Vector<Control *> TabContainer::_get_tab_controls() const {
+ Vector<Control *> controls;
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *control = Object::cast_to<Control>(get_child(i));
+ if (!control || control->is_set_as_top_level() || control == tab_bar) {
+ continue;
+ }
- Control *c = Object::cast_to<Control>(p_child);
- if (!c || c->is_set_as_top_level()) {
- return;
+ controls.push_back(control);
}
- // Defer the call because tab is not yet removed (remove_child_notify is called right before p_child is actually removed).
- call_deferred(SNAME("_update_current_tab"));
-
- p_child->disconnect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback));
-
- update();
-}
-
-void TabContainer::_update_current_tab() {
- _refresh_texts();
-
- int tc = get_tab_count();
- if (current >= tc) {
- current = tc - 1;
- }
- if (current < 0) {
- current = 0;
- } else {
- set_current_tab(current);
- }
+ return controls;
}
-Variant TabContainer::get_drag_data(const Point2 &p_point) {
+Variant TabContainer::_get_drag_data_fw(const Point2 &p_point, Control *p_from_control) {
if (!drag_to_rearrange_enabled) {
return Variant();
}
int tab_over = get_tab_idx_at_point(p_point);
-
if (tab_over < 0) {
return Variant();
}
@@ -838,18 +340,20 @@ Variant TabContainer::get_drag_data(const Point2 &p_point) {
tf->set_texture(icon);
drag_preview->add_child(tf);
}
+
Label *label = memnew(Label(get_tab_title(tab_over)));
- drag_preview->add_child(label);
set_drag_preview(drag_preview);
+ drag_preview->add_child(label);
Dictionary drag_data;
drag_data["type"] = "tabc_element";
drag_data["tabc_element"] = tab_over;
drag_data["from_path"] = get_path();
+
return drag_data;
}
-bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+bool TabContainer::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const {
if (!drag_to_rearrange_enabled) {
return false;
}
@@ -865,7 +369,7 @@ bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) c
if (from_path == to_path) {
return true;
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between other TabContainers
+ // Drag and drop between other TabContainers.
Node *from_node = get_node(from_path);
TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
@@ -873,16 +377,15 @@ bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) c
}
}
}
+
return false;
}
-void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
+void TabContainer::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) {
if (!drag_to_rearrange_enabled) {
return;
}
- int hover_now = get_tab_idx_at_point(p_point);
-
Dictionary d = p_data;
if (!d.has("type")) {
return;
@@ -890,91 +393,240 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
if (String(d["type"]) == "tabc_element") {
int tab_from_id = d["tabc_element"];
+ int hover_now = get_tab_idx_at_point(p_point);
NodePath from_path = d["from_path"];
NodePath to_path = get_path();
+
if (from_path == to_path) {
- if (hover_now < 0) {
- hover_now = get_tab_count() - 1;
+ if (tab_from_id == hover_now) {
+ return;
+ }
+
+ // Drop the new tab to the left or right depending on where the target tab is being hovered.
+ if (hover_now != -1) {
+ Rect2 tab_rect = tab_bar->get_tab_rect(hover_now);
+ if (is_layout_rtl() ^ (p_point.x <= tab_rect.position.x + tab_rect.size.width / 2)) {
+ if (hover_now > tab_from_id) {
+ hover_now -= 1;
+ }
+ } else if (tab_from_id > hover_now) {
+ hover_now += 1;
+ }
+ } else {
+ hover_now = is_layout_rtl() ^ (p_point.x < tab_bar->get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1;
+ }
+
+ move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index(false));
+ if (!is_tab_disabled(hover_now)) {
+ set_current_tab(hover_now);
}
- move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index());
- set_current_tab(hover_now);
+
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between TabContainers
+ // Drag and drop between TabContainers.
+
Node *from_node = get_node(from_path);
TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+
if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ // Get the tab properties before they get erased by the child removal.
+ String tab_title = from_tabc->get_tab_title(tab_from_id);
+ bool tab_disabled = from_tabc->is_tab_disabled(tab_from_id);
+
+ // Drop the new tab to the left or right depending on where the target tab is being hovered.
+ if (hover_now != -1) {
+ Rect2 tab_rect = tab_bar->get_tab_rect(hover_now);
+ if (is_layout_rtl() ^ (p_point.x > tab_rect.position.x + tab_rect.size.width / 2)) {
+ hover_now += 1;
+ }
+ } else {
+ hover_now = is_layout_rtl() ^ (p_point.x < tab_bar->get_tab_rect(0).position.x) ? 0 : get_tab_count();
+ }
+
Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
from_tabc->remove_child(moving_tabc);
- add_child(moving_tabc, false, INTERNAL_MODE_FRONT);
- if (hover_now < 0) {
- hover_now = get_tab_count() - 1;
+ add_child(moving_tabc, true);
+
+ set_tab_title(get_tab_count() - 1, tab_title);
+ set_tab_disabled(get_tab_count() - 1, tab_disabled);
+
+ move_child(moving_tabc, get_tab_control(hover_now)->get_index(false));
+ if (!is_tab_disabled(hover_now)) {
+ set_current_tab(hover_now);
}
- move_child(moving_tabc, get_tab_control(hover_now)->get_index());
- set_current_tab(hover_now);
- emit_signal(SNAME("tab_changed"), hover_now);
}
}
}
- update();
}
-int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
- if (get_tab_count() == 0) {
- return -1;
+void TabContainer::_on_tab_changed(int p_tab) {
+ call_deferred(SNAME("_repaint"));
+
+ emit_signal(SNAME("tab_changed"), p_tab);
+}
+
+void TabContainer::_on_tab_selected(int p_tab) {
+ if (p_tab != get_previous_tab()) {
+ call_deferred(SNAME("_repaint"));
+ }
+
+ emit_signal(SNAME("tab_selected"), p_tab);
+}
+
+void TabContainer::_on_tab_button_pressed(int p_tab) {
+ emit_signal(SNAME("tab_button_pressed"), p_tab);
+}
+
+void TabContainer::_refresh_tab_names() {
+ Vector<Control *> controls = _get_tab_controls();
+ for (int i = 0; i < controls.size(); i++) {
+ if (!controls[i]->has_meta("_tab_name") && String(controls[i]->get_name()) != get_tab_title(i)) {
+ tab_bar->set_tab_title(i, controls[i]->get_name());
+ }
}
+}
- // must be on tabs in the tab header area.
- if (p_point.y > _get_top_margin()) {
- return -1;
+void TabContainer::add_child_notify(Node *p_child) {
+ if (p_child == tab_bar) {
+ return;
}
- Size2 size = get_size();
- int button_ofs = 0;
- int px = p_point.x;
+ Container::add_child_notify(p_child);
- if (is_layout_rtl()) {
- px = size.width - px;
+ Control *c = Object::cast_to<Control>(p_child);
+ if (!c || c->is_set_as_top_level()) {
+ return;
}
+ c->hide();
- if (px < tabs_ofs_cache) {
- return -1;
+ tab_bar->add_tab(p_child->get_name());
+
+ _update_margins();
+ if (get_tab_count() == 1) {
+ update();
}
- Popup *popup = get_popup();
- if (popup) {
- Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
- button_ofs += menu->get_width();
+ p_child->connect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names));
+
+ // TabBar won't emit the "tab_changed" signal when not inside the tree.
+ if (!is_inside_tree()) {
+ call_deferred("_repaint");
}
- if (buttons_visible_cache) {
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
- button_ofs += increment->get_width() + decrement->get_width();
+}
+
+void TabContainer::move_child_notify(Node *p_child) {
+ if (p_child == tab_bar) {
+ return;
}
- if (px > size.width - button_ofs) {
- return -1;
+
+ Container::move_child_notify(p_child);
+
+ Control *c = Object::cast_to<Control>(p_child);
+ if (c && !c->is_set_as_top_level()) {
+ int old_idx = -1;
+ String tab_name = String(c->get_meta("_tab_name", c->get_name()));
+
+ // Find the previous tab index of the control.
+ for (int i = 0; i < get_tab_count(); i++) {
+ if (get_tab_title(i) == tab_name) {
+ old_idx = i;
+ break;
+ }
+ }
+
+ tab_bar->move_tab(old_idx, get_tab_idx_from_control(c));
}
+}
- // get the tab at the point
- Vector<Control *> tabs = _get_tabs();
- px -= tabs_ofs_cache;
- for (int i = first_tab_cache; i <= last_tab_cache; i++) {
- int tab_width = _get_tab_width(i);
- if (px < tab_width) {
+void TabContainer::remove_child_notify(Node *p_child) {
+ if (p_child == tab_bar) {
+ return;
+ }
+
+ Container::remove_child_notify(p_child);
+
+ Control *c = Object::cast_to<Control>(p_child);
+ if (!c || c->is_set_as_top_level()) {
+ return;
+ }
+
+ tab_bar->remove_tab(get_tab_idx_from_control(c));
+
+ _update_margins();
+ if (get_tab_count() == 0) {
+ update();
+ }
+
+ p_child->remove_meta("_tab_name");
+ p_child->disconnect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names));
+
+ // TabBar won't emit the "tab_changed" signal when not inside the tree.
+ if (!is_inside_tree()) {
+ call_deferred("_repaint");
+ }
+}
+
+int TabContainer::get_tab_count() const {
+ return tab_bar->get_tab_count();
+}
+
+void TabContainer::set_current_tab(int p_current) {
+ tab_bar->set_current_tab(p_current);
+}
+
+int TabContainer::get_current_tab() const {
+ return tab_bar->get_current_tab();
+}
+
+int TabContainer::get_previous_tab() const {
+ return tab_bar->get_previous_tab();
+}
+
+Control *TabContainer::get_tab_control(int p_idx) const {
+ Vector<Control *> controls = _get_tab_controls();
+ if (p_idx >= 0 && p_idx < controls.size()) {
+ return controls[p_idx];
+ } else {
+ return nullptr;
+ }
+}
+
+Control *TabContainer::get_current_tab_control() const {
+ return get_tab_control(tab_bar->get_current_tab());
+}
+
+int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
+ return tab_bar->get_tab_idx_at_point(p_point);
+}
+
+int TabContainer::get_tab_idx_from_control(Control *p_child) const {
+ ERR_FAIL_NULL_V(p_child, -1);
+ ERR_FAIL_COND_V(p_child->get_parent() != this, -1);
+
+ Vector<Control *> controls = _get_tab_controls();
+ for (int i = 0; i < controls.size(); i++) {
+ if (controls[i] == p_child) {
return i;
}
- px -= tab_width;
}
+
return -1;
}
-void TabContainer::set_tab_align(TabAlign p_align) {
- ERR_FAIL_INDEX(p_align, 3);
- align = p_align;
- update();
+void TabContainer::set_tab_alignment(TabBar::AlignmentMode p_alignment) {
+ tab_bar->set_tab_alignment(p_alignment);
+ _update_margins();
+}
+
+TabBar::AlignmentMode TabContainer::get_tab_alignment() const {
+ return tab_bar->get_tab_alignment();
}
-TabContainer::TabAlign TabContainer::get_tab_align() const {
- return align;
+void TabContainer::set_clip_tabs(bool p_clip_tabs) {
+ tab_bar->set_clip_tabs(p_clip_tabs);
+}
+
+bool TabContainer::get_clip_tabs() const {
+ return tab_bar->get_clip_tabs();
}
void TabContainer::set_tabs_visible(bool p_visible) {
@@ -983,11 +635,12 @@ void TabContainer::set_tabs_visible(bool p_visible) {
}
tabs_visible = p_visible;
+ tab_bar->set_visible(tabs_visible);
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
- if (p_visible) {
+ Vector<Control *> controls = _get_tab_controls();
+ for (int i = 0; i < controls.size(); i++) {
+ Control *c = controls[i];
+ if (tabs_visible) {
c->set_offset(SIDE_TOP, _get_top_margin());
} else {
c->set_offset(SIDE_TOP, 0);
@@ -995,7 +648,7 @@ void TabContainer::set_tabs_visible(bool p_visible) {
}
update();
- minimum_size_changed();
+ update_minimum_size();
}
bool TabContainer::are_tabs_visible() const {
@@ -1009,7 +662,8 @@ void TabContainer::set_all_tabs_in_front(bool p_in_front) {
all_tabs_in_front = p_in_front;
- update();
+ remove_child(tab_bar);
+ add_child(tab_bar, false, all_tabs_in_front ? INTERNAL_MODE_FRONT : INTERNAL_MODE_BACK);
}
bool TabContainer::is_all_tabs_in_front() const {
@@ -1019,96 +673,88 @@ bool TabContainer::is_all_tabs_in_front() const {
void TabContainer::set_tab_title(int p_tab, const String &p_title) {
Control *child = get_tab_control(p_tab);
ERR_FAIL_COND(!child);
- child->set_meta("_tab_name", p_title);
- _refresh_texts();
- update();
-}
-String TabContainer::get_tab_title(int p_tab) const {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND_V(!child, "");
- if (child->has_meta("_tab_name")) {
- return child->get_meta("_tab_name");
+ tab_bar->set_tab_title(p_tab, p_title);
+
+ if (p_title == child->get_name()) {
+ child->remove_meta("_tab_name");
} else {
- return child->get_name();
+ child->set_meta("_tab_name", p_title);
+ }
+
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
}
}
+String TabContainer::get_tab_title(int p_tab) const {
+ return tab_bar->get_tab_title(p_tab);
+}
+
void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND(!child);
- child->set_meta("_tab_icon", p_icon);
- update();
+ tab_bar->set_tab_icon(p_tab, p_icon);
+
+ _update_margins();
+ _repaint();
}
Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND_V(!child, Ref<Texture2D>());
- if (child->has_meta("_tab_icon")) {
- return child->get_meta("_tab_icon");
- } else {
- return Ref<Texture2D>();
- }
+ return tab_bar->get_tab_icon(p_tab);
}
void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND(!child);
- child->set_meta("_tab_disabled", p_disabled);
- update();
-}
+ tab_bar->set_tab_disabled(p_tab, p_disabled);
-bool TabContainer::get_tab_disabled(int p_tab) const {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND_V(!child, false);
- if (child->has_meta("_tab_disabled")) {
- return child->get_meta("_tab_disabled");
- } else {
- return false;
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
}
}
+bool TabContainer::is_tab_disabled(int p_tab) const {
+ return tab_bar->is_tab_disabled(p_tab);
+}
+
void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
Control *child = get_tab_control(p_tab);
ERR_FAIL_COND(!child);
- child->set_meta("_tab_hidden", p_hidden);
- update();
- for (int i = 0; i < get_tab_count(); i++) {
- int try_tab = (p_tab + 1 + i) % get_tab_count();
- if (get_tab_disabled(try_tab) || get_tab_hidden(try_tab)) {
- continue;
- }
- set_current_tab(try_tab);
- return;
+ tab_bar->set_tab_hidden(p_tab, p_hidden);
+ child->hide();
+
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
}
+}
- //assumed no other tab can be switched to, just hide
- child->hide();
+bool TabContainer::is_tab_hidden(int p_tab) const {
+ return tab_bar->is_tab_hidden(p_tab);
}
-bool TabContainer::get_tab_hidden(int p_tab) const {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND_V(!child, false);
- if (child->has_meta("_tab_hidden")) {
- return child->get_meta("_tab_hidden");
- } else {
- return false;
- }
+void TabContainer::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
+ tab_bar->set_tab_button_icon(p_tab, p_icon);
+
+ _update_margins();
+ _repaint();
+}
+
+Ref<Texture2D> TabContainer::get_tab_button_icon(int p_tab) const {
+ return tab_bar->get_tab_button_icon(p_tab);
}
void TabContainer::get_translatable_strings(List<String> *p_strings) const {
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
+ Vector<Control *> controls = _get_tab_controls();
+ for (int i = 0; i < controls.size(); i++) {
+ Control *c = controls[i];
if (!c->has_meta("_tab_name")) {
continue;
}
String name = c->get_meta("_tab_name");
-
- if (name != "") {
+ if (!name.is_empty()) {
p_strings->push_back(name);
}
}
@@ -1117,9 +763,26 @@ void TabContainer::get_translatable_strings(List<String> *p_strings) const {
Size2 TabContainer::get_minimum_size() const {
Size2 ms;
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
+ if (tabs_visible) {
+ ms = tab_bar->get_minimum_size();
+
+ if (!get_clip_tabs()) {
+ if (get_popup()) {
+ ms.x += get_theme_icon(SNAME("menu"))->get_width();
+ }
+
+ int side_margin = get_theme_constant(SNAME("side_margin"));
+ if (side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER &&
+ (get_tab_alignment() != TabBar::ALIGNMENT_RIGHT || !get_popup())) {
+ ms.x += side_margin;
+ }
+ }
+ }
+
+ Vector<Control *> controls = _get_tab_controls();
+ int max_control_height = 0;
+ for (int i = 0; i < controls.size(); i++) {
+ Control *c = controls[i];
if (!c->is_visible_in_tree() && !use_hidden_tabs_for_min_size) {
continue;
@@ -1127,29 +790,30 @@ Size2 TabContainer::get_minimum_size() const {
Size2 cms = c->get_combined_minimum_size();
ms.x = MAX(ms.x, cms.x);
- ms.y = MAX(ms.y, cms.y);
+ max_control_height = MAX(max_control_height, cms.y);
}
+ ms.y += max_control_height;
- Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
- Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
- Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
-
- if (tabs_visible) {
- ms.y += MAX(MAX(tab_unselected->get_minimum_size().y, tab_selected->get_minimum_size().y), tab_disabled->get_minimum_size().y);
- ms.y += _get_top_margin();
- }
-
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"));
- ms += sb->get_minimum_size();
+ Size2 panel_ms = get_theme_stylebox(SNAME("panel"))->get_minimum_size();
+ ms.x = MAX(ms.x, panel_ms.x);
+ ms.y += panel_ms.y;
return ms;
}
void TabContainer::set_popup(Node *p_popup) {
- ERR_FAIL_NULL(p_popup);
+ bool had_popup = get_popup();
+
Popup *popup = Object::cast_to<Popup>(p_popup);
popup_obj_id = popup ? popup->get_instance_id() : ObjectID();
- update();
+
+ if (had_popup != bool(popup)) {
+ update();
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
+ }
+ }
}
Popup *TabContainer::get_popup() const {
@@ -1164,6 +828,7 @@ Popup *TabContainer::get_popup() const {
popup_obj_id = ObjectID();
}
}
+
return nullptr;
}
@@ -1176,21 +841,30 @@ bool TabContainer::get_drag_to_rearrange_enabled() const {
}
void TabContainer::set_tabs_rearrange_group(int p_group_id) {
- tabs_rearrange_group = p_group_id;
+ tab_bar->set_tabs_rearrange_group(p_group_id);
}
int TabContainer::get_tabs_rearrange_group() const {
- return tabs_rearrange_group;
+ return tab_bar->get_tabs_rearrange_group();
}
void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) {
use_hidden_tabs_for_min_size = p_use_hidden_tabs;
+ update_minimum_size();
}
bool TabContainer::get_use_hidden_tabs_for_min_size() const {
return use_hidden_tabs_for_min_size;
}
+Vector<int> TabContainer::get_allowed_size_flags_horizontal() const {
+ return Vector<int>();
+}
+
+Vector<int> TabContainer::get_allowed_size_flags_vertical() const {
+ return Vector<int>();
+}
+
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_count"), &TabContainer::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabContainer::set_current_tab);
@@ -1198,8 +872,10 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_previous_tab"), &TabContainer::get_previous_tab);
ClassDB::bind_method(D_METHOD("get_current_tab_control"), &TabContainer::get_current_tab_control);
ClassDB::bind_method(D_METHOD("get_tab_control", "tab_idx"), &TabContainer::get_tab_control);
- ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &TabContainer::set_tab_align);
- ClassDB::bind_method(D_METHOD("get_tab_align"), &TabContainer::get_tab_align);
+ ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabContainer::set_tab_alignment);
+ ClassDB::bind_method(D_METHOD("get_tab_alignment"), &TabContainer::get_tab_alignment);
+ ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabContainer::set_clip_tabs);
+ ClassDB::bind_method(D_METHOD("get_clip_tabs"), &TabContainer::get_clip_tabs);
ClassDB::bind_method(D_METHOD("set_tabs_visible", "visible"), &TabContainer::set_tabs_visible);
ClassDB::bind_method(D_METHOD("are_tabs_visible"), &TabContainer::are_tabs_visible);
ClassDB::bind_method(D_METHOD("set_all_tabs_in_front", "is_front"), &TabContainer::set_all_tabs_in_front);
@@ -1209,39 +885,51 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon);
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled);
- ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled);
+ ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabContainer::is_tab_disabled);
ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden);
- ClassDB::bind_method(D_METHOD("get_tab_hidden", "tab_idx"), &TabContainer::get_tab_hidden);
+ ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabContainer::is_tab_hidden);
+ ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabContainer::set_tab_button_icon);
+ ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabContainer::get_tab_button_icon);
ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point);
+ ClassDB::bind_method(D_METHOD("get_tab_idx_from_control", "control"), &TabContainer::get_tab_idx_from_control);
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group);
-
ClassDB::bind_method(D_METHOD("set_use_hidden_tabs_for_min_size", "enabled"), &TabContainer::set_use_hidden_tabs_for_min_size);
ClassDB::bind_method(D_METHOD("get_use_hidden_tabs_for_min_size"), &TabContainer::get_use_hidden_tabs_for_min_size);
+ ClassDB::bind_method(D_METHOD("_repaint"), &TabContainer::_repaint);
ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
- ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab);
+ ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &TabContainer::_get_drag_data_fw);
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TabContainer::_can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TabContainer::_drop_data_fw);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("pre_popup_pressed"));
- ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "all_tabs_in_front"), "set_all_tabs_in_front", "is_all_tabs_in_front");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tabs_rearrange_group"), "set_tabs_rearrange_group", "get_tabs_rearrange_group");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size");
-
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
}
TabContainer::TabContainer() {
+ tab_bar = memnew(TabBar);
+ tab_bar->set_drag_forwarding(this);
+ add_child(tab_bar, false, INTERNAL_MODE_FRONT);
+ tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE);
+ tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed));
+ tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected));
+ tab_bar->connect("tab_button_pressed", callable_mp(this, &TabContainer::_on_tab_button_pressed));
+
connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited));
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index fe96df25e8..9adaa0d844 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,65 +33,52 @@
#include "scene/gui/container.h"
#include "scene/gui/popup.h"
-#include "scene/resources/text_line.h"
+#include "scene/gui/tab_bar.h"
class TabContainer : public Container {
GDCLASS(TabContainer, Container);
-public:
- enum TabAlign {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT
- };
-
-private:
- int first_tab_cache = 0;
- int tabs_ofs_cache = 0;
- int last_tab_cache = 0;
- int current = 0;
- int previous = 0;
+ TabBar *tab_bar = nullptr;
bool tabs_visible = true;
bool all_tabs_in_front = false;
- bool buttons_visible_cache = false;
bool menu_hovered = false;
- int highlight_arrow = -1;
- TabAlign align = ALIGN_CENTER;
- int _get_top_margin() const;
mutable ObjectID popup_obj_id;
bool drag_to_rearrange_enabled = false;
bool use_hidden_tabs_for_min_size = false;
- int tabs_rearrange_group = -1;
+ bool theme_changing = false;
- Vector<Ref<TextLine>> text_buf;
- Vector<Control *> _get_tabs() const;
- int _get_tab_width(int p_index) const;
- bool _theme_changing = false;
+ int _get_top_margin() const;
+ Vector<Control *> _get_tab_controls() const;
void _on_theme_changed();
void _repaint();
+ void _refresh_tab_names();
+ void _update_margins();
void _on_mouse_exited();
- void _update_current_tab();
- void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x);
- void _refresh_texts();
+ void _on_tab_changed(int p_tab);
+ void _on_tab_selected(int p_tab);
+ void _on_tab_button_pressed(int p_tab);
+
+ Variant _get_drag_data_fw(const Point2 &p_point, Control *p_from_control);
+ bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const;
+ void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control);
protected:
- void _child_renamed_callback();
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _notification(int p_what);
virtual void add_child_notify(Node *p_child) override;
virtual void move_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
+ static void _bind_methods();
- Variant get_drag_data(const Point2 &p_point) override;
- bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
- void drop_data(const Point2 &p_point, const Variant &p_data) override;
+public:
int get_tab_idx_at_point(const Point2 &p_point) const;
+ int get_tab_idx_from_control(Control *p_child) const;
- static void _bind_methods();
+ void set_tab_alignment(TabBar::AlignmentMode p_alignment);
+ TabBar::AlignmentMode get_tab_alignment() const;
-public:
- void set_tab_align(TabAlign p_align);
- TabAlign get_tab_align() const;
+ void set_clip_tabs(bool p_clip_tabs);
+ bool get_clip_tabs() const;
void set_tabs_visible(bool p_visible);
bool are_tabs_visible() const;
@@ -106,10 +93,13 @@ public:
Ref<Texture2D> get_tab_icon(int p_tab) const;
void set_tab_disabled(int p_tab, bool p_disabled);
- bool get_tab_disabled(int p_tab) const;
+ bool is_tab_disabled(int p_tab) const;
void set_tab_hidden(int p_tab, bool p_hidden);
- bool get_tab_hidden(int p_tab) const;
+ bool is_tab_hidden(int p_tab) const;
+
+ void set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon);
+ Ref<Texture2D> get_tab_button_icon(int p_tab) const;
int get_tab_count() const;
void set_current_tab(int p_current);
@@ -133,9 +123,10 @@ public:
void set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs);
bool get_use_hidden_tabs_for_min_size() const;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
TabContainer();
};
-VARIANT_ENUM_CAST(TabContainer::TabAlign);
-
#endif // TAB_CONTAINER_H
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 74268707ff..315ffbd419 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,21 +39,10 @@
#include "core/os/os.h"
#include "core/string/string_builder.h"
#include "core/string/translation.h"
+#include "label.h"
#include "scene/main/window.h"
-static bool _is_text_char(char32_t c) {
- return !is_symbol(c);
-}
-
-static bool _is_whitespace(char32_t c) {
- return c == '\t' || c == ' ';
-}
-
-static bool _is_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-
///////////////////////////////////////////////////////////////////////////////
/// TEXT ///
///////////////////////////////////////////////////////////////////////////////
@@ -127,6 +116,10 @@ void TextEdit::Text::set_width(float p_width) {
width = p_width;
}
+float TextEdit::Text::get_width() const {
+ return width;
+}
+
int TextEdit::Text::get_line_wrap_amount(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
@@ -154,57 +147,72 @@ _FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const {
void TextEdit::Text::_calculate_line_height() {
int height = 0;
- for (int i = 0; i < text.size(); i++) {
+ for (const Line &l : text) {
// Found another line with the same height...nothing to update.
- if (text[i].height == line_height) {
+ if (l.height == line_height) {
height = line_height;
break;
}
- height = MAX(height, text[i].height);
+ height = MAX(height, l.height);
}
line_height = height;
}
void TextEdit::Text::_calculate_max_line_width() {
int width = 0;
- for (int i = 0; i < text.size(); i++) {
- if (is_hidden(i)) {
+ for (const Line &l : text) {
+ if (l.hidden) {
continue;
}
// Found another line with the same width...nothing to update.
- if (text[i].width == max_width) {
+ if (l.width == max_width) {
width = max_width;
break;
}
- width = MAX(width, text[i].width);
+ width = MAX(width, l.width);
}
max_width = width;
}
-void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Array &p_bidi_override) {
+void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_changed, const String &p_ime_text, const Array &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
if (font.is_null() || font_size <= 0) {
return; // Not in tree?
}
- text.write[p_line].data_buf->clear();
+ if (p_text_changed) {
+ text.write[p_line].data_buf->clear();
+ }
+
text.write[p_line].data_buf->set_width(width);
text.write[p_line].data_buf->set_direction((TextServer::Direction)direction);
text.write[p_line].data_buf->set_preserve_control(draw_control_chars);
if (p_ime_text.length() > 0) {
- text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
+ if (p_text_changed) {
+ text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
+ }
if (!p_bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), p_bidi_override);
}
} else {
- text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
+ if (p_text_changed) {
+ text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
+ }
if (!text[p_line].bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override);
}
}
+ if (!p_text_changed) {
+ RID r = text.write[p_line].data_buf->get_rid();
+ int spans = TS->shaped_get_span_count(r);
+ for (int i = 0; i < spans; i++) {
+ TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, opentype_features);
+ }
+ }
+
// Apply tab align.
if (tab_size > 0) {
Vector<float> tabs;
@@ -215,7 +223,7 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_
// Update height.
const int old_height = text.write[p_line].height;
const int wrap_amount = get_line_wrap_amount(p_line);
- int height = font->get_height(font_size);
+ int height = font_height;
for (int i = 0; i <= wrap_amount; i++) {
height = MAX(height, text[p_line].data_buf->get_line_size(i).y);
}
@@ -261,20 +269,53 @@ void TextEdit::Text::invalidate_all_lines() {
}
}
+void TextEdit::Text::invalidate_font() {
+ if (!is_dirty) {
+ return;
+ }
+
+ max_width = -1;
+ line_height = -1;
+
+ if (!font.is_null() && font_size > 0) {
+ font_height = font->get_height(font_size);
+ }
+
+ for (int i = 0; i < text.size(); i++) {
+ invalidate_cache(i, -1, false);
+ }
+ is_dirty = false;
+}
+
void TextEdit::Text::invalidate_all() {
if (!is_dirty) {
return;
}
+ max_width = -1;
+ line_height = -1;
+
+ if (!font.is_null() && font_size > 0) {
+ font_height = font->get_height(font_size);
+ }
+
for (int i = 0; i < text.size(); i++) {
- invalidate_cache(i);
+ invalidate_cache(i, -1, true);
}
is_dirty = false;
}
void TextEdit::Text::clear() {
text.clear();
- insert(0, "", Array());
+
+ max_width = -1;
+ line_height = -1;
+
+ Line line;
+ line.gutters.resize(gutter_count);
+ line.data = "";
+ text.insert(0, line);
+ invalidate_cache(0, -1, true);
}
int TextEdit::Text::get_max_width() const {
@@ -286,33 +327,67 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_o
text.write[p_line].data = p_text;
text.write[p_line].bidi_override = p_bidi_override;
- invalidate_cache(p_line);
+ invalidate_cache(p_line, -1, true);
}
-void TextEdit::Text::insert(int p_at, const String &p_text, const Array &p_bidi_override) {
- Line line;
- line.gutters.resize(gutter_count);
- line.hidden = false;
- line.data = p_text;
- line.bidi_override = p_bidi_override;
- text.insert(p_at, line);
+void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override) {
+ int new_line_count = p_text.size() - 1;
+ if (new_line_count > 0) {
+ text.resize(text.size() + new_line_count);
+ for (int i = (text.size() - 1); i > p_at; i--) {
+ if ((i - new_line_count) <= 0) {
+ break;
+ }
+ text.write[i] = text[i - new_line_count];
+ }
+ }
- invalidate_cache(p_at);
+ for (int i = 0; i < p_text.size(); i++) {
+ if (i == 0) {
+ set(p_at + i, p_text[i], p_bidi_override[i]);
+ continue;
+ }
+ Line line;
+ line.gutters.resize(gutter_count);
+ line.data = p_text[i];
+ line.bidi_override = p_bidi_override[i];
+ text.write[p_at + i] = line;
+ invalidate_cache(p_at + i, -1, true);
+ }
}
-void TextEdit::Text::remove(int p_at) {
- int height = text[p_at].height;
- int width = text[p_at].width;
+void TextEdit::Text::remove_range(int p_from_line, int p_to_line) {
+ if (p_from_line == p_to_line) {
+ return;
+ }
+
+ bool dirty_height = false;
+ bool dirty_width = false;
+ for (int i = p_from_line; i < p_to_line; i++) {
+ if (!dirty_height && text[i].height == line_height) {
+ dirty_height = true;
+ }
+
+ if (!dirty_width && text[i].width == max_width) {
+ dirty_width = true;
+ }
+
+ if (dirty_height && dirty_width) {
+ break;
+ }
+ }
- text.remove(p_at);
+ int diff = (p_to_line - p_from_line);
+ for (int i = p_to_line; i < text.size() - 1; i++) {
+ text.write[(i - diff) + 1] = text[i + 1];
+ }
+ text.resize(text.size() - diff);
- // If this is the tallest line, we need to get the next tallest.
- if (height == line_height) {
+ if (dirty_height) {
_calculate_line_height();
}
- // If this is the longest line, we need to get the next longest.
- if (width == max_width) {
+ if (dirty_width) {
_calculate_max_line_width();
}
}
@@ -330,7 +405,7 @@ void TextEdit::Text::add_gutter(int p_at) {
void TextEdit::Text::remove_gutter(int p_gutter) {
for (int i = 0; i < text.size(); i++) {
- text.write[i].gutters.remove(p_gutter);
+ text.write[i].gutters.remove_at(p_gutter);
}
gutter_count--;
}
@@ -357,32 +432,38 @@ void TextEdit::_notification(int p_what) {
}
_update_wrap_at_column(true);
} break;
+
case NOTIFICATION_RESIZED: {
_update_scrollbars();
_update_wrap_at_column();
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
call_deferred(SNAME("_update_scrollbars"));
call_deferred(SNAME("_update_wrap_at_column"));
}
} break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
_update_caches();
_update_wrap_at_column(true);
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
window_has_focus = true;
draw_caret = true;
update();
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
window_has_focus = false;
draw_caret = false;
update();
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (scrolling && get_v_scroll() != target_v_scroll) {
double target_y = target_v_scroll - get_v_scroll();
@@ -390,6 +471,11 @@ void TextEdit::_notification(int p_what) {
// To ensure minimap is responsive override the speed setting.
double vel = ((target_y / dist) * ((minimap_clicked) ? 3000 : v_scroll_speed)) * get_physics_process_delta_time();
+ // Prevent too small velocity to block scrolling
+ if (Math::abs(vel) < v_scroll->get_step()) {
+ vel = v_scroll->get_step() * SIGN(vel);
+ }
+
if (Math::abs(vel) >= dist) {
set_v_scroll(target_v_scroll);
scrolling = false;
@@ -404,6 +490,7 @@ void TextEdit::_notification(int p_what) {
set_physics_process_internal(false);
}
} break;
+
case NOTIFICATION_DRAW: {
if (first_draw) {
// Size may not be the final one, so attempts to ensure caret was visible may have failed.
@@ -596,19 +683,21 @@ void TextEdit::_notification(int p_what) {
}
}
+ bool draw_placeholder = text.size() == 1 && text[0].length() == 0;
+
// Get the highlighted words.
String highlighted_text = get_selected_text();
// Check if highlighted words contain only whitespaces (tabs or spaces).
- bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String();
+ bool only_whitespaces_highlighted = highlighted_text.strip_edges().is_empty();
const int caret_wrap_index = get_caret_wrap_index();
int first_visible_line = get_first_visible_line() - 1;
int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
- draw_amount += get_line_wrap_count(first_visible_line + 1);
+ draw_amount += draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_line_wrap_count(first_visible_line + 1);
- // minimap
+ // Draw minimap.
if (draw_minimap) {
int minimap_visible_lines = get_minimap_visible_lines();
int minimap_line_height = (minimap_char_size.y + minimap_line_spacing);
@@ -731,8 +820,8 @@ void TextEdit::_notification(int p_what) {
int xpos = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * j)) + tabs;
bool out_of_bounds = (xpos >= xmargin_end + minimap_width);
- bool is_whitespace = _is_whitespace(str[j]);
- if (!is_whitespace) {
+ bool whitespace = is_whitespace(str[j]);
+ if (!whitespace) {
characters++;
if (j < str.length() - 1 && color == previous_color && !out_of_bounds) {
@@ -754,7 +843,7 @@ void TextEdit::_notification(int p_what) {
if (characters > 0) {
previous_color.a *= 0.6;
// take one for zero indexing, and if we hit whitespace / the end of a word.
- int chars = MAX(0, (j - (characters - 1)) - (is_whitespace ? 1 : 0)) + 1;
+ int chars = MAX(0, (j - (characters - 1)) - (whitespace ? 1 : 0)) + 1;
int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs;
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - char_x_ofs - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
@@ -788,9 +877,10 @@ void TextEdit::_notification(int p_what) {
bottom_limit_y -= style_normal->get_margin(SIDE_BOTTOM);
}
- // draw main text
+ // Draw main text.
caret.visible = false;
- int row_height = get_line_height();
+ line_drawing_cache.clear();
+ int row_height = draw_placeholder ? placeholder_line_height + line_spacing : get_line_height();
int line = first_visible_line;
for (int i = 0; i < draw_amount; i++) {
line++;
@@ -810,15 +900,20 @@ void TextEdit::_notification(int p_what) {
continue;
}
+ LineDrawingCache cache_entry;
+
Dictionary color_map = _get_line_syntax_highlighting(line);
// Ensure we at least use the font color.
Color current_color = !editable ? font_readonly_color : font_color;
+ if (draw_placeholder) {
+ current_color = font_placeholder_color;
+ }
- const Ref<TextParagraph> ldata = text.get_line_data(line);
+ const Ref<TextParagraph> ldata = draw_placeholder ? placeholder_data_buf : text.get_line_data(line);
- Vector<String> wrap_rows = get_line_wrapped_text(line);
- int line_wrap_amount = get_line_wrap_count(line);
+ Vector<String> wrap_rows = draw_placeholder ? placeholder_wraped_rows : get_line_wrapped_text(line);
+ int line_wrap_amount = draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_line_wrap_count(line);
for (int line_wrap_index = 0; line_wrap_index <= line_wrap_amount; line_wrap_index++) {
if (line_wrap_index != 0) {
@@ -878,7 +973,7 @@ void TextEdit::_notification(int p_what) {
// Give visual indication of empty selected line.
if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
- int char_w = font->get_char_size(' ', 0, font_size).width;
+ float char_w = font->get_char_size(' ', 0, font_size).width;
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color);
} else {
@@ -899,6 +994,8 @@ void TextEdit::_notification(int p_what) {
if (line_wrap_index == 0) {
// Only do these if we are on the first wrapped part of a line.
+ cache_entry.y_offset = ofs_y;
+
int gutter_offset = style_normal->get_margin(SIDE_LEFT);
for (int g = 0; g < gutters.size(); g++) {
const GutterInfo gutter = gutters[g];
@@ -910,7 +1007,7 @@ void TextEdit::_notification(int p_what) {
switch (gutter.type) {
case GUTTER_TYPE_STRING: {
const String &text = get_line_gutter_text(line, g);
- if (text == "") {
+ if (text.is_empty()) {
break;
}
@@ -953,15 +1050,17 @@ void TextEdit::_notification(int p_what) {
icon->draw_rect(ci, gutter_rect, false, get_line_gutter_item_color(line, g));
} break;
case GUTTER_TYPE_CUSTOM: {
- if (gutter.custom_draw_obj.is_valid()) {
- Object *cdo = ObjectDB::get_instance(gutter.custom_draw_obj);
- if (cdo) {
- Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
- if (rtl) {
- gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x;
- }
- cdo->call(gutter.custom_draw_callback, line, g, Rect2(gutter_rect));
+ if (gutter.custom_draw_callback.is_valid()) {
+ Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
+ if (rtl) {
+ gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x;
}
+
+ Variant args[3] = { line, g, Rect2(gutter_rect) };
+ const Variant *argp[] = { &args[0], &args[1], &args[2] };
+ Callable::CallError ce;
+ Variant ret;
+ gutter.custom_draw_callback.call(argp, 3, ret, ce);
}
} break;
}
@@ -1045,12 +1144,12 @@ void TextEdit::_notification(int p_what) {
}
if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word
- if (_is_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '.') {
+ if (is_ascii_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '_' || lookup_symbol_word[0] == '.') {
int highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
while (highlighted_word_col != -1) {
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_word_col + start, highlighted_word_col + lookup_symbol_word.length() + start);
for (int j = 0; j < sel.size(); j++) {
- Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, row_height);
+ Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y + (line_spacing / 2), sel[j].y - sel[j].x, row_height);
if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) {
continue;
}
@@ -1060,9 +1159,9 @@ void TextEdit::_notification(int p_what) {
} else if (rect.position.x + rect.size.x > xmargin_end) {
rect.size.x = xmargin_end - rect.position.x;
}
- rect.position.y = TS->shaped_text_get_ascent(rid) + font->get_underline_position(font_size);
- rect.size.y = font->get_underline_thickness(font_size);
- draw_rect(rect, font_selected_color);
+ rect.position.y += ceil(TS->shaped_text_get_ascent(rid)) + ceil(font->get_underline_position(font_size));
+ rect.size.y = MAX(1, font->get_underline_thickness(font_size));
+ draw_rect(rect, color);
}
highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1);
@@ -1076,7 +1175,11 @@ void TextEdit::_notification(int p_what) {
int gl_size = TS->shaped_text_get_glyph_count(rid);
ofs_y += ldata->get_line_ascent(line_wrap_index);
- int char_ofs = 0;
+
+ int first_visible_char = TS->shaped_text_get_range(rid).y;
+ int last_visible_char = TS->shaped_text_get_range(rid).x;
+
+ float char_ofs = 0;
if (outline_size > 0 && outline_color.a > 0) {
for (int j = 0; j < gl_size; j++) {
for (int k = 0; k < glyphs[j].repeat; k++) {
@@ -1111,7 +1214,7 @@ void TextEdit::_notification(int p_what) {
}
}
- int char_pos = char_ofs + char_margin + ofs_x;
+ float char_pos = char_ofs + char_margin + ofs_x;
if (char_pos >= xmargin_beg) {
if (highlight_matching_braces_enabled) {
if ((brace_open_match_line == line && brace_open_match_column == glyphs[j].start) ||
@@ -1143,21 +1246,36 @@ void TextEdit::_notification(int p_what) {
}
}
+ bool had_glyphs_drawn = false;
for (int k = 0; k < glyphs[j].repeat; k++) {
if (!clipped && (char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) {
if (glyphs[j].font_rid != RID()) {
TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color);
+ had_glyphs_drawn = true;
} else if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
TS->draw_hex_code_box(ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color);
+ had_glyphs_drawn = true;
}
}
char_ofs += glyphs[j].advance;
}
+
+ if (had_glyphs_drawn) {
+ if (first_visible_char > glyphs[j].start) {
+ first_visible_char = glyphs[j].start;
+ } else if (last_visible_char < glyphs[j].end) {
+ last_visible_char = glyphs[j].end;
+ }
+ }
+
if ((char_ofs + char_margin) >= xmargin_end) {
break;
}
}
+ cache_entry.first_visible_chars.push_back(first_visible_char);
+ cache_entry.last_visible_chars.push_back(last_visible_char);
+
// is_line_folded
if (line_wrap_index == line_wrap_amount && line < text.size() - 1 && _is_line_hidden(line + 1)) {
int xofs = char_ofs + char_margin + ofs_x + (folded_eol_icon->get_width() / 2);
@@ -1170,7 +1288,8 @@ void TextEdit::_notification(int p_what) {
}
// Carets.
- int caret_width = Math::round(1 * get_theme_default_base_scale());
+ // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1).
+ const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale());
if (!clipped && caret.line == line && line_wrap_index == caret_wrap_index) {
caret.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index);
@@ -1200,7 +1319,7 @@ void TextEdit::_notification(int p_what) {
if (caret.draw_pos.x >= xmargin_beg && caret.draw_pos.x < xmargin_end) {
caret.visible = true;
- if (draw_caret) {
+ if (draw_caret || drag_caret_force_displayed) {
if (caret_type == CaretType::CARET_TYPE_BLOCK || overtype_mode) {
//Block or underline caret, draw trailing carets at full height.
int h = font->get_height(font_size);
@@ -1308,6 +1427,10 @@ void TextEdit::_notification(int p_what) {
}
}
}
+
+ if (!draw_placeholder) {
+ line_drawing_cache[line] = cache_entry;
+ }
}
if (has_focus()) {
@@ -1317,6 +1440,7 @@ void TextEdit::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_FOCUS_ENTER: {
if (caret_blink_enabled) {
caret_blink_timer->start();
@@ -1348,6 +1472,7 @@ void TextEdit::_notification(int p_what) {
DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, caret_start, caret_end);
}
} break;
+
case NOTIFICATION_FOCUS_EXIT: {
if (caret_blink_enabled) {
caret_blink_timer->stop();
@@ -1357,18 +1482,21 @@ void TextEdit::_notification(int p_what) {
DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
}
- ime_text = "";
- ime_selection = Point2();
- text.invalidate_cache(caret.line, caret.column, ime_text);
+ if (!ime_text.is_empty()) {
+ ime_text = "";
+ ime_selection = Point2();
+ text.invalidate_cache(caret.line, caret.column, true, ime_text);
+ }
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
- if (deselect_on_focus_loss_enabled) {
+ if (deselect_on_focus_loss_enabled && !selection.drag_attempt) {
deselect();
}
} break;
+
case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
if (has_focus()) {
ime_text = DisplayServer::get_singleton()->ime_get_text();
@@ -1381,13 +1509,95 @@ void TextEdit::_notification(int p_what) {
t = ime_text;
}
- text.invalidate_cache(caret.line, caret.column, t, structured_text_parser(st_parser, st_args, t));
+ text.invalidate_cache(caret.line, caret.column, true, t, structured_text_parser(st_parser, st_args, t));
update();
}
} break;
+
+ case NOTIFICATION_DRAG_BEGIN: {
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
+ drag_action = true;
+ dragging_minimap = false;
+ dragging_selection = false;
+ can_drag_minimap = false;
+ click_select_held->stop();
+ } break;
+
+ case NOTIFICATION_DRAG_END: {
+ if (is_drag_successful()) {
+ if (selection.drag_attempt) {
+ selection.drag_attempt = false;
+ if (is_editable() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ delete_selection();
+ } else if (deselect_on_focus_loss_enabled) {
+ deselect();
+ }
+ }
+ } else {
+ selection.drag_attempt = false;
+ }
+ drag_action = false;
+ drag_caret_force_displayed = false;
+ } break;
+ }
+}
+
+void TextEdit::unhandled_key_input(const Ref<InputEvent> &p_event) {
+ Ref<InputEventKey> k = p_event;
+
+ if (k.is_valid()) {
+ if (!k->is_pressed()) {
+ return;
+ }
+ // Handle Unicode (with modifiers active, process after shortcuts).
+ if (has_focus() && editable && (k->get_unicode() >= 32)) {
+ handle_unicode_input(k->get_unicode());
+ accept_event();
+ }
}
}
+bool TextEdit::alt_input(const Ref<InputEvent> &p_gui_input) {
+ Ref<InputEventKey> k = p_gui_input;
+ if (k.is_valid()) {
+ if (!k->is_pressed()) {
+ if (alt_start && k->get_keycode() == Key::ALT) {
+ alt_start = false;
+ if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) {
+ handle_unicode_input(alt_code);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ if (k->is_alt_pressed()) {
+ if (!alt_start) {
+ if (k->get_keycode() == Key::KP_ADD) {
+ alt_start = true;
+ alt_code = 0;
+ return true;
+ }
+ } else {
+ if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0);
+ }
+ if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::KP_0);
+ }
+ if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) {
+ alt_code = alt_code << 4;
+ alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
ERR_FAIL_COND(p_gui_input.is_null());
@@ -1469,8 +1679,9 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
set_caret_line(row, false, false);
set_caret_column(col);
+ selection.drag_attempt = false;
- if (mb->is_shift_pressed() && (caret.column != prev_col || caret.line != prev_line)) {
+ if (selecting_enabled && mb->is_shift_pressed() && (caret.column != prev_col || caret.line != prev_line)) {
if (!selection.active) {
selection.active = true;
selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER;
@@ -1512,6 +1723,9 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
+ } else if (is_mouse_over_selection()) {
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
+ selection.drag_attempt = true;
} else {
selection.active = false;
selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER;
@@ -1525,6 +1739,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (!mb->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && mb->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance) {
// Triple-click select line.
selection.selecting_mode = SelectionMode::SELECTION_MODE_LINE;
+ selection.drag_attempt = false;
_update_selection_mode_line();
last_dblclk = 0;
} else if (mb->is_double_click() && text[caret.line].length()) {
@@ -1534,7 +1749,6 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
last_dblclk = OS::get_singleton()->get_ticks_msec();
last_dblclk_pos = mb->get_position();
}
-
update();
}
@@ -1542,7 +1756,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
paste_primary_clipboard();
}
- if (mb->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
+ if (mb->get_button_index() == MouseButton::RIGHT && (context_menu_enabled || is_move_caret_on_right_click_enabled())) {
_reset_caret_blink_timer();
Point2i pos = get_line_column_at_pos(mpos);
@@ -1567,18 +1781,26 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- _generate_context_menu();
- menu->set_position(get_screen_transform().xform(mpos));
- menu->set_size(Vector2(1, 1));
- menu->popup();
- grab_focus();
+ if (context_menu_enabled) {
+ _generate_context_menu();
+ menu->set_position(get_screen_position() + mpos);
+ menu->reset_size();
+ menu->popup();
+ grab_focus();
+ }
}
} else {
if (mb->get_button_index() == MouseButton::LEFT) {
+ if (selection.drag_attempt && is_mouse_over_selection()) {
+ selection.active = false;
+ }
dragging_minimap = false;
dragging_selection = false;
can_drag_minimap = false;
click_select_held->stop();
+ if (!drag_action) {
+ selection.drag_attempt = false;
+ }
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
}
@@ -1663,6 +1885,14 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
hovered_gutter = current_hovered_gutter;
update();
}
+
+ if (drag_action && can_drop_data(mpos, get_viewport()->gui_get_drag_data())) {
+ drag_caret_force_displayed = true;
+ Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
+ set_caret_line(pos.y, false);
+ set_caret_column(pos.x);
+ dragging_selection = true;
+ }
}
if (draw_minimap && !dragging_selection) {
@@ -1676,6 +1906,10 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
Ref<InputEventKey> k = p_gui_input;
if (k.is_valid()) {
+ if (alt_input(p_gui_input)) {
+ accept_event();
+ return;
+ }
if (!k->is_pressed()) {
return;
}
@@ -1756,44 +1990,45 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
- // SELECT ALL, SELECT WORD UNDER CARET, CUT, COPY, PASTE.
-
- if (k->is_action("ui_text_select_all", true)) {
- select_all();
- accept_event();
- return;
- }
- if (k->is_action("ui_text_select_word_under_caret", true)) {
- select_word_under_caret();
- accept_event();
- return;
- }
- if (k->is_action("ui_cut", true)) {
- cut();
- accept_event();
- return;
- }
- if (k->is_action("ui_copy", true)) {
- copy();
- accept_event();
- return;
- }
- if (k->is_action("ui_paste", true)) {
- paste();
- accept_event();
- return;
- }
+ if (is_shortcut_keys_enabled()) {
+ // SELECT ALL, SELECT WORD UNDER CARET, CUT, COPY, PASTE.
+ if (k->is_action("ui_text_select_all", true)) {
+ select_all();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_select_word_under_caret", true)) {
+ select_word_under_caret();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_cut", true)) {
+ cut();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_copy", true)) {
+ copy();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_paste", true)) {
+ paste();
+ accept_event();
+ return;
+ }
- // UNDO/REDO.
- if (k->is_action("ui_undo", true)) {
- undo();
- accept_event();
- return;
- }
- if (k->is_action("ui_redo", true)) {
- redo();
- accept_event();
- return;
+ // UNDO/REDO.
+ if (k->is_action("ui_undo", true)) {
+ undo();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_redo", true)) {
+ redo();
+ accept_event();
+ return;
+ }
}
// MISC.
@@ -1801,8 +2036,8 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (context_menu_enabled) {
_generate_context_menu();
adjust_viewport_to_caret();
- menu->set_position(get_screen_transform().xform(get_caret_draw_pos()));
- menu->set_size(Vector2(1, 1));
+ menu->set_position(get_screen_position() + get_caret_draw_pos());
+ menu->reset_size();
menu->popup();
menu->grab_focus();
}
@@ -1926,6 +2161,7 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) {
bool first_line = false;
if (!p_split_current_line) {
+ deselect();
if (p_above) {
if (caret.line > 0) {
set_caret_line(caret.line - 1, false);
@@ -1964,16 +2200,21 @@ void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
if (p_move_by_word) {
int cc = caret.column;
-
+ // If the caret is at the start of the line, and not on the first line, move it up to the end of the previous line.
if (cc == 0 && caret.line > 0) {
set_caret_line(caret.line - 1);
set_caret_column(text[caret.line].length());
} else {
PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
- for (int i = words.size() - 2; i >= 0; i = i - 2) {
- if (words[i] < cc) {
- cc = words[i];
- break;
+ if (words.is_empty() || cc <= words[0]) {
+ // This solves the scenario where there are no words but glyfs that can be ignored.
+ cc = 0;
+ } else {
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < cc) {
+ cc = words[i];
+ break;
+ }
}
}
set_caret_column(cc);
@@ -2015,16 +2256,21 @@ void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
if (p_move_by_word) {
int cc = caret.column;
-
+ // If the caret is at the end of the line, and not on the last line, move it down to the beginning of the next line.
if (cc == text[caret.line].length() && caret.line < text.size() - 1) {
set_caret_line(caret.line + 1);
set_caret_column(0);
} else {
PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
- for (int i = 1; i < words.size(); i = i + 2) {
- if (words[i] > cc) {
- cc = words[i];
- break;
+ if (words.is_empty() || cc >= words[words.size() - 1]) {
+ // This solves the scenario where there are no words but glyfs that can be ignored.
+ cc = text[caret.line].length();
+ } else {
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > cc) {
+ cc = words[i];
+ break;
+ }
}
}
set_caret_column(cc);
@@ -2114,15 +2360,7 @@ void TextEdit::_move_caret_to_line_start(bool p_select) {
}
if (caret.column == row_start_col || wi == 0) {
// Compute whitespace symbols sequence length.
- int current_line_whitespace_len = 0;
- while (current_line_whitespace_len < text[caret.line].length()) {
- char32_t c = text[caret.line][current_line_whitespace_len];
- if (c != '\t' && c != ' ') {
- break;
- }
- current_line_whitespace_len++;
- }
-
+ int current_line_whitespace_len = get_first_non_whitespace_column(caret.line);
if (get_caret_column() == current_line_whitespace_len) {
set_caret_column(0);
} else {
@@ -2195,37 +2433,47 @@ void TextEdit::_move_caret_page_down(bool p_select) {
}
void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
- if (!editable) {
+ if (!editable || (caret.column == 0 && caret.line == 0 && !has_selection())) {
return;
}
- if (has_selection() || (!p_all_to_left && !p_word)) {
+ if (has_selection() || (!p_all_to_left && !p_word) || caret.column == 0) {
backspace();
return;
}
if (p_all_to_left) {
int caret_current_column = caret.column;
- caret.column = 0;
+ set_caret_column(0);
_remove_text(caret.line, 0, caret.line, caret_current_column);
return;
}
if (p_word) {
- int line = caret.line;
int column = caret.column;
-
- PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
- for (int i = words.size() - 2; i >= 0; i = i - 2) {
- if (words[i] < column) {
- column = words[i];
- break;
+ // Check for the case "<word><space><caret>" and ignore the space.
+ // No need to check for column being 0 since it is cheked above.
+ if (is_whitespace(text[caret.line][caret.column - 1])) {
+ column -= 1;
+ }
+ // Get a list with the indices of the word bounds of the given text line.
+ const PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
+ if (words.is_empty() || column <= words[0]) {
+ // If "words" is empty, meaning no words are left, we can remove everything until the begining of the line.
+ column = 0;
+ } else {
+ // Otherwise search for the first word break that is smaller than the index from we're currentlu deleteing
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < column) {
+ column = words[i];
+ break;
+ }
}
}
- _remove_text(line, column, caret.line, caret.column);
+ _remove_text(caret.line, column, caret.line, caret.column);
- set_caret_line(line, false);
+ set_caret_line(caret.line, false);
set_caret_column(column);
return;
}
@@ -2250,6 +2498,10 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) {
int next_column;
if (p_all_to_right) {
+ if (caret.column == curline_len) {
+ return;
+ }
+
// Delete everything to right of caret
next_column = curline_len;
next_line = caret.line;
@@ -2311,6 +2563,47 @@ void TextEdit::_move_caret_document_end(bool p_select) {
}
}
+void TextEdit::_update_placeholder() {
+ if (font.is_null() || font_size <= 0) {
+ return; // Not in tree?
+ }
+
+ // Placeholder is generally smaller then text documents, and updates less so this should be fast enough for now.
+ placeholder_data_buf->clear();
+ placeholder_data_buf->set_width(text.get_width());
+ placeholder_data_buf->set_direction((TextServer::Direction)text_direction);
+ placeholder_data_buf->set_preserve_control(draw_control_chars);
+ placeholder_data_buf->add_string(placeholder_text, font, font_size, opentype_features, language);
+
+ placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_text);
+ if (placeholder_bidi_override.is_empty()) {
+ TS->shaped_text_set_bidi_override(placeholder_data_buf->get_rid(), placeholder_bidi_override);
+ }
+
+ if (get_tab_size() > 0) {
+ Vector<float> tabs;
+ tabs.push_back(font->get_char_size(' ', 0, font_size).width * get_tab_size());
+ placeholder_data_buf->tab_align(tabs);
+ }
+
+ // Update height.
+ const int wrap_amount = placeholder_data_buf->get_line_count() - 1;
+ placeholder_line_height = font->get_height(font_size);
+ for (int i = 0; i <= wrap_amount; i++) {
+ placeholder_line_height = MAX(placeholder_line_height, placeholder_data_buf->get_line_size(i).y);
+ }
+
+ // Update width.
+ placeholder_max_width = placeholder_data_buf->get_size().x;
+
+ // Update wrapped rows.
+ placeholder_wraped_rows.clear();
+ for (int i = 0; i <= wrap_amount; i++) {
+ Vector2i line_range = placeholder_data_buf->get_line_range(i);
+ placeholder_wraped_rows.push_back(placeholder_text.substr(line_range.x, line_range.y - line_range.x));
+ }
+}
+
void TextEdit::_update_caches() {
/* Internal API for CodeEdit. */
brace_mismatch_color = get_theme_color(SNAME("brace_mismatch_color"), SNAME("CodeEdit"));
@@ -2341,6 +2634,7 @@ void TextEdit::_update_caches() {
font_size = get_theme_font_size(SNAME("font_size"));
font_color = get_theme_color(SNAME("font_color"));
font_readonly_color = get_theme_color(SNAME("font_readonly_color"));
+ font_placeholder_color = get_theme_color(SNAME("font_placeholder_color"));
outline_size = get_theme_constant(SNAME("outline_size"));
outline_color = get_theme_color(SNAME("font_outline_color"));
@@ -2358,12 +2652,13 @@ void TextEdit::_update_caches() {
} else {
dir = (TextServer::Direction)text_direction;
}
- text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
text.set_font_features(opentype_features);
text.set_draw_control_chars(draw_control_chars);
text.set_font(font);
text.set_font_size(font_size);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
/* Syntax highlighting. */
if (syntax_highlighter.is_valid()) {
@@ -2380,6 +2675,75 @@ bool TextEdit::is_text_field() const {
return true;
}
+Variant TextEdit::get_drag_data(const Point2 &p_point) {
+ if (selection.active && selection.drag_attempt) {
+ String t = get_selected_text();
+ Label *l = memnew(Label);
+ l->set_text(t);
+ set_drag_preview(l);
+ return t;
+ }
+
+ return Variant();
+}
+
+bool TextEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+ bool drop_override = Control::can_drop_data(p_point, p_data); // In case user wants to drop custom data.
+ if (drop_override) {
+ return drop_override;
+ }
+
+ return is_editable() && p_data.get_type() == Variant::STRING;
+}
+
+void TextEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
+ Control::drop_data(p_point, p_data);
+
+ if (p_data.get_type() == Variant::STRING && is_editable()) {
+ Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
+ int caret_row_tmp = pos.y;
+ int caret_column_tmp = pos.x;
+ if (selection.drag_attempt) {
+ selection.drag_attempt = false;
+ if (!is_mouse_over_selection(!Input::get_singleton()->is_key_pressed(Key::CTRL))) {
+ begin_complex_operation();
+ if (!Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ if (caret_row_tmp > selection.to_line) {
+ caret_row_tmp = caret_row_tmp - (selection.to_line - selection.from_line);
+ } else if (caret_row_tmp == selection.to_line && caret_column_tmp >= selection.to_column) {
+ caret_column_tmp = caret_column_tmp - (selection.to_column - selection.from_column);
+ }
+ delete_selection();
+ } else {
+ deselect();
+ }
+
+ set_caret_line(caret_row_tmp, true, false);
+ set_caret_column(caret_column_tmp);
+ insert_text_at_caret(p_data);
+ end_complex_operation();
+ }
+ } else if (is_mouse_over_selection()) {
+ caret_row_tmp = selection.from_line;
+ caret_column_tmp = selection.from_column;
+ set_caret_line(caret_row_tmp, true, false);
+ set_caret_column(caret_column_tmp);
+ insert_text_at_caret(p_data);
+ grab_focus();
+ } else {
+ deselect();
+ set_caret_line(caret_row_tmp, true, false);
+ set_caret_column(caret_column_tmp);
+ insert_text_at_caret(p_data);
+ grab_focus();
+ }
+
+ if (caret_row_tmp != caret.line || caret_column_tmp != caret.column) {
+ select(caret_row_tmp, caret_column_tmp, caret.line, caret.column);
+ }
+ }
+}
+
Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
Point2i pos = get_line_column_at_pos(p_pos);
int row = pos.y;
@@ -2410,8 +2774,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
}
String TextEdit::get_tooltip(const Point2 &p_pos) const {
- Object *tooltip_obj = ObjectDB::get_instance(tooltip_obj_id);
- if (!tooltip_obj) {
+ if (!tooltip_callback.is_valid()) {
return Control::get_tooltip(p_pos);
}
Point2i pos = get_line_column_at_pos(p_pos);
@@ -2424,19 +2787,20 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
}
int beg, end;
if (select_word(s, col, beg, end)) {
- String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud);
-
- return tt;
+ Variant args[1] = { s.substr(beg, end - beg) };
+ const Variant *argp[] = { &args[0] };
+ Callable::CallError ce;
+ Variant ret;
+ tooltip_callback.call(argp, 1, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, "", "Failed to call custom tooltip.");
+ return ret;
}
return Control::get_tooltip(p_pos);
}
-void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) {
- ERR_FAIL_NULL(p_obj);
- tooltip_obj_id = p_obj->get_instance_id();
- tooltip_func = p_function;
- tooltip_ud = p_udata;
+void TextEdit::set_tooltip_request_func(const Callable &p_tooltip_callback) {
+ tooltip_callback = p_tooltip_callback;
}
/* Text */
@@ -2472,8 +2836,9 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) {
} else {
dir = (TextServer::Direction)text_direction;
}
- text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
- text.invalidate_all();
+ text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text.invalidate_font();
+ _update_placeholder();
if (menu_dir) {
menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED);
@@ -2494,7 +2859,8 @@ void TextEdit::set_opentype_feature(const String &p_name, int p_value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
opentype_features[tag] = p_value;
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
}
@@ -2510,7 +2876,8 @@ int TextEdit::get_opentype_feature(const String &p_name) const {
void TextEdit::clear_opentype_features() {
opentype_features.clear();
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
@@ -2523,8 +2890,9 @@ void TextEdit::set_language(const String &p_language) {
} else {
dir = (TextServer::Direction)text_direction;
}
- text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
text.invalidate_all();
+ _update_placeholder();
update();
}
}
@@ -2533,7 +2901,7 @@ String TextEdit::get_language() const {
return language;
}
-void TextEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
+void TextEdit::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
if (st_parser != p_parser) {
st_parser = p_parser;
for (int i = 0; i < text.size(); i++) {
@@ -2543,7 +2911,7 @@ void TextEdit::set_structured_text_bidi_override(Control::StructuredTextParser p
}
}
-Control::StructuredTextParser TextEdit::get_structured_text_bidi_override() const {
+TextServer::StructuredTextParser TextEdit::get_structured_text_bidi_override() const {
return st_parser;
}
@@ -2566,6 +2934,7 @@ void TextEdit::set_tab_size(const int p_size) {
}
text.set_tab_size(p_size);
text.invalidate_all_lines();
+ _update_placeholder();
update();
}
@@ -2624,15 +2993,31 @@ void TextEdit::clear() {
}
void TextEdit::_clear() {
+ if (editable && undo_enabled) {
+ _move_caret_document_start(false);
+ begin_complex_operation();
+
+ _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0));
+ insert_text_at_caret("");
+ text.clear();
+
+ end_complex_operation();
+ return;
+ }
+ // Cannot merge with above, as we are not part of the tree on creation.
+ int old_text_size = text.size();
+
clear_undo_history();
text.clear();
- caret.column = 0;
- caret.line = 0;
+ set_caret_line(0, false);
+ set_caret_column(0);
caret.x_ofs = 0;
caret.line_ofs = 0;
caret.wrap_ofs = 0;
caret.last_fit_x = 0;
selection.active = false;
+
+ emit_signal(SNAME("lines_edited_from"), old_text_size, 0);
}
void TextEdit::set_text(const String &p_text) {
@@ -2677,18 +3062,30 @@ int TextEdit::get_line_count() const {
return text.size();
}
+void TextEdit::set_placeholder(const String &p_text) {
+ placeholder_text = p_text;
+ _update_placeholder();
+ update();
+}
+
+String TextEdit::get_placeholder() const {
+ return placeholder_text;
+}
+
void TextEdit::set_line(int p_line, const String &p_new_text) {
if (p_line < 0 || p_line >= text.size()) {
return;
}
+ begin_complex_operation();
_remove_text(p_line, 0, p_line, text[p_line].length());
_insert_text(p_line, 0, p_new_text);
- if (caret.line == p_line) {
- caret.column = MIN(caret.column, p_new_text.length());
+ if (caret.line == p_line && caret.column > p_new_text.length()) {
+ set_caret_column(MIN(caret.column, p_new_text.length()), false);
}
if (has_selection() && p_line == selection.to_line && selection.to_column > text[p_line].length()) {
selection.to_column = text[p_line].length();
}
+ end_complex_operation();
}
String TextEdit::get_line(int p_line) const {
@@ -2731,7 +3128,7 @@ int TextEdit::get_first_non_whitespace_column(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
int col = 0;
- while (col < text[p_line].length() && _is_whitespace(text[p_line][col])) {
+ while (col < text[p_line].length() && is_whitespace(text[p_line][col])) {
col++;
}
return col;
@@ -2743,8 +3140,10 @@ void TextEdit::swap_lines(int p_from_line, int p_to_line) {
String tmp = get_line(p_from_line);
String tmp2 = get_line(p_to_line);
+ begin_complex_operation();
set_line(p_to_line, tmp);
set_line(p_from_line, tmp2);
+ end_complex_operation();
}
void TextEdit::insert_line_at(int p_at, const String &p_text) {
@@ -2753,7 +3152,7 @@ void TextEdit::insert_line_at(int p_at, const String &p_text) {
_insert_text(p_at, 0, p_text + "\n");
if (caret.line >= p_at) {
// offset caret when located after inserted line
- ++caret.line;
+ set_caret_line(caret.line + 1, false);
}
if (has_selection()) {
if (selection.from_line >= p_at) {
@@ -2765,6 +3164,7 @@ void TextEdit::insert_line_at(int p_at, const String &p_text) {
++selection.to_line;
}
}
+ update();
}
void TextEdit::insert_text_at_caret(const String &p_text) {
@@ -3145,9 +3545,6 @@ void TextEdit::undo() {
TextOperation op = undo_stack_pos->get();
_do_text_op(op, true);
- if (op.type != TextOperation::TYPE_INSERT && (op.from_line != op.to_line || op.to_column != op.from_column + 1)) {
- select(op.from_line, op.from_column, op.to_line, op.to_column);
- }
current_op.version = op.prev_version;
if (undo_stack_pos->get().chain_backward) {
@@ -3163,6 +3560,10 @@ void TextEdit::undo() {
}
}
+ if (op.type != TextOperation::TYPE_INSERT && (op.from_line != op.to_line || op.to_column != op.from_column + 1)) {
+ select(op.from_line, op.from_column, op.to_line, op.to_column);
+ }
+
_update_scrollbars();
if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) {
set_caret_line(undo_stack_pos->get().to_line, false);
@@ -3316,9 +3717,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro
if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) {
// Validate for whole words.
- if (pos > 0 && _is_text_char(text_line[pos - 1])) {
+ if (pos > 0 && !is_symbol(text_line[pos - 1])) {
is_match = false;
- } else if (pos + p_key.length() < text_line.length() && _is_text_char(text_line[pos + p_key.length()])) {
+ } else if (pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) {
is_match = false;
}
}
@@ -3457,6 +3858,49 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_
return Point2i(col, row);
}
+Point2i TextEdit::get_pos_at_line_column(int p_line, int p_column) const {
+ Rect2i rect = get_rect_at_line_column(p_line, p_column);
+ return rect.position.x == -1 ? rect.position : rect.position + Vector2i(0, get_line_height());
+}
+
+Rect2i TextEdit::get_rect_at_line_column(int p_line, int p_column) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), Rect2i(-1, -1, 0, 0));
+ ERR_FAIL_COND_V(p_column < 0, Rect2i(-1, -1, 0, 0));
+ ERR_FAIL_COND_V(p_column > text[p_line].length(), Rect2i(-1, -1, 0, 0));
+
+ if (line_drawing_cache.size() == 0 || !line_drawing_cache.has(p_line)) {
+ // Line is not in the cache, which means it's outside of the viewing area.
+ return Rect2i(-1, -1, 0, 0);
+ }
+ LineDrawingCache cache_entry = line_drawing_cache[p_line];
+
+ int wrap_index = get_line_wrap_index_at_column(p_line, p_column);
+ if (wrap_index >= cache_entry.first_visible_chars.size()) {
+ // Line seems to be wrapped beyond the viewable area.
+ return Rect2i(-1, -1, 0, 0);
+ }
+
+ int first_visible_char = cache_entry.first_visible_chars[wrap_index];
+ int last_visible_char = cache_entry.last_visible_chars[wrap_index];
+ if (p_column < first_visible_char || p_column > last_visible_char) {
+ // Character is outside of the viewing area, no point calculating its position.
+ return Rect2i(-1, -1, 0, 0);
+ }
+
+ Point2i pos, size;
+ pos.y = cache_entry.y_offset + get_line_height() * wrap_index;
+ pos.x = get_total_gutter_width() + style_normal->get_margin(SIDE_LEFT) - get_h_scroll();
+
+ RID text_rid = text.get_line_data(p_line)->get_line_rid(wrap_index);
+ Vector2 col_bounds = TS->shaped_text_get_grapheme_bounds(text_rid, p_column);
+ pos.x += col_bounds.x;
+ size.x = col_bounds.y - col_bounds.x;
+
+ size.y = get_line_height();
+
+ return Rect2i(pos, size);
+}
+
int TextEdit::get_minimap_line_at_pos(const Point2i &p_pos) const {
float rows = p_pos.y;
rows -= style_normal->get_margin(SIDE_TOP);
@@ -3511,6 +3955,21 @@ bool TextEdit::is_dragging_cursor() const {
return dragging_selection || dragging_minimap;
}
+bool TextEdit::is_mouse_over_selection(bool p_edges) const {
+ if (!has_selection()) {
+ return false;
+ }
+ Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
+ int row = pos.y;
+ int col = pos.x;
+ if (p_edges) {
+ if ((row == selection.from_line && col == selection.from_column) || (row == selection.to_line && col == selection.to_column)) {
+ return true;
+ }
+ }
+ return (row >= selection.from_line && row <= selection.to_line && (row > selection.from_line || col > selection.from_column) && (row < selection.to_line || col < selection.to_column));
+}
+
/* Caret */
void TextEdit::set_caret_type(CaretType p_type) {
caret_type = p_type;
@@ -3600,6 +4059,7 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_
}
}
}
+ bool caret_moved = caret.line != p_line;
caret.line = p_line;
int n_col = _get_char_pos_for_line(caret.last_fit_x, p_line, p_wrap_index);
@@ -3613,15 +4073,16 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_
n_col -= 1;
}
}
+ caret_moved = (caret_moved || caret.column != n_col);
caret.column = n_col;
- if (p_adjust_viewport) {
+ if (is_inside_tree() && p_adjust_viewport) {
adjust_viewport_to_caret();
}
setting_caret_line = false;
- if (!caret_pos_dirty) {
+ if (caret_moved && !caret_pos_dirty) {
if (is_inside_tree()) {
MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed");
}
@@ -3637,19 +4098,20 @@ void TextEdit::set_caret_column(int p_col, bool p_adjust_viewport) {
if (p_col < 0) {
p_col = 0;
}
+ if (p_col > get_line(caret.line).length()) {
+ p_col = get_line(caret.line).length();
+ }
+ bool caret_moved = caret.column != p_col;
caret.column = p_col;
- if (caret.column > get_line(caret.line).length()) {
- caret.column = get_line(caret.line).length();
- }
caret.last_fit_x = _get_column_x_offset_for_line(caret.column, caret.line);
- if (p_adjust_viewport) {
+ if (is_inside_tree() && p_adjust_viewport) {
adjust_viewport_to_caret();
}
- if (!caret_pos_dirty) {
+ if (caret_moved && !caret_pos_dirty) {
if (is_inside_tree()) {
MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed");
}
@@ -3770,13 +4232,18 @@ void TextEdit::select_word_under_caret() {
int end = 0;
const PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
for (int i = 0; i < words.size(); i = i + 2) {
- if ((words[i] < caret.column && words[i + 1] > caret.column) || (i == words.size() - 2 && caret.column == words[i + 1])) {
+ if ((words[i] <= caret.column && words[i + 1] >= caret.column) || (i == words.size() - 2 && caret.column == words[i + 1])) {
begin = words[i];
end = words[i + 1];
break;
}
}
+ // No word found.
+ if (begin == 0 && end == 0) {
+ return;
+ }
+
select(caret.line, begin, caret.line, end);
/* Move the caret to the end of the word for easier editing. */
set_caret_column(end, false);
@@ -3852,10 +4319,12 @@ String TextEdit::get_selected_text() const {
}
int TextEdit::get_selection_line() const {
+ ERR_FAIL_COND_V(!selection.active, -1);
return selection.selecting_line;
}
int TextEdit::get_selection_column() const {
+ ERR_FAIL_COND_V(!selection.active, -1);
return selection.selecting_column;
}
@@ -3897,7 +4366,7 @@ void TextEdit::delete_selection() {
update();
}
-/* line wrapping. */
+/* Line wrapping. */
void TextEdit::set_line_wrapping_mode(LineWrappingMode p_wrapping_mode) {
if (line_wrapping_mode != p_wrapping_mode) {
line_wrapping_mode = p_wrapping_mode;
@@ -4013,6 +4482,8 @@ int TextEdit::get_h_scroll() const {
}
void TextEdit::set_v_scroll_speed(float p_speed) {
+ // Prevent setting a vertical scroll speed value under 1.0
+ ERR_FAIL_COND(p_speed < 1.0);
v_scroll_speed = p_speed;
}
@@ -4029,7 +4500,10 @@ double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
return p_line;
}
- double new_line_scroll_pos = get_visible_line_count_in_range(0, CLAMP(p_line, 0, text.size() - 1));
+ double new_line_scroll_pos = 0.0;
+ if (p_line > 0) {
+ new_line_scroll_pos = get_visible_line_count_in_range(0, MIN(p_line - 1, text.size() - 1));
+ }
new_line_scroll_pos += p_wrap_index;
return new_line_scroll_pos;
}
@@ -4052,9 +4526,13 @@ void TextEdit::set_line_as_center_visible(int p_line, int p_wrap_index) {
ERR_FAIL_COND(p_wrap_index > get_line_wrap_count(p_line));
int visible_rows = get_visible_line_count();
- Point2i next_line = get_next_visible_line_index_offset_from(p_line, p_wrap_index, -visible_rows / 2);
+ Point2i next_line = get_next_visible_line_index_offset_from(p_line, p_wrap_index, (-visible_rows / 2) - 1);
int first_line = p_line - next_line.x + 1;
+ if (first_line < 0) {
+ set_v_scroll(0);
+ return;
+ }
set_v_scroll(get_scroll_pos_for_line(first_line, next_line.y));
}
@@ -4066,6 +4544,12 @@ void TextEdit::set_line_as_last_visible(int p_line, int p_wrap_index) {
Point2i next_line = get_next_visible_line_index_offset_from(p_line, p_wrap_index, -get_visible_line_count() - 1);
int first_line = p_line - next_line.x + 1;
+ // Adding _get_visible_lines_offset is not 100% correct as we end up showing almost p_line + 1, however, it provides a
+ // better user experience. Therefore we need to special case < visible line count, else showing line 0 is impossible.
+ if (get_visible_line_count_in_range(0, p_line) < get_visible_line_count() + 1) {
+ set_v_scroll(0);
+ return;
+ }
set_v_scroll(get_scroll_pos_for_line(first_line, next_line.y) + _get_visible_lines_offset());
}
@@ -4089,11 +4573,15 @@ int TextEdit::get_visible_line_count() const {
int TextEdit::get_visible_line_count_in_range(int p_from_line, int p_to_line) const {
ERR_FAIL_INDEX_V(p_from_line, text.size(), 0);
ERR_FAIL_INDEX_V(p_to_line, text.size(), 0);
- ERR_FAIL_COND_V(p_from_line > p_to_line, 0);
+
+ // So we can handle inputs in whatever order
+ if (p_from_line > p_to_line) {
+ SWAP(p_from_line, p_to_line);
+ }
/* Returns the total number of (lines + wrapped - hidden). */
if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
- return p_to_line - p_from_line;
+ return (p_to_line - p_from_line) + 1;
}
int total_rows = 0;
@@ -4278,7 +4766,7 @@ void TextEdit::add_gutter(int p_at) {
void TextEdit::remove_gutter(int p_gutter) {
ERR_FAIL_INDEX(p_gutter, gutters.size());
- gutters.remove(p_gutter);
+ gutters.remove_at(p_gutter);
for (int i = 0; i < text.size() + 1; i++) {
text.remove_gutter(p_gutter);
@@ -4398,12 +4886,10 @@ void TextEdit::merge_gutters(int p_from_line, int p_to_line) {
update();
}
-void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) {
+void TextEdit::set_gutter_custom_draw(int p_gutter, const Callable &p_draw_callback) {
ERR_FAIL_INDEX(p_gutter, gutters.size());
- ERR_FAIL_NULL(p_object);
- gutters.write[p_gutter].custom_draw_obj = p_object->get_instance_id();
- gutters.write[p_gutter].custom_draw_callback = p_callback;
+ gutters.write[p_gutter].custom_draw_callback = p_draw_callback;
update();
}
@@ -4522,7 +5008,8 @@ void TextEdit::set_draw_control_chars(bool p_enabled) {
menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
}
text.set_draw_control_chars(draw_control_chars);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
}
@@ -4602,6 +5089,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text);
ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count);
+ ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &TextEdit::set_placeholder);
+ ClassDB::bind_method(D_METHOD("get_placeholder"), &TextEdit::get_placeholder);
+
ClassDB::bind_method(D_METHOD("set_line", "line", "new_text"), &TextEdit::set_line);
ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line);
@@ -4693,7 +5183,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("search", "text", "flags", "from_line", "from_colum"), &TextEdit::search);
/* Tooltip */
- ClassDB::bind_method(D_METHOD("set_tooltip_request_func", "object", "callback", "data"), &TextEdit::set_tooltip_request_func);
+ ClassDB::bind_method(D_METHOD("set_tooltip_request_func", "callback"), &TextEdit::set_tooltip_request_func);
/* Mouse */
ClassDB::bind_method(D_METHOD("get_local_mouse_pos"), &TextEdit::get_local_mouse_pos);
@@ -4701,9 +5191,13 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_word_at_pos", "position"), &TextEdit::get_word_at_pos);
ClassDB::bind_method(D_METHOD("get_line_column_at_pos", "position", "allow_out_of_bounds"), &TextEdit::get_line_column_at_pos, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_pos_at_line_column", "line", "column"), &TextEdit::get_pos_at_line_column);
+ ClassDB::bind_method(D_METHOD("get_rect_at_line_column", "line", "column"), &TextEdit::get_rect_at_line_column);
+
ClassDB::bind_method(D_METHOD("get_minimap_line_at_pos", "position"), &TextEdit::get_minimap_line_at_pos);
ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor);
+ ClassDB::bind_method(D_METHOD("is_mouse_over_selection", "edges"), &TextEdit::is_mouse_over_selection);
/* Caret. */
BIND_ENUM_CONSTANT(CARET_TYPE_LINE);
@@ -4778,7 +5272,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("deselect"), &TextEdit::deselect);
ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection);
- /* line wrapping. */
+ /* Line wrapping. */
BIND_ENUM_CONSTANT(LINE_WRAPPING_NONE);
BIND_ENUM_CONSTANT(LINE_WRAPPING_BOUNDARY);
@@ -4795,7 +5289,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_wrapped_text", "line"), &TextEdit::get_line_wrapped_text);
/* Viewport. */
- // Scolling.
+ // Scrolling.
ClassDB::bind_method(D_METHOD("set_smooth_scroll_enable", "enable"), &TextEdit::set_smooth_scroll_enabled);
ClassDB::bind_method(D_METHOD("is_smooth_scroll_enabled"), &TextEdit::is_smooth_scroll_enabled);
@@ -4861,7 +5355,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gutter_overwritable", "gutter", "overwritable"), &TextEdit::set_gutter_overwritable);
ClassDB::bind_method(D_METHOD("is_gutter_overwritable", "gutter"), &TextEdit::is_gutter_overwritable);
ClassDB::bind_method(D_METHOD("merge_gutters", "from_line", "to_line"), &TextEdit::merge_gutters);
- ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "object", "callback"), &TextEdit::set_gutter_custom_draw);
+ ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "draw_callback"), &TextEdit::set_gutter_custom_draw);
ClassDB::bind_method(D_METHOD("get_total_gutter_width"), &TextEdit::get_total_gutter_width);
// Line gutters.
@@ -4906,8 +5400,9 @@ void TextEdit::_bind_methods() {
/* Inspector */
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text", PROPERTY_HINT_MULTILINE_TEXT), "set_placeholder", "get_placeholder");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
@@ -4975,19 +5470,21 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
}
@@ -5017,7 +5514,7 @@ bool TextEdit::_get(const StringName &p_name, Variant &r_ret) const {
void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
@@ -5157,7 +5654,7 @@ void TextEdit::_cut_internal() {
set_caret_line(get_caret_line() + 1);
}
- // Correct the visualy perceived caret column taking care of identation level of the lines.
+ // Correct the visually perceived caret column taking care of indentation level of the lines.
int diff_indent = indent_level - get_indent_level(get_caret_line());
cc += diff_indent;
if (diff_indent != 0) {
@@ -5275,8 +5772,10 @@ void TextEdit::_generate_context_menu() {
if (editable) {
menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE);
}
- menu->add_separator();
- if (is_selecting_enabled()) {
+ if (selecting_enabled || editable) {
+ menu->add_separator();
+ }
+ if (selecting_enabled) {
menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE);
}
if (editable) {
@@ -5403,9 +5902,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) {
p_from_column = col;
- if (col > 0 && _is_text_char(p_search[col - 1])) {
+ if (col > 0 && !is_symbol(p_search[col - 1])) {
col = -1;
- } else if ((col + p_key.length()) < p_search.length() && _is_text_char(p_search[col + p_key.length()])) {
+ } else if ((col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) {
col = -1;
}
}
@@ -5460,7 +5959,7 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line) const {
int row = 0;
Vector<Vector2i> rows2 = text.get_line_wrap_ranges(p_line);
for (int i = 0; i < rows2.size(); i++) {
- if ((p_char >= rows2[i].x) && (p_char < rows2[i].y)) {
+ if ((p_char >= rows2[i].x) && (p_char <= rows2[i].y)) {
row = i;
break;
}
@@ -5544,8 +6043,8 @@ void TextEdit::_update_selection_mode_word() {
selection.selected_word_beg = beg;
selection.selected_word_end = end;
selection.selected_word_origin = beg;
- set_caret_line(selection.to_line, false);
- set_caret_column(selection.to_column);
+ set_caret_line(line, false);
+ set_caret_column(end);
} else {
if ((col <= selection.selected_word_origin && line == selection.selecting_line) || line < selection.selecting_line) {
selection.selecting_column = selection.selected_word_end;
@@ -5601,6 +6100,10 @@ void TextEdit::_update_selection_mode_line() {
}
void TextEdit::_pre_shift_selection() {
+ if (!selecting_enabled) {
+ return;
+ }
+
if (!selection.active || selection.selecting_mode == SelectionMode::SELECTION_MODE_NONE) {
selection.selecting_line = caret.line;
selection.selecting_column = caret.column;
@@ -5611,6 +6114,10 @@ void TextEdit::_pre_shift_selection() {
}
void TextEdit::_post_shift_selection() {
+ if (!selecting_enabled) {
+ return;
+ }
+
if (selection.active && selection.selecting_mode == SelectionMode::SELECTION_MODE_SHIFT) {
select(selection.selecting_line, selection.selecting_column, caret.line, caret.column);
update();
@@ -5639,6 +6146,7 @@ void TextEdit::_update_wrap_at_column(bool p_force) {
text.set_width(-1);
}
text.invalidate_all_lines();
+ _update_placeholder();
}
_update_caret_wrap_offset();
@@ -5666,14 +6174,16 @@ void TextEdit::_update_scrollbars() {
h_scroll->set_begin(Point2(0, size.height - hmin.height));
h_scroll->set_end(Point2(size.width - vmin.width, size.height));
+ bool draw_placeholder = text.size() == 1 && text[0].length() == 0;
+
int visible_rows = get_visible_line_count();
- int total_rows = get_total_visible_line_count();
+ int total_rows = draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_total_visible_line_count();
if (scroll_past_end_of_file_enabled) {
total_rows += visible_rows - 1;
}
int visible_width = size.width - style_normal->get_minimum_size().width;
- int total_width = text.get_max_width() + vmin.x + gutters_width + gutter_padding;
+ int total_width = (draw_placeholder ? placeholder_max_width : text.get_max_width()) + vmin.x + gutters_width + gutter_padding;
if (draw_minimap) {
total_width += minimap_width;
@@ -5696,6 +6206,7 @@ void TextEdit::_update_scrollbars() {
caret.line_ofs = 0;
caret.wrap_ofs = 0;
v_scroll->set_value(0);
+ v_scroll->set_max(0);
v_scroll->hide();
}
@@ -5713,6 +6224,7 @@ void TextEdit::_update_scrollbars() {
} else {
caret.x_ofs = 0;
h_scroll->set_value(0);
+ h_scroll->set_max(0);
h_scroll->hide();
}
@@ -5743,20 +6255,22 @@ void TextEdit::_scroll_moved(double p_to_val) {
}
if (v_scroll->is_visible_in_tree()) {
// Set line ofs and wrap ofs.
+ bool draw_placeholder = text.size() == 1 && text[0].length() == 0;
+
int v_scroll_i = floor(get_v_scroll());
int sc = 0;
int n_line;
for (n_line = 0; n_line < text.size(); n_line++) {
if (!_is_line_hidden(n_line)) {
sc++;
- sc += get_line_wrap_count(n_line);
+ sc += draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_line_wrap_count(n_line);
if (sc > v_scroll_i) {
break;
}
}
}
n_line = MIN(n_line, text.size() - 1);
- int line_wrap_amount = get_line_wrap_count(n_line);
+ int line_wrap_amount = draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_line_wrap_count(n_line);
int wi = line_wrap_amount - (sc - v_scroll_i - 1);
wi = CLAMP(wi, 0, line_wrap_amount);
@@ -6088,11 +6602,11 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
ERR_FAIL_COND(p_char < 0);
/* STEP 1: Remove \r from source text and separate in substrings. */
-
- Vector<String> substrings = p_text.replace("\r", "").split("\n");
+ const String text_to_insert = p_text.replace("\r", "");
+ Vector<String> substrings = text_to_insert.split("\n");
// Is this just a new empty line?
- bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
+ bool shift_first_line = p_char == 0 && substrings.size() == 2 && text_to_insert == "\n";
/* STEP 2: Add spaces if the char is greater than the end of the line. */
while (p_char > text[p_line].length()) {
@@ -6100,24 +6614,19 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
}
/* STEP 3: Separate dest string in pre and post text. */
-
- String preinsert_text = text[p_line].substr(0, p_char);
String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
- for (int j = 0; j < substrings.size(); j++) {
- // Insert the substrings.
+ substrings.write[0] = text[p_line].substr(0, p_char) + substrings[0];
+ substrings.write[substrings.size() - 1] += postinsert_text;
- if (j == 0) {
- text.set(p_line, preinsert_text + substrings[j], structured_text_parser(st_parser, st_args, preinsert_text + substrings[j]));
- } else {
- text.insert(p_line + j, substrings[j], structured_text_parser(st_parser, st_args, substrings[j]));
- }
-
- if (j == substrings.size() - 1) {
- text.set(p_line + j, text[p_line + j] + postinsert_text, structured_text_parser(st_parser, st_args, text[p_line + j] + postinsert_text));
- }
+ Vector<Array> bidi_override;
+ bidi_override.resize(substrings.size());
+ for (int i = 0; i < substrings.size(); i++) {
+ bidi_override.write[i] = structured_text_parser(st_parser, st_args, substrings[i]);
}
+ text.insert(p_line, substrings, bidi_override);
+
if (shift_first_line) {
text.move_gutters(p_line, p_line + 1);
text.set_hidden(p_line + 1, text.is_hidden(p_line));
@@ -6150,7 +6659,7 @@ String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_lin
ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // 'from > to'.
ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // 'from > to'.
- String ret;
+ StringBuilder ret;
for (int i = p_from_line; i <= p_to_line; i++) {
int begin = (i == p_from_line) ? p_from_column : 0;
@@ -6162,7 +6671,7 @@ String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_lin
ret += text[i].substr(begin, end - begin);
}
- return ret;
+ return ret.as_string();
}
void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
@@ -6176,9 +6685,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
String pre_text = text[p_from_line].substr(0, p_from_column);
String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
- for (int i = p_from_line; i < p_to_line; i++) {
- text.remove(p_from_line + 1);
- }
+ text.remove_range(p_from_line, p_to_line);
text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text));
if (!text_changed_dirty && !setting_text) {
@@ -6190,11 +6697,14 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
emit_signal(SNAME("lines_edited_from"), p_to_line, p_from_line);
}
-TextEdit::TextEdit() {
+TextEdit::TextEdit(const String &p_placeholder) {
+ placeholder_data_buf.instantiate();
+
clear();
set_focus_mode(FOCUS_ALL);
_update_caches();
set_default_cursor_shape(CURSOR_IBEAM);
+ set_process_unhandled_key_input(true);
text.set_tab_size(text.get_tab_size());
@@ -6230,5 +6740,7 @@ TextEdit::TextEdit() {
undo_stack_max_size = GLOBAL_GET("gui/common/text_edit_undo_stack_max_size");
+ set_placeholder(p_placeholder);
+
set_editable(true);
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 09315ae395..993203bee6 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -70,7 +70,7 @@ public:
GUTTER_TYPE_CUSTOM
};
- /* Contex Menu. */
+ /* Context Menu. */
enum MenuItems {
MENU_CUT,
MENU_COPY,
@@ -120,8 +120,7 @@ private:
bool clickable = false;
bool overwritable = false;
- ObjectID custom_draw_obj = ObjectID();
- StringName custom_draw_callback;
+ Callable custom_draw_callback;
};
class Text {
@@ -159,6 +158,7 @@ private:
mutable Vector<Line> text;
Ref<Font> font;
int font_size = -1;
+ int font_height = 0;
Dictionary opentype_features;
String language;
@@ -189,6 +189,7 @@ private:
int get_max_width() const;
void set_width(float p_width);
+ float get_width() const;
int get_line_wrap_amount(int p_line) const;
Vector<Vector2i> get_line_wrap_ranges(int p_line) const;
@@ -204,12 +205,13 @@ private:
}
}
bool is_hidden(int p_line) const { return text[p_line].hidden; }
- void insert(int p_at, const String &p_text, const Array &p_bidi_override);
- void remove(int p_at);
+ void insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override);
+ void remove_range(int p_from_line, int p_to_line);
int size() const { return text.size(); }
void clear();
- void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
+ void invalidate_cache(int p_line, int p_column = -1, bool p_text_changed = false, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
+ void invalidate_font();
void invalidate_all();
void invalidate_all_lines();
@@ -245,10 +247,24 @@ private:
bool setting_text = false;
+ bool alt_start = false;
+ uint32_t alt_code = 0;
+
// Text properties.
String ime_text = "";
Point2 ime_selection;
+ // Placeholder
+ String placeholder_text = "";
+ Array placeholder_bidi_override;
+ Ref<TextParagraph> placeholder_data_buf;
+ int placeholder_line_height = -1;
+ int placeholder_max_width = -1;
+
+ Vector<String> placeholder_wraped_rows;
+
+ void _update_placeholder();
+
/* Initialise to opposite first, so we get past the early-out in set_editable. */
bool editable = false;
@@ -258,7 +274,7 @@ private:
Dictionary opentype_features;
String language = "";
- Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
Array st_args;
void _clear();
@@ -271,7 +287,7 @@ private:
bool virtual_keyboard_enabled = true;
bool middle_mouse_paste_enabled = true;
- // Overridable actions
+ // Overridable actions.
String cut_copy_line = "";
// Context menu.
@@ -312,7 +328,7 @@ private:
List<TextOperation> undo_stack;
List<TextOperation>::Element *undo_stack_pos = nullptr;
- Timer *idle_detect;
+ Timer *idle_detect = nullptr;
uint32_t version = 0;
uint32_t saved_version = 0;
@@ -331,11 +347,17 @@ private:
int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) const;
/* Tooltip. */
- ObjectID tooltip_obj_id;
- StringName tooltip_func;
- Variant tooltip_ud;
+ Callable tooltip_callback;
/* Mouse */
+ struct LineDrawingCache {
+ int y_offset = 0;
+ Vector<int> first_visible_chars;
+ Vector<int> last_visible_chars;
+ };
+
+ HashMap<int, LineDrawingCache> line_drawing_cache;
+
int _get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const;
/* Caret. */
@@ -361,11 +383,14 @@ private:
bool draw_caret = true;
bool caret_blink_enabled = false;
- Timer *caret_blink_timer;
+ Timer *caret_blink_timer = nullptr;
bool move_caret_on_right_click = true;
- bool caret_mid_grapheme_enabled = false;
+ bool caret_mid_grapheme_enabled = true;
+
+ bool drag_action = false;
+ bool drag_caret_force_displayed = false;
void _emit_caret_changed();
@@ -392,6 +417,7 @@ private:
int to_column = 0;
bool shiftclick_left = false;
+ bool drag_attempt = false;
} selection;
bool selecting_enabled = true;
@@ -403,7 +429,7 @@ private:
bool dragging_selection = false;
- Timer *click_select_held;
+ Timer *click_select_held = nullptr;
uint64_t last_dblclk = 0;
Vector2 last_dblclk_pos;
void _click_selection_held();
@@ -415,7 +441,7 @@ private:
void _pre_shift_selection();
void _post_shift_selection();
- /* line wrapping. */
+ /* Line wrapping. */
LineWrappingMode line_wrapping_mode = LineWrappingMode::LINE_WRAPPING_NONE;
int wrap_at_column = 0;
@@ -426,8 +452,8 @@ private:
void _update_caret_wrap_offset();
/* Viewport. */
- HScrollBar *h_scroll;
- VScrollBar *v_scroll;
+ HScrollBar *h_scroll = nullptr;
+ VScrollBar *v_scroll = nullptr;
bool scroll_past_end_of_file_enabled = false;
@@ -455,14 +481,14 @@ private:
void _scroll_lines_up();
void _scroll_lines_down();
- // Minimap
+ // Minimap.
bool draw_minimap = false;
int minimap_width = 80;
Point2 minimap_char_size = Point2(1, 2);
int minimap_line_spacing = 1;
- // minimap scroll
+ // Minimap scroll.
bool minimap_clicked = false;
bool hovering_minimap = false;
bool dragging_minimap = false;
@@ -485,7 +511,6 @@ private:
/* Syntax highlighting. */
Ref<SyntaxHighlighter> syntax_highlighter;
- Map<int, Dictionary> syntax_highlighting_cache;
Dictionary _get_line_syntax_highlighting(int p_line);
@@ -501,6 +526,7 @@ private:
int font_size = 16;
Color font_color = Color(1, 1, 1);
Color font_readonly_color = Color(1, 1, 1);
+ Color font_placeholder_color = Color(1, 1, 1, 0.6);
int outline_size = 0;
Color outline_color = Color(1, 1, 1);
@@ -599,12 +625,17 @@ protected:
public:
/* General overrides. */
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
virtual void gui_input(const Ref<InputEvent> &p_gui_input) override;
+ bool alt_input(const Ref<InputEvent> &p_gui_input);
virtual Size2 get_minimum_size() const override;
virtual bool is_text_field() const override;
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
+ virtual Variant get_drag_data(const Point2 &p_point) override;
+ virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
+ virtual void drop_data(const Point2 &p_point, const Variant &p_data) override;
virtual String get_tooltip(const Point2 &p_pos) const override;
- void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata);
+ void set_tooltip_request_func(const Callable &p_tooltip_callback);
/* Text */
// Text properties.
@@ -623,8 +654,8 @@ public:
void set_language(const String &p_language);
String get_language() const;
- void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
- Control::StructuredTextParser get_structured_text_bidi_override() const;
+ void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
+ TextServer::StructuredTextParser get_structured_text_bidi_override() const;
void set_structured_text_bidi_override_options(Array p_args);
Array get_structured_text_bidi_override_options() const;
@@ -654,6 +685,9 @@ public:
String get_text() const;
int get_line_count() const;
+ void set_placeholder(const String &p_text);
+ String get_placeholder() const;
+
void set_line(int p_line, const String &p_new_text);
String get_line(int p_line) const;
@@ -717,9 +751,13 @@ public:
String get_word_at_pos(const Vector2 &p_pos) const;
Point2i get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_of_bounds = true) const;
+ Point2i get_pos_at_line_column(int p_line, int p_column) const;
+ Rect2i get_rect_at_line_column(int p_line, int p_column) const;
+
int get_minimap_line_at_pos(const Point2i &p_pos) const;
bool is_dragging_cursor() const;
+ bool is_mouse_over_selection(bool p_edges = true) const;
/* Caret */
void set_caret_type(CaretType p_type);
@@ -782,7 +820,7 @@ public:
void deselect();
void delete_selection();
- /* line wrapping. */
+ /* Line wrapping. */
void set_line_wrapping_mode(LineWrappingMode p_wrapping_mode);
LineWrappingMode get_line_wrapping_mode() const;
@@ -864,7 +902,7 @@ public:
void merge_gutters(int p_from_line, int p_to_line);
- void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback);
+ void set_gutter_custom_draw(int p_gutter, const Callable &p_draw_callback);
// Line gutters.
void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata);
@@ -906,7 +944,7 @@ public:
void set_draw_spaces(bool p_enabled);
bool is_drawing_spaces() const;
- TextEdit();
+ TextEdit(const String &p_placeholder = String());
};
VARIANT_ENUM_CAST(TextEdit::CaretType);
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index 8659ea06a2..26acfaaa70 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,7 +37,7 @@
Size2 TextureButton::get_minimum_size() const {
Size2 rscale = Control::get_minimum_size();
- if (!expand) {
+ if (!ignore_texture_size) {
if (normal.is_null()) {
if (pressed.is_null()) {
if (hover.is_null()) {
@@ -170,55 +170,60 @@ void TextureButton::_notification(int p_what) {
Point2 ofs;
Size2 size;
+ bool draw_focus = (has_focus() && focused.is_valid());
+
+ // If no other texture is valid, try using focused texture.
+ bool draw_focus_only = draw_focus && !texdraw.is_valid();
+ if (draw_focus_only) {
+ texdraw = focused;
+ }
if (texdraw.is_valid()) {
size = texdraw->get_size();
_texture_region = Rect2(Point2(), texdraw->get_size());
_tile = false;
- if (expand) {
- switch (stretch_mode) {
- case STRETCH_KEEP:
- size = texdraw->get_size();
- break;
- case STRETCH_SCALE:
- size = get_size();
- break;
- case STRETCH_TILE:
- size = get_size();
- _tile = true;
- break;
- case STRETCH_KEEP_CENTERED:
- ofs = (get_size() - texdraw->get_size()) / 2;
- size = texdraw->get_size();
- break;
- case STRETCH_KEEP_ASPECT_CENTERED:
- case STRETCH_KEEP_ASPECT: {
- Size2 _size = get_size();
- float tex_width = texdraw->get_width() * _size.height / texdraw->get_height();
- float tex_height = _size.height;
-
- if (tex_width > _size.width) {
- tex_width = _size.width;
- tex_height = texdraw->get_height() * tex_width / texdraw->get_width();
- }
+ switch (stretch_mode) {
+ case STRETCH_KEEP:
+ size = texdraw->get_size();
+ break;
+ case STRETCH_SCALE:
+ size = get_size();
+ break;
+ case STRETCH_TILE:
+ size = get_size();
+ _tile = true;
+ break;
+ case STRETCH_KEEP_CENTERED:
+ ofs = (get_size() - texdraw->get_size()) / 2;
+ size = texdraw->get_size();
+ break;
+ case STRETCH_KEEP_ASPECT_CENTERED:
+ case STRETCH_KEEP_ASPECT: {
+ Size2 _size = get_size();
+ float tex_width = texdraw->get_width() * _size.height / texdraw->get_height();
+ float tex_height = _size.height;
+
+ if (tex_width > _size.width) {
+ tex_width = _size.width;
+ tex_height = texdraw->get_height() * tex_width / texdraw->get_width();
+ }
- if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
- ofs.x = (_size.width - tex_width) / 2;
- ofs.y = (_size.height - tex_height) / 2;
- }
- size.width = tex_width;
- size.height = tex_height;
- } break;
- case STRETCH_KEEP_ASPECT_COVERED: {
- size = get_size();
- Size2 tex_size = texdraw->get_size();
- Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
- float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
- Size2 scaled_tex_size = tex_size * scale;
- Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
- _texture_region = Rect2(ofs2, size / scale);
- } break;
- }
+ if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
+ ofs.x = (_size.width - tex_width) / 2;
+ ofs.y = (_size.height - tex_height) / 2;
+ }
+ size.width = tex_width;
+ size.height = tex_height;
+ } break;
+ case STRETCH_KEEP_ASPECT_COVERED: {
+ size = get_size();
+ Size2 tex_size = texdraw->get_size();
+ Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
+ float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
+ Size2 scaled_tex_size = tex_size * scale;
+ Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
+ _texture_region = Rect2(ofs2, size / scale);
+ } break;
}
_position_rect = Rect2(ofs, size);
@@ -226,7 +231,9 @@ void TextureButton::_notification(int p_what) {
size.width *= hflip ? -1.0f : 1.0f;
size.height *= vflip ? -1.0f : 1.0f;
- if (_tile) {
+ if (draw_focus_only) {
+ // Do nothing, we only needed to calculate the rectangle.
+ } else if (_tile) {
draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
} else {
draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region);
@@ -235,7 +242,7 @@ void TextureButton::_notification(int p_what) {
_position_rect = Rect2();
}
- if (has_focus() && focused.is_valid()) {
+ if (draw_focus) {
draw_texture_rect(focused, Rect2(ofs, size), false);
};
} break;
@@ -249,7 +256,7 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disabled_texture", "texture"), &TextureButton::set_disabled_texture);
ClassDB::bind_method(D_METHOD("set_focused_texture", "texture"), &TextureButton::set_focused_texture);
ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask);
- ClassDB::bind_method(D_METHOD("set_expand", "expand"), &TextureButton::set_expand);
+ ClassDB::bind_method(D_METHOD("set_ignore_texture_size", "ignore"), &TextureButton::set_ignore_texture_size);
ClassDB::bind_method(D_METHOD("set_stretch_mode", "mode"), &TextureButton::set_stretch_mode);
ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureButton::set_flip_h);
ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureButton::is_flipped_h);
@@ -262,7 +269,7 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_disabled_texture"), &TextureButton::get_disabled_texture);
ClassDB::bind_method(D_METHOD("get_focused_texture"), &TextureButton::get_focused_texture);
ClassDB::bind_method(D_METHOD("get_click_mask"), &TextureButton::get_click_mask);
- ClassDB::bind_method(D_METHOD("get_expand"), &TextureButton::get_expand);
+ ClassDB::bind_method(D_METHOD("get_ignore_texture_size"), &TextureButton::get_ignore_texture_size);
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureButton::get_stretch_mode);
ADD_GROUP("Textures", "texture_");
@@ -272,7 +279,7 @@ void TextureButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_disabled", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_disabled_texture", "get_disabled_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_focused", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_focused_texture", "get_focused_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_texture_size", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_ignore_texture_size", "get_ignore_texture_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_h", "is_flipped_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_v", "is_flipped_v");
@@ -289,19 +296,19 @@ void TextureButton::_bind_methods() {
void TextureButton::set_normal_texture(const Ref<Texture2D> &p_normal) {
normal = p_normal;
update();
- minimum_size_changed();
+ update_minimum_size();
}
void TextureButton::set_pressed_texture(const Ref<Texture2D> &p_pressed) {
pressed = p_pressed;
update();
- minimum_size_changed();
+ update_minimum_size();
}
void TextureButton::set_hover_texture(const Ref<Texture2D> &p_hover) {
hover = p_hover;
update();
- minimum_size_changed();
+ update_minimum_size();
}
void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) {
@@ -312,7 +319,7 @@ void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) {
void TextureButton::set_click_mask(const Ref<BitMap> &p_click_mask) {
click_mask = p_click_mask;
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TextureButton::get_normal_texture() const {
@@ -343,13 +350,13 @@ void TextureButton::set_focused_texture(const Ref<Texture2D> &p_focused) {
focused = p_focused;
};
-bool TextureButton::get_expand() const {
- return expand;
+bool TextureButton::get_ignore_texture_size() const {
+ return ignore_texture_size;
}
-void TextureButton::set_expand(bool p_expand) {
- expand = p_expand;
- minimum_size_changed();
+void TextureButton::set_ignore_texture_size(bool p_ignore) {
+ ignore_texture_size = p_ignore;
+ update_minimum_size();
update();
}
diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h
index 8361f3c341..5762949acd 100644
--- a/scene/gui/texture_button.h
+++ b/scene/gui/texture_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -54,8 +54,8 @@ private:
Ref<Texture2D> disabled;
Ref<Texture2D> focused;
Ref<BitMap> click_mask;
- bool expand = false;
- StretchMode stretch_mode = STRETCH_SCALE;
+ bool ignore_texture_size = false;
+ StretchMode stretch_mode = STRETCH_KEEP;
Rect2 _texture_region;
Rect2 _position_rect;
@@ -85,8 +85,8 @@ public:
Ref<Texture2D> get_focused_texture() const;
Ref<BitMap> get_click_mask() const;
- bool get_expand() const;
- void set_expand(bool p_expand);
+ bool get_ignore_texture_size() const;
+ void set_ignore_texture_size(bool p_ignore);
void set_stretch_mode(StretchMode p_stretch_mode);
StretchMode get_stretch_mode() const;
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index fe11de128a..f79c68671c 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,7 @@
void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) {
under = p_texture;
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TextureProgressBar::get_under_texture() const {
@@ -46,7 +46,7 @@ void TextureProgressBar::set_over_texture(const Ref<Texture2D> &p_texture) {
over = p_texture;
update();
if (under.is_null()) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -58,7 +58,7 @@ void TextureProgressBar::set_stretch_margin(Side p_side, int p_size) {
ERR_FAIL_INDEX((int)p_side, 4);
stretch_margin[p_side] = p_size;
update();
- minimum_size_changed();
+ update_minimum_size();
}
int TextureProgressBar::get_stretch_margin(Side p_side) const {
@@ -69,7 +69,7 @@ int TextureProgressBar::get_stretch_margin(Side p_side) const {
void TextureProgressBar::set_nine_patch_stretch(bool p_stretch) {
nine_patch_stretch = p_stretch;
update();
- minimum_size_changed();
+ update_minimum_size();
}
bool TextureProgressBar::get_nine_patch_stretch() const {
@@ -93,7 +93,7 @@ Size2 TextureProgressBar::get_minimum_size() const {
void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) {
progress = p_texture;
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TextureProgressBar::get_progress_texture() const {
@@ -387,7 +387,6 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu
}
void TextureProgressBar::_notification(int p_what) {
- const float corners[12] = { -0.125, -0.375, -0.625, -0.875, 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875 };
switch (p_what) {
case NOTIFICATION_DRAW: {
if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP || mode == FILL_BILINEAR_LEFT_AND_RIGHT || mode == FILL_BILINEAR_TOP_AND_BOTTOM)) {
@@ -452,7 +451,7 @@ void TextureProgressBar::_notification(int p_what) {
float val = get_as_ratio() * rad_max_degrees / 360;
if (val == 1) {
Rect2 region = Rect2(progress_offset, s);
- Rect2 source = Rect2(Point2(), s);
+ Rect2 source = Rect2(Point2(), progress->get_size());
draw_texture_rect_region(progress, region, source, tint_progress);
} else if (val != 0) {
Array pts;
@@ -466,16 +465,14 @@ void TextureProgressBar::_notification(int p_what) {
}
float end = start + direction * val;
- pts.append(start);
- pts.append(end);
float from = MIN(start, end);
float to = MAX(start, end);
- for (int i = 0; i < 12; i++) {
- if (corners[i] > from && corners[i] < to) {
- pts.append(corners[i]);
- }
+ pts.append(from);
+ for (float corner = Math::floor(from * 4 + 0.5) * 0.25 + 0.125; corner < to; corner += 0.25) {
+ pts.append(corner);
}
- pts.sort();
+ pts.append(to);
+
Vector<Point2> uvs;
Vector<Point2> points;
uvs.push_back(get_relative_center());
@@ -492,6 +489,8 @@ void TextureProgressBar::_notification(int p_what) {
colors.push_back(tint_progress);
draw_polygon(points, colors, uvs, progress);
}
+
+ // Draw a reference cross.
if (Engine::get_singleton()->is_editor_hint()) {
Point2 p;
@@ -502,6 +501,7 @@ void TextureProgressBar::_notification(int p_what) {
}
p *= get_relative_center();
+ p += progress_offset;
p = p.floor();
draw_line(p - Point2(8, 0), p + Point2(8, 0), Color(0.9, 0.5, 0.5), 2);
draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2);
@@ -629,26 +629,30 @@ void TextureProgressBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_nine_patch_stretch", "stretch"), &TextureProgressBar::set_nine_patch_stretch);
ClassDB::bind_method(D_METHOD("get_nine_patch_stretch"), &TextureProgressBar::get_nine_patch_stretch);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch");
+
+ ADD_GROUP("Stretch Margin", "stretch_margin_");
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_BOTTOM);
+
ADD_GROUP("Textures", "texture_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_under_texture", "get_under_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_over_texture", "get_over_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_progress_texture", "get_progress_texture");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_progress_offset"), "set_texture_progress_offset", "get_texture_progress_offset");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode");
+
ADD_GROUP("Tint", "tint_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over"), "set_tint_over", "get_tint_over");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress"), "set_tint_progress", "get_tint_progress");
+
ADD_GROUP("Radial Fill", "radial_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "radial_center_offset"), "set_radial_center_offset", "get_radial_center_offset");
- ADD_GROUP("Stretch", "stretch_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_BOTTOM);
BIND_ENUM_CONSTANT(FILL_LEFT_TO_RIGHT);
BIND_ENUM_CONSTANT(FILL_RIGHT_TO_LEFT);
diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h
index c508f41387..4d3e38e006 100644
--- a/scene/gui/texture_progress_bar.h
+++ b/scene/gui/texture_progress_bar.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp
index 1cba88e06f..ecdf55caf0 100644
--- a/scene/gui/texture_rect.cpp
+++ b/scene/gui/texture_rect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,92 +29,92 @@
/*************************************************************************/
#include "texture_rect.h"
+
#include "core/core_string_names.h"
#include "servers/rendering_server.h"
void TextureRect::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- if (texture.is_null()) {
- return;
- }
-
- Size2 size;
- Point2 offset;
- Rect2 region;
- bool tile = false;
-
- switch (stretch_mode) {
- case STRETCH_SCALE_ON_EXPAND: {
- size = expand ? get_size() : texture->get_size();
- } break;
- case STRETCH_SCALE: {
- size = get_size();
- } break;
- case STRETCH_TILE: {
- size = get_size();
- tile = true;
- } break;
- case STRETCH_KEEP: {
- size = texture->get_size();
- } break;
- case STRETCH_KEEP_CENTERED: {
- offset = (get_size() - texture->get_size()) / 2;
- size = texture->get_size();
- } break;
- case STRETCH_KEEP_ASPECT_CENTERED:
- case STRETCH_KEEP_ASPECT: {
- size = get_size();
- int tex_width = texture->get_width() * size.height / texture->get_height();
- int tex_height = size.height;
-
- if (tex_width > size.width) {
- tex_width = size.width;
- tex_height = texture->get_height() * tex_width / texture->get_width();
- }
-
- if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
- offset.x += (size.width - tex_width) / 2;
- offset.y += (size.height - tex_height) / 2;
- }
-
- size.width = tex_width;
- size.height = tex_height;
- } break;
- case STRETCH_KEEP_ASPECT_COVERED: {
- size = get_size();
-
- Size2 tex_size = texture->get_size();
- Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
- float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
- Size2 scaled_tex_size = tex_size * scale;
-
- region.position = ((scaled_tex_size - size) / scale).abs() / 2.0f;
- region.size = size / scale;
- } break;
- }
-
- Ref<AtlasTexture> p_atlas = texture;
-
- if (p_atlas.is_valid() && region.has_no_area()) {
- Size2 scale_size(size.width / texture->get_width(), size.height / texture->get_height());
-
- offset.width += hflip ? p_atlas->get_margin().get_position().width * scale_size.width * 2 : 0;
- offset.height += vflip ? p_atlas->get_margin().get_position().height * scale_size.height * 2 : 0;
- }
-
- size.width *= hflip ? -1.0f : 1.0f;
- size.height *= vflip ? -1.0f : 1.0f;
-
- if (region.has_no_area()) {
- draw_texture_rect(texture, Rect2(offset, size), tile);
- } else {
- draw_texture_rect_region(texture, Rect2(offset, size), region);
- }
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ if (texture.is_null()) {
+ return;
+ }
+
+ Size2 size;
+ Point2 offset;
+ Rect2 region;
+ bool tile = false;
+
+ switch (stretch_mode) {
+ case STRETCH_SCALE: {
+ size = get_size();
+ } break;
+ case STRETCH_TILE: {
+ size = get_size();
+ tile = true;
+ } break;
+ case STRETCH_KEEP: {
+ size = texture->get_size();
+ } break;
+ case STRETCH_KEEP_CENTERED: {
+ offset = (get_size() - texture->get_size()) / 2;
+ size = texture->get_size();
+ } break;
+ case STRETCH_KEEP_ASPECT_CENTERED:
+ case STRETCH_KEEP_ASPECT: {
+ size = get_size();
+ int tex_width = texture->get_width() * size.height / texture->get_height();
+ int tex_height = size.height;
+
+ if (tex_width > size.width) {
+ tex_width = size.width;
+ tex_height = texture->get_height() * tex_width / texture->get_width();
+ }
+
+ if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
+ offset.x += (size.width - tex_width) / 2;
+ offset.y += (size.height - tex_height) / 2;
+ }
+
+ size.width = tex_width;
+ size.height = tex_height;
+ } break;
+ case STRETCH_KEEP_ASPECT_COVERED: {
+ size = get_size();
+
+ Size2 tex_size = texture->get_size();
+ Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
+ float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
+ Size2 scaled_tex_size = tex_size * scale;
+
+ region.position = ((scaled_tex_size - size) / scale).abs() / 2.0f;
+ region.size = size / scale;
+ } break;
+ }
+
+ Ref<AtlasTexture> p_atlas = texture;
+
+ if (p_atlas.is_valid() && region.has_no_area()) {
+ Size2 scale_size(size.width / texture->get_width(), size.height / texture->get_height());
+
+ offset.width += hflip ? p_atlas->get_margin().get_position().width * scale_size.width * 2 : 0;
+ offset.height += vflip ? p_atlas->get_margin().get_position().height * scale_size.height * 2 : 0;
+ }
+
+ size.width *= hflip ? -1.0f : 1.0f;
+ size.height *= vflip ? -1.0f : 1.0f;
+
+ if (region.has_no_area()) {
+ draw_texture_rect(texture, Rect2(offset, size), tile);
+ } else {
+ draw_texture_rect_region(texture, Rect2(offset, size), region);
+ }
+ } break;
}
}
Size2 TextureRect::get_minimum_size() const {
- if (!expand && !texture.is_null()) {
+ if (!ignore_texture_size && !texture.is_null()) {
return texture->get_size();
} else {
return Size2();
@@ -124,8 +124,8 @@ Size2 TextureRect::get_minimum_size() const {
void TextureRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TextureRect::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &TextureRect::get_texture);
- ClassDB::bind_method(D_METHOD("set_expand", "enable"), &TextureRect::set_expand);
- ClassDB::bind_method(D_METHOD("has_expand"), &TextureRect::has_expand);
+ ClassDB::bind_method(D_METHOD("set_ignore_texture_size", "ignore"), &TextureRect::set_ignore_texture_size);
+ ClassDB::bind_method(D_METHOD("get_ignore_texture_size"), &TextureRect::get_ignore_texture_size);
ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureRect::set_flip_h);
ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureRect::is_flipped_h);
ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureRect::set_flip_v);
@@ -134,12 +134,11 @@ void TextureRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureRect::get_stretch_mode);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale On Expand (Compat),Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_texture_size"), "set_ignore_texture_size", "get_ignore_texture_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
- BIND_ENUM_CONSTANT(STRETCH_SCALE_ON_EXPAND);
BIND_ENUM_CONSTANT(STRETCH_SCALE);
BIND_ENUM_CONSTANT(STRETCH_TILE);
BIND_ENUM_CONSTANT(STRETCH_KEEP);
@@ -152,7 +151,7 @@ void TextureRect::_bind_methods() {
void TextureRect::_texture_changed() {
if (texture.is_valid()) {
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -172,21 +171,21 @@ void TextureRect::set_texture(const Ref<Texture2D> &p_tex) {
}
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TextureRect::get_texture() const {
return texture;
}
-void TextureRect::set_expand(bool p_expand) {
- expand = p_expand;
+void TextureRect::set_ignore_texture_size(bool p_ignore) {
+ ignore_texture_size = p_ignore;
update();
- minimum_size_changed();
+ update_minimum_size();
}
-bool TextureRect::has_expand() const {
- return expand;
+bool TextureRect::get_ignore_texture_size() const {
+ return ignore_texture_size;
}
void TextureRect::set_stretch_mode(StretchMode p_mode) {
diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h
index 0f93d5732f..7d667b25a8 100644
--- a/scene/gui/texture_rect.h
+++ b/scene/gui/texture_rect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,7 +38,6 @@ class TextureRect : public Control {
public:
enum StretchMode {
- STRETCH_SCALE_ON_EXPAND, //default, for backwards compatibility
STRETCH_SCALE,
STRETCH_TILE,
STRETCH_KEEP,
@@ -49,11 +48,11 @@ public:
};
private:
- bool expand = false;
+ bool ignore_texture_size = false;
bool hflip = false;
bool vflip = false;
Ref<Texture2D> texture;
- StretchMode stretch_mode = STRETCH_SCALE_ON_EXPAND;
+ StretchMode stretch_mode = STRETCH_SCALE;
void _texture_changed();
@@ -66,8 +65,8 @@ public:
void set_texture(const Ref<Texture2D> &p_tex);
Ref<Texture2D> get_texture() const;
- void set_expand(bool p_expand);
- bool has_expand() const;
+ void set_ignore_texture_size(bool p_ignore);
+ bool get_ignore_texture_size() const;
void set_stretch_mode(StretchMode p_mode);
StretchMode get_stretch_mode() const;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 27e617bdd0..0ca9a66e08 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,10 +37,9 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
+#include "scene/gui/box_container.h"
#include "scene/main/window.h"
-#include "box_container.h"
-
#include <limits.h>
Size2 TreeItem::Cell::get_icon_size() const {
@@ -102,6 +101,7 @@ void TreeItem::_change_tree(Tree *p_tree) {
if (tree->popup_edited_item == this) {
tree->popup_edited_item = nullptr;
+ tree->popup_pressing_edited_item = nullptr;
tree->pressing_for_editor = false;
}
@@ -198,6 +198,65 @@ bool TreeItem::is_indeterminate(int p_column) const {
return cells[p_column].indeterminate;
}
+void TreeItem::propagate_check(int p_column, bool p_emit_signal) {
+ bool ch = cells[p_column].checked;
+
+ if (p_emit_signal) {
+ tree->emit_signal(SNAME("check_propagated_to_item"), this, p_column);
+ }
+ _propagate_check_through_children(p_column, ch, p_emit_signal);
+ _propagate_check_through_parents(p_column, p_emit_signal);
+}
+
+void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal) {
+ TreeItem *current = get_first_child();
+ while (current) {
+ current->set_checked(p_column, p_checked);
+ if (p_emit_signal) {
+ current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column);
+ }
+ current->_propagate_check_through_children(p_column, p_checked, p_emit_signal);
+ current = current->get_next();
+ }
+}
+
+void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal) {
+ TreeItem *current = get_parent();
+ if (!current) {
+ return;
+ }
+
+ bool all_unchecked_and_not_indeterminate = true;
+ bool any_unchecked_or_indeterminate = false;
+
+ TreeItem *child_item = current->get_first_child();
+ while (child_item) {
+ if (!child_item->is_checked(p_column)) {
+ any_unchecked_or_indeterminate = true;
+ if (child_item->is_indeterminate(p_column)) {
+ all_unchecked_and_not_indeterminate = false;
+ break;
+ }
+ } else {
+ all_unchecked_and_not_indeterminate = false;
+ }
+ child_item = child_item->get_next();
+ }
+
+ if (all_unchecked_and_not_indeterminate) {
+ current->set_checked(p_column, false);
+ } else if (any_unchecked_or_indeterminate) {
+ current->set_indeterminate(p_column, true);
+ } else {
+ current->set_checked(p_column, true);
+ }
+
+ if (p_emit_signal) {
+ current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column);
+ }
+ current->_propagate_check_through_parents(p_column, p_emit_signal);
+}
+
void TreeItem::set_text(int p_column, String p_text) {
ERR_FAIL_INDEX(p_column, cells.size());
cells.write[p_column].text = p_text;
@@ -275,7 +334,7 @@ int TreeItem::get_opentype_feature(int p_column, const String &p_name) const {
return cells[p_column].opentype_features[tag];
}
-void TreeItem::set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser) {
+void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser) {
ERR_FAIL_INDEX(p_column, cells.size());
if (cells[p_column].st_parser != p_parser) {
@@ -287,8 +346,8 @@ void TreeItem::set_structured_text_bidi_override(int p_column, Control::Structur
}
}
-Control::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const {
- ERR_FAIL_INDEX_V(p_column, cells.size(), Control::STRUCTURED_TEXT_NONE);
+TextServer::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_NONE);
return cells[p_column].st_parser;
}
@@ -496,8 +555,9 @@ void TreeItem::uncollapse_tree() {
void TreeItem::set_custom_minimum_height(int p_height) {
custom_min_height = p_height;
- for (Cell &c : cells)
+ for (Cell &c : cells) {
c.cached_minimum_size_dirty = true;
+ }
_changed_notify();
}
@@ -845,10 +905,16 @@ String TreeItem::get_button_tooltip(int p_column, int p_idx) const {
return cells[p_column].buttons[p_idx].tooltip;
}
+int TreeItem::get_button_id(int p_column, int p_idx) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
+ ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), -1);
+ return cells[p_column].buttons[p_idx].id;
+}
+
void TreeItem::erase_button(int p_column, int p_idx) {
ERR_FAIL_INDEX(p_column, cells.size());
ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
- cells.write[p_column].buttons.remove(p_idx);
+ cells.write[p_column].buttons.remove_at(p_idx);
_changed_notify(p_column);
}
@@ -1002,18 +1068,18 @@ bool TreeItem::is_custom_set_as_button(int p_column) const {
return cells[p_column].custom_button;
}
-void TreeItem::set_text_align(int p_column, TextAlign p_align) {
+void TreeItem::set_text_alignment(int p_column, HorizontalAlignment p_alignment) {
ERR_FAIL_INDEX(p_column, cells.size());
- cells.write[p_column].text_align = p_align;
+ cells.write[p_column].text_alignment = p_alignment;
cells.write[p_column].cached_minimum_size_dirty = true;
_changed_notify(p_column);
}
-TreeItem::TextAlign TreeItem::get_text_align(int p_column) const {
- ERR_FAIL_INDEX_V(p_column, cells.size(), ALIGN_LEFT);
- return cells[p_column].text_align;
+HorizontalAlignment TreeItem::get_text_alignment(int p_column) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), HORIZONTAL_ALIGNMENT_LEFT);
+ return cells[p_column].text_alignment;
}
void TreeItem::set_expand_right(int p_column, bool p_enable) {
@@ -1033,8 +1099,9 @@ bool TreeItem::get_expand_right(int p_column) const {
void TreeItem::set_disable_folding(bool p_disable) {
disable_folding = p_disable;
- for (Cell &c : cells)
+ for (Cell &c : cells) {
c.cached_minimum_size_dirty = true;
+ }
_changed_notify(0);
}
@@ -1096,31 +1163,30 @@ Size2 TreeItem::get_minimum_size(int p_column) {
return cell.cached_minimum_size;
}
-Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void TreeItem::_call_recursive_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 = 0;
- return Variant();
+ return;
}
if (p_args[0]->get_type() != Variant::STRING && p_args[0]->get_type() != Variant::STRING_NAME) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- return Variant();
+ return;
}
StringName method = *p_args[0];
call_recursive(method, &p_args[1], p_argcount - 1, r_error);
- return Variant();
}
void recursive_call_aux(TreeItem *p_item, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (!p_item) {
return;
}
- p_item->call(p_method, p_args, p_argcount, r_error);
+ p_item->callp(p_method, p_args, p_argcount, r_error);
TreeItem *c = p_item->get_first_child();
while (c) {
recursive_call_aux(c, p_method, p_args, p_argcount, r_error);
@@ -1141,6 +1207,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_checked", "column"), &TreeItem::is_checked);
ClassDB::bind_method(D_METHOD("is_indeterminate", "column"), &TreeItem::is_indeterminate);
+ ClassDB::bind_method(D_METHOD("propagate_check", "column", "emit_signal"), &TreeItem::propagate_check, DEFVAL(true));
+
ClassDB::bind_method(D_METHOD("set_text", "column", "text"), &TreeItem::set_text);
ClassDB::bind_method(D_METHOD("get_text", "column"), &TreeItem::get_text);
@@ -1220,9 +1288,11 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_as_button", "column", "enable"), &TreeItem::set_custom_as_button);
ClassDB::bind_method(D_METHOD("is_custom_set_as_button", "column"), &TreeItem::is_custom_set_as_button);
- ClassDB::bind_method(D_METHOD("add_button", "column", "button", "button_idx", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count);
ClassDB::bind_method(D_METHOD("get_button_tooltip", "column", "button_idx"), &TreeItem::get_button_tooltip);
+ ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_idx"), &TreeItem::get_button_id);
+ ClassDB::bind_method(D_METHOD("get_button_by_id", "column", "id"), &TreeItem::get_button_by_id);
ClassDB::bind_method(D_METHOD("get_button", "column", "button_idx"), &TreeItem::get_button);
ClassDB::bind_method(D_METHOD("set_button", "column", "button_idx", "button"), &TreeItem::set_button);
ClassDB::bind_method(D_METHOD("erase_button", "column", "button_idx"), &TreeItem::erase_button);
@@ -1231,8 +1301,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tooltip", "column", "tooltip"), &TreeItem::set_tooltip);
ClassDB::bind_method(D_METHOD("get_tooltip", "column"), &TreeItem::get_tooltip);
- ClassDB::bind_method(D_METHOD("set_text_align", "column", "text_align"), &TreeItem::set_text_align);
- ClassDB::bind_method(D_METHOD("get_text_align", "column"), &TreeItem::get_text_align);
+ ClassDB::bind_method(D_METHOD("set_text_alignment", "column", "text_alignment"), &TreeItem::set_text_alignment);
+ ClassDB::bind_method(D_METHOD("get_text_alignment", "column"), &TreeItem::get_text_alignment);
ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right);
ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right);
@@ -1256,10 +1326,10 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index);
- ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::_move_before);
- ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::_move_after);
+ ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::move_before);
+ ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::move_after);
- ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
+ ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::remove_child);
{
MethodInfo mi;
@@ -1278,10 +1348,6 @@ void TreeItem::_bind_methods() {
BIND_ENUM_CONSTANT(CELL_MODE_RANGE);
BIND_ENUM_CONSTANT(CELL_MODE_ICON);
BIND_ENUM_CONSTANT(CELL_MODE_CUSTOM);
-
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
}
void TreeItem::clear_children() {
@@ -1346,8 +1412,8 @@ void Tree::update_cache() {
cache.font_color = get_theme_color(SNAME("font_color"));
cache.font_selected_color = get_theme_color(SNAME("font_selected_color"));
cache.drop_position_color = get_theme_color(SNAME("drop_position_color"));
- cache.hseparation = get_theme_constant(SNAME("hseparation"));
- cache.vseparation = get_theme_constant(SNAME("vseparation"));
+ cache.hseparation = get_theme_constant(SNAME("h_separation"));
+ cache.vseparation = get_theme_constant(SNAME("v_separation"));
cache.item_margin = get_theme_constant(SNAME("item_margin"));
cache.button_margin = get_theme_constant(SNAME("button_margin"));
@@ -1477,16 +1543,17 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
}
w += ts.width;
- switch (p_cell.text_align) {
- case TreeItem::ALIGN_LEFT:
+ switch (p_cell.text_alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
rect.position.x += MAX(0, (rect.size.width - w));
}
- break;
- case TreeItem::ALIGN_CENTER:
+ } break;
+ case HORIZONTAL_ALIGNMENT_CENTER:
rect.position.x += MAX(0, (rect.size.width - w) / 2);
break;
- case TreeItem::ALIGN_RIGHT:
+ case HORIZONTAL_ALIGNMENT_RIGHT:
if (!rtl) {
rect.position.x += MAX(0, (rect.size.width - w));
}
@@ -1539,7 +1606,7 @@ void Tree::update_column(int p_col) {
columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction);
}
- columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].opentype_features, (columns[p_col].language != "") ? columns[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
+ columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].opentype_features, !columns[p_col].language.is_empty() ? columns[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
}
void Tree::update_item_cell(TreeItem *p_item, int p_col) {
@@ -1547,7 +1614,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
p_item->cells.write[p_col].text_buf->clear();
if (p_item->cells[p_col].mode == TreeItem::CELL_MODE_RANGE) {
- if (p_item->cells[p_col].text != "") {
+ if (!p_item->cells[p_col].text.is_empty()) {
if (!p_item->cells[p_col].editable) {
return;
}
@@ -1574,7 +1641,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
valtext = p_item->cells[p_col].text;
}
- if (p_item->cells[p_col].suffix != String()) {
+ if (!p_item->cells[p_col].suffix.is_empty()) {
valtext += " " + p_item->cells[p_col].suffix;
}
@@ -1597,7 +1664,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
} else {
font_size = cache.font_size;
}
- p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].opentype_features, (p_item->cells[p_col].language != "") ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
+ p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].opentype_features, !p_item->cells[p_col].language.is_empty() ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext));
p_item->cells.write[p_col].dirty = false;
}
@@ -1634,8 +1701,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
bool skip = (p_item == root && hide_root);
if (!skip && (p_pos.y + label_h - cache.offset.y) > 0) {
- //draw separation.
- //if (p_item->get_parent()!=root || !hide_root)
+ // Draw separation.
ERR_FAIL_COND_V(cache.font.is_null(), -1);
@@ -1663,7 +1729,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (p_item->cells[i].expand_right) {
int plus = 1;
- while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text == "" && p_item->cells[i + plus].icon.is_null()) {
+ while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
w += get_column_width(i + plus);
plus++;
skip2++;
@@ -1688,19 +1754,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
Ref<Texture2D> b = p_item->cells[i].buttons[j].texture;
Size2 s = b->get_size() + cache.button_pressed->get_minimum_size();
- if (s.height < label_h) {
- s.height = label_h;
- }
Point2i o = Point2i(ofs + w - s.width, p_pos.y) - cache.offset + p_draw_ofs;
if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) {
- //being pressed
+ // Being pressed.
Point2 od = o;
if (rtl) {
od.x = get_size().width - od.x - s.x;
}
- cache.button_pressed->draw(get_canvas_item(), Rect2(od, s));
+ cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, s.width, MAX(s.height, label_h)));
}
o.y += (label_h - s.height) / 2;
@@ -1860,7 +1923,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
} break;
case TreeItem::CELL_MODE_RANGE: {
- if (p_item->cells[i].text != "") {
+ if (!p_item->cells[i].text.is_empty()) {
if (!p_item->cells[i].editable) {
break;
}
@@ -2023,7 +2086,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
if (!p_item->collapsed) { /* if not collapsed, check the children */
-
TreeItem *c = p_item->first_child;
int base_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y;
@@ -2031,82 +2093,97 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
int prev_hl_ofs = base_ofs;
while (c) {
+ int child_h = -1;
if (htotal >= 0) {
- int child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c);
+ child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c);
+ }
- // Draw relationship lines.
- if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root)) {
- int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin);
- int parent_ofs = p_pos.x + cache.item_margin;
- Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs;
+ // Draw relationship lines.
+ if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root)) {
+ int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin);
+ int parent_ofs = p_pos.x + cache.item_margin;
+ Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs;
- if (c->get_first_child() != nullptr) {
- root_pos -= Point2i(cache.arrow->get_width(), 0);
- }
+ if (c->get_first_child() != nullptr) {
+ root_pos -= Point2i(cache.arrow->get_width(), 0);
+ }
- float line_width = cache.relationship_line_width * Math::round(cache.base_scale);
- float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale);
- float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale);
+ float line_width = cache.relationship_line_width * Math::round(cache.base_scale);
+ float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale);
+ float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale);
- Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs;
+ Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs;
- int more_prev_ofs = 0;
+ int more_prev_ofs = 0;
- if (root_pos.y + line_width >= 0) {
- if (rtl) {
- root_pos.x = get_size().width - root_pos.x;
- parent_pos.x = get_size().width - parent_pos.x;
- }
+ if (root_pos.y + line_width >= 0) {
+ if (rtl) {
+ root_pos.x = get_size().width - root_pos.x;
+ parent_pos.x = get_size().width - parent_pos.x;
+ }
- // Order of parts on this bend: the horizontal line first, then the vertical line.
- if (_is_branch_selected(c)) {
- // If this item or one of its children is selected, we draw the line using parent highlight style.
+ // Order of parts on this bend: the horizontal line first, then the vertical line.
+ if (_is_branch_selected(c)) {
+ // If this item or one of its children is selected, we draw the line using parent highlight style.
+ if (htotal >= 0) {
RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.parent_hl_line_color, parent_line_width);
+ }
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width);
+
+ more_prev_ofs = cache.parent_hl_line_margin;
+ prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
+ } else if (p_item->is_selected(0)) {
+ // If parent item is selected (but this item is not), we draw the line using children highlight style.
+ // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted.
+ if (_is_sibling_branch_selected(c)) {
+ if (htotal >= 0) {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width);
+ }
RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width);
- more_prev_ofs = cache.parent_hl_line_margin;
prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
- } else if (p_item->is_selected(0)) {
- // If parent item is selected (but this item is not), we draw the line using children highlight style.
- // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted.
- if (_is_sibling_branch_selected(c)) {
- RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width);
- RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width);
-
- prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
- } else {
+ } else {
+ if (htotal >= 0) {
RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width);
- RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width);
}
- } else {
- // If nothing of the above is true, we draw the line using normal style.
- // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted.
- if (_is_sibling_branch_selected(c)) {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width);
+ }
+ } else {
+ // If nothing of the above is true, we draw the line using normal style.
+ // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted.
+ if (_is_sibling_branch_selected(c)) {
+ if (htotal >= 0) {
RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + cache.parent_hl_line_margin, root_pos.y), cache.relationship_line_color, line_width);
- RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width);
+ }
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width);
- prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
- } else {
+ prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
+ } else {
+ if (htotal >= 0) {
RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width);
- RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width);
}
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width);
}
}
-
- prev_ofs = root_pos.y + more_prev_ofs;
}
- if (child_h < 0) {
- if (cache.draw_relationship_lines == 0) {
- return -1; // break, stop drawing, no need to anymore
- }
+ prev_ofs = root_pos.y + more_prev_ofs;
+ }
- htotal = -1;
- children_pos.y = cache.offset.y + p_draw_size.height;
- } else {
- htotal += child_h;
- children_pos.y += child_h;
+ if (child_h < 0) {
+ if (htotal == -1) {
+ break; // Last loop done, stop.
}
+
+ if (cache.draw_relationship_lines == 0) {
+ return -1; // No need to draw anymore, full stop.
+ }
+
+ htotal = -1;
+ children_pos.y = cache.offset.y + p_draw_size.height;
+ } else {
+ htotal += child_h;
+ children_pos.y += child_h;
}
c = c->next;
@@ -2192,14 +2269,11 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c
emit_signal(SNAME("item_selected"));
emitted_row = true;
}
- /*
- if (p_col==i)
- p_current->selected_signal.call(p_col);
- */
-
} else if (c.selected) {
- c.selected = false;
- //p_current->deselected_signal.call(p_col);
+ if (p_selected != p_current) {
+ // Deselect other rows.
+ c.selected = false;
+ }
}
} else if (select_mode == SELECT_SINGLE || select_mode == SELECT_MULTI) {
if (!r_in_range && &selected_cell == &c) {
@@ -2319,12 +2393,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
return -1;
}
- if (!p_item->disable_folding && !hide_folding && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) {
- if (p_item->first_child) {
- p_item->set_collapsed(!p_item->is_collapsed());
- }
-
- return -1; //handled!
+ if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) {
+ p_item->set_collapsed(!p_item->is_collapsed());
+ return -1;
}
int x = p_pos.x;
@@ -2340,7 +2411,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
if (p_item->cells[i].expand_right) {
int plus = 1;
- while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text == "" && p_item->cells[i + plus].icon.is_null()) {
+ while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
col_width += cache.hseparation;
col_width += get_column_width(i + plus);
plus++;
@@ -2431,7 +2502,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
/* process selection */
if (p_double_click && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check
-
+ // Emits the "item_activated" signal.
propagate_mouse_activated = true;
incr_search.clear();
@@ -2523,7 +2594,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
} break;
case TreeItem::CELL_MODE_RANGE: {
- if (c.text != "") {
+ if (!c.text.is_empty()) {
//if (x >= (get_column_width(col)-item_h/2)) {
popup_menu->clear();
for (int i = 0; i < c.text.get_slice_count(","); i++) {
@@ -2532,7 +2603,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
}
popup_menu->set_size(Size2(col_width, 0));
- popup_menu->set_position(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - cache.offset);
+ popup_menu->set_position(get_screen_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - cache.offset);
popup_menu->popup();
popup_edited_item = p_item;
popup_edited_item_col = col;
@@ -2611,8 +2682,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
}
click_handled = true;
- popup_edited_item = p_item;
- popup_edited_item_col = col;
+ popup_pressing_edited_item = p_item;
+ popup_pressing_edited_item_column = col;
pressing_item_rect = Rect2(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs) - cache.offset, Size2(col_width, item_h));
pressing_for_editor_text = editor_text;
@@ -3147,10 +3218,16 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
update();
}
- if (pressing_for_editor && popup_edited_item && (popup_edited_item->get_cell_mode(popup_edited_item_col) == TreeItem::CELL_MODE_RANGE)) {
- //range drag
+ if (pressing_for_editor && popup_pressing_edited_item && (popup_pressing_edited_item->get_cell_mode(popup_pressing_edited_item_column) == TreeItem::CELL_MODE_RANGE)) {
+ /* This needs to happen now, because the popup can be closed when pressing another item, and must remain the popup edited item until it actually closes */
+ popup_edited_item = popup_pressing_edited_item;
+ popup_edited_item_col = popup_pressing_edited_item_column;
+
+ popup_pressing_edited_item = nullptr;
+ popup_pressing_edited_item_column = -1;
if (!range_drag_enabled) {
+ //range drag
Vector2 cpos = mm->get_position();
if (rtl) {
cpos.x = get_size().width - cpos.x;
@@ -3175,7 +3252,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
if (drag_touching && !drag_touching_deaccel) {
drag_accum -= mm->get_relative().y;
v_scroll->set_value(drag_from + drag_accum);
- drag_speed = -mm->get_speed().y;
+ drag_speed = -mm->get_velocity().y;
}
}
@@ -3226,8 +3303,21 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
} else {
Rect2 rect = get_selected()->get_meta("__focus_rect");
Point2 mpos = b->get_position();
+ int icon_size_x = 0;
+ Ref<Texture2D> icon = get_selected()->get_icon(selected_col);
+ if (icon.is_valid()) {
+ Rect2i icon_region = get_selected()->get_icon_region(selected_col);
+ if (icon_region == Rect2i()) {
+ icon_size_x = icon->get_width();
+ } else {
+ icon_size_x = icon_region.size.width;
+ }
+ }
+ // Icon is treated as if it is outside of the rect so that double clicking on it will emit the item_double_clicked signal.
if (rtl) {
- mpos.x = get_size().width - mpos.x;
+ mpos.x = get_size().width - (mpos.x + icon_size_x);
+ } else {
+ mpos.x -= icon_size_x;
}
if (rect.has_point(mpos)) {
if (!edit_selected()) {
@@ -3433,7 +3523,7 @@ bool Tree::edit_selected() {
item_edited(col, s);
return true;
- } else if (c.mode == TreeItem::CELL_MODE_RANGE && c.text != "") {
+ } else if (c.mode == TreeItem::CELL_MODE_RANGE && !c.text.is_empty()) {
popup_menu->clear();
for (int i = 0; i < c.text.get_slice_count(","); i++) {
String s2 = c.text.get_slicec(',', i);
@@ -3441,7 +3531,7 @@ bool Tree::edit_selected() {
}
popup_menu->set_size(Size2(rect.size.width, 0));
- popup_menu->set_position(get_global_position() + rect.position + Point2i(0, rect.size.height));
+ popup_menu->set_position(get_screen_position() + rect.position + Point2i(0, rect.size.height));
popup_menu->popup();
popup_edited_item = s;
popup_edited_item_col = col;
@@ -3569,178 +3659,187 @@ int Tree::_get_title_button_height() const {
}
void Tree::_notification(int p_what) {
- if (p_what == NOTIFICATION_FOCUS_ENTER) {
- if (get_viewport()) {
- focus_in_id = get_viewport()->get_processed_events_count();
- }
- }
- if (p_what == NOTIFICATION_MOUSE_EXIT) {
- if (cache.hover_type != Cache::CLICK_NONE) {
- cache.hover_type = Cache::CLICK_NONE;
- update();
- }
- }
-
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- drag_touching = false;
- }
+ switch (p_what) {
+ case NOTIFICATION_FOCUS_ENTER: {
+ if (get_viewport()) {
+ focus_in_id = get_viewport()->get_processed_events_count();
+ }
+ } break;
- if (p_what == NOTIFICATION_ENTER_TREE) {
- update_cache();
- }
- if (p_what == NOTIFICATION_DRAG_END) {
- drop_mode_flags = 0;
- scrolling = false;
- set_physics_process_internal(false);
- update();
- }
- if (p_what == NOTIFICATION_DRAG_BEGIN) {
- single_select_defer = nullptr;
- if (cache.scroll_speed > 0) {
- scrolling = true;
- set_physics_process_internal(true);
- }
- }
- if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- if (drag_touching) {
- if (drag_touching_deaccel) {
- float pos = v_scroll->get_value();
- pos += drag_speed * get_physics_process_delta_time();
+ case NOTIFICATION_MOUSE_EXIT: {
+ if (cache.hover_type != Cache::CLICK_NONE) {
+ cache.hover_type = Cache::CLICK_NONE;
+ update();
+ }
+ } break;
- bool turnoff = false;
- if (pos < 0) {
- pos = 0;
- turnoff = true;
- set_physics_process_internal(false);
- drag_touching = false;
- drag_touching_deaccel = false;
- }
- if (pos > (v_scroll->get_max() - v_scroll->get_page())) {
- pos = v_scroll->get_max() - v_scroll->get_page();
- turnoff = true;
- }
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ drag_touching = false;
+ } break;
- v_scroll->set_value(pos);
- float sgn = drag_speed < 0 ? -1 : 1;
- float val = Math::abs(drag_speed);
- val -= 1000 * get_physics_process_delta_time();
+ case NOTIFICATION_ENTER_TREE: {
+ update_cache();
+ } break;
- if (val < 0) {
- turnoff = true;
- }
- drag_speed = sgn * val;
+ case NOTIFICATION_DRAG_END: {
+ drop_mode_flags = 0;
+ scrolling = false;
+ set_physics_process_internal(false);
+ update();
+ } break;
- if (turnoff) {
- set_physics_process_internal(false);
- drag_touching = false;
- drag_touching_deaccel = false;
- }
+ case NOTIFICATION_DRAG_BEGIN: {
+ single_select_defer = nullptr;
+ if (cache.scroll_speed > 0) {
+ scrolling = true;
+ set_physics_process_internal(true);
}
- }
+ } break;
- Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
- if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) {
- Point2 point;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (drag_touching) {
+ if (drag_touching_deaccel) {
+ float pos = v_scroll->get_value();
+ pos += drag_speed * get_physics_process_delta_time();
- if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) {
- point.x = mouse_position.x - cache.scroll_border;
- } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) {
- point.x = mouse_position.x - (get_size().width - cache.scroll_border);
- }
+ bool turnoff = false;
+ if (pos < 0) {
+ pos = 0;
+ turnoff = true;
+ set_physics_process_internal(false);
+ drag_touching = false;
+ drag_touching_deaccel = false;
+ }
+ if (pos > (v_scroll->get_max() - v_scroll->get_page())) {
+ pos = v_scroll->get_max() - v_scroll->get_page();
+ turnoff = true;
+ }
- if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) {
- point.y = mouse_position.y - cache.scroll_border;
- } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) {
- point.y = mouse_position.y - (get_size().height - cache.scroll_border);
+ v_scroll->set_value(pos);
+ float sgn = drag_speed < 0 ? -1 : 1;
+ float val = Math::abs(drag_speed);
+ val -= 1000 * get_physics_process_delta_time();
+
+ if (val < 0) {
+ turnoff = true;
+ }
+ drag_speed = sgn * val;
+
+ if (turnoff) {
+ set_physics_process_internal(false);
+ drag_touching = false;
+ drag_touching_deaccel = false;
+ }
+ }
}
- point *= cache.scroll_speed * get_physics_process_delta_time();
- point += get_scroll();
- h_scroll->set_value(point.x);
- v_scroll->set_value(point.y);
- }
- }
+ Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
+ if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) {
+ Point2 point;
- if (p_what == NOTIFICATION_DRAW) {
- update_cache();
- update_scrollbars();
- RID ci = get_canvas_item();
+ if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) {
+ point.x = mouse_position.x - cache.scroll_border;
+ } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) {
+ point.x = mouse_position.x - (get_size().width - cache.scroll_border);
+ }
- Ref<StyleBox> bg = cache.bg;
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
+ if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) {
+ point.y = mouse_position.y - cache.scroll_border;
+ } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) {
+ point.y = mouse_position.y - (get_size().height - cache.scroll_border);
+ }
- Point2 draw_ofs;
- draw_ofs += bg->get_offset();
- Size2 draw_size = get_size() - bg->get_minimum_size();
- if (h_scroll->is_visible()) {
- draw_size.width -= h_scroll->get_minimum_size().width;
- }
+ point *= cache.scroll_speed * get_physics_process_delta_time();
+ point += get_scroll();
+ h_scroll->set_value(point.x);
+ v_scroll->set_value(point.y);
+ }
+ } break;
- bg->draw(ci, Rect2(Point2(), get_size()));
+ case NOTIFICATION_DRAW: {
+ update_cache();
+ update_scrollbars();
+ RID ci = get_canvas_item();
+
+ Ref<StyleBox> bg = cache.bg;
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+
+ Point2 draw_ofs;
+ draw_ofs += bg->get_offset();
+ Size2 draw_size = get_size() - bg->get_minimum_size();
+ if (h_scroll->is_visible()) {
+ draw_size.width -= h_scroll->get_minimum_size().width;
+ }
- int tbh = _get_title_button_height();
+ bg->draw(ci, Rect2(Point2(), get_size()));
- draw_ofs.y += tbh;
- draw_size.y -= tbh;
+ int tbh = _get_title_button_height();
- cache.rtl = is_layout_rtl();
+ draw_ofs.y += tbh;
+ draw_size.y -= tbh;
- if (root && get_size().x > 0 && get_size().y > 0) {
- draw_item(Point2(), draw_ofs, draw_size, root);
- }
+ cache.rtl = is_layout_rtl();
- if (show_column_titles) {
- //title buttons
- int ofs2 = cache.bg->get_margin(SIDE_LEFT);
- for (int i = 0; i < columns.size(); i++) {
- Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button);
- Ref<Font> f = cache.tb_font;
- Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
- if (cache.rtl) {
- tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
- }
- sb->draw(ci, tbrect);
- ofs2 += tbrect.size.width;
- //text
- int clip_w = tbrect.size.width - sb->get_minimum_size().width;
- columns.write[i].text_buf->set_width(clip_w);
-
- Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- columns[i].text_buf->draw(ci, text_pos, cache.title_button_color);
+ if (root && get_size().x > 0 && get_size().y > 0) {
+ draw_item(Point2(), draw_ofs, draw_size, root);
}
- }
- // Draw the background focus outline last, so that it is drawn in front of the section headings.
- // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling.
- if (has_focus()) {
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
- const Ref<StyleBox> bg_focus = get_theme_stylebox(SNAME("bg_focus"));
- bg_focus->draw(ci, Rect2(Point2(), get_size()));
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
- }
- }
+ if (show_column_titles) {
+ //title buttons
+ int ofs2 = cache.bg->get_margin(SIDE_LEFT);
+ for (int i = 0; i < columns.size(); i++) {
+ Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button);
+ Ref<Font> f = cache.tb_font;
+ Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
+ if (cache.rtl) {
+ tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
+ }
+ sb->draw(ci, tbrect);
+ ofs2 += tbrect.size.width;
+ //text
+ int clip_w = tbrect.size.width - sb->get_minimum_size().width;
+ columns.write[i].text_buf->set_width(clip_w);
+
+ Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ columns[i].text_buf->draw(ci, text_pos, cache.title_button_color);
+ }
+ }
- if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- update_cache();
- _update_all();
- }
+ // Draw the background focus outline last, so that it is drawn in front of the section headings.
+ // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling.
+ if (has_focus()) {
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
+ const Ref<StyleBox> bg_focus = get_theme_stylebox(SNAME("bg_focus"));
+ bg_focus->draw(ci, Rect2(Point2(), get_size()));
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
+ }
+ } break;
- if (p_what == NOTIFICATION_RESIZED || p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- if (popup_edited_item != nullptr) {
- Rect2 rect = popup_edited_item->get_meta("__focus_rect");
- Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2);
- Point2i textedpos = get_global_position() + rect.position - ofs;
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ update_cache();
+ _update_all();
+ } break;
- if (cache.text_editor_position != textedpos) {
- cache.text_editor_position = textedpos;
- text_editor->set_position(textedpos);
- value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height));
+ case NOTIFICATION_RESIZED:
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ if (popup_edited_item != nullptr) {
+ Rect2 rect = popup_edited_item->get_meta("__focus_rect");
+ Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2);
+ Point2i textedpos = get_global_position() + rect.position - ofs;
+
+ if (cache.text_editor_position != textedpos) {
+ cache.text_editor_position = textedpos;
+ text_editor->set_position(textedpos);
+ value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height));
+ }
}
- }
+ } break;
}
}
@@ -3913,6 +4012,7 @@ void Tree::clear() {
selected_item = nullptr;
edited_item = nullptr;
popup_edited_item = nullptr;
+ popup_pressing_edited_item = nullptr;
update();
};
@@ -3990,10 +4090,6 @@ int Tree::get_edited_column() const {
}
TreeItem *Tree::get_next_selected(TreeItem *p_item) {
- /*
- if (!p_item)
- return nullptr;
- */
if (!root) {
return nullptr;
}
@@ -4232,12 +4328,16 @@ int Tree::get_pressed_button() const {
return pressed_button;
}
-Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column) const {
+Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column, int p_button) const {
ERR_FAIL_NULL_V(p_item, Rect2());
ERR_FAIL_COND_V(p_item->tree != this, Rect2());
if (p_column != -1) {
ERR_FAIL_INDEX_V(p_column, columns.size(), Rect2());
}
+ if (p_button != -1) {
+ ERR_FAIL_COND_V(p_column == -1, Rect2()); // pass a column if you want to pass a button
+ ERR_FAIL_INDEX_V(p_button, p_item->cells[p_column].buttons.size(), Rect2());
+ }
int ofs = get_item_offset(p_item);
int height = compute_item_height(p_item);
@@ -4255,6 +4355,19 @@ Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column) const {
}
r.position.x = accum;
r.size.x = get_column_width(p_column);
+ if (p_button != -1) {
+ const TreeItem::Cell &c = p_item->cells[p_column];
+ Vector2 ofst = Vector2(r.position.x + r.size.x, r.position.y);
+ for (int j = c.buttons.size() - 1; j >= 0; j--) {
+ Ref<Texture2D> b = c.buttons[j].texture;
+ Size2 size = b->get_size() + cache.button_pressed->get_minimum_size();
+ ofst.x -= size.x;
+
+ if (j == p_button) {
+ return Rect2(ofst, size);
+ }
+ }
+ }
}
return r;
@@ -4350,27 +4463,35 @@ Point2 Tree::get_scroll() const {
return ofs;
}
-void Tree::scroll_to_item(TreeItem *p_item) {
+void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) {
if (!is_visible_in_tree()) {
- // hack to work around crash in get_item_rect() if Tree is not in tree.
- return;
+ return; // Hack to work around crash in get_item_rect() if Tree is not in tree.
}
- // make sure the scrollbar min and max are up to date with latest changes.
update_scrollbars();
- const Rect2 r = get_item_rect(p_item);
+ const real_t tree_height = get_size().y;
+ const Rect2 item_rect = get_item_rect(p_item);
+ const real_t item_y = item_rect.position.y;
+ const real_t item_height = item_rect.size.y + cache.vseparation;
- if (r.position.y <= v_scroll->get_value()) {
- v_scroll->set_value(r.position.y);
- } else if (r.position.y + r.size.y + 2 * cache.vseparation > v_scroll->get_value() + get_size().y) {
- v_scroll->set_value(r.position.y + r.size.y + 2 * cache.vseparation - get_size().y);
+ if (p_center_on_item) {
+ v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f);
+ } else {
+ if (item_y < v_scroll->get_value()) {
+ v_scroll->set_value(item_y);
+ } else {
+ const real_t new_position = item_y + item_height - tree_height;
+ if (new_position > v_scroll->get_value()) {
+ v_scroll->set_value(new_position);
+ }
+ }
}
}
void Tree::set_h_scroll_enabled(bool p_enable) {
h_scroll_enabled = p_enable;
- minimum_size_changed();
+ update_minimum_size();
}
bool Tree::is_h_scroll_enabled() const {
@@ -4379,7 +4500,7 @@ bool Tree::is_h_scroll_enabled() const {
void Tree::set_v_scroll_enabled(bool p_enable) {
v_scroll_enabled = p_enable;
- minimum_size_changed();
+ update_minimum_size();
}
bool Tree::is_v_scroll_enabled() const {
@@ -4682,7 +4803,7 @@ String Tree::get_tooltip(const Point2 &p_pos) const {
Size2 size = b->get_size() + cache.button_pressed->get_minimum_size();
if (pos.x > col_width - size.width) {
String tooltip = c.buttons[j].tooltip;
- if (tooltip != "") {
+ if (!tooltip.is_empty()) {
return tooltip;
}
}
@@ -4756,7 +4877,7 @@ bool Tree::get_allow_reselect() const {
void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Tree::clear);
- ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::_create_item, DEFVAL(Variant()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::create_item, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root);
ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width);
@@ -4771,7 +4892,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_root", "enable"), &Tree::set_hide_root);
ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden);
- ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::_get_next_selected);
+ ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::get_next_selected);
ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected);
ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column);
ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button);
@@ -4785,10 +4906,11 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_edited_column"), &Tree::get_edited_column);
ClassDB::bind_method(D_METHOD("edit_selected"), &Tree::edit_selected);
ClassDB::bind_method(D_METHOD("get_custom_popup_rect"), &Tree::get_custom_popup_rect);
- ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column"), &Tree::_get_item_rect, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column", "button_index"), &Tree::get_item_rect, DEFVAL(-1), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position);
ClassDB::bind_method(D_METHOD("get_column_at_position", "position"), &Tree::get_column_at_position);
ClassDB::bind_method(D_METHOD("get_drop_section_at_position", "position"), &Tree::get_drop_section_at_position);
+ ClassDB::bind_method(D_METHOD("get_button_id_at_position", "position"), &Tree::get_button_id_at_position);
ClassDB::bind_method(D_METHOD("ensure_cursor_is_visible"), &Tree::ensure_cursor_is_visible);
@@ -4809,7 +4931,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language);
ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll);
- ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::_scroll_to_item);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "item", "center_on_item"), &Tree::scroll_to_item, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled);
ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled);
@@ -4830,6 +4952,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_allow_reselect"), &Tree::get_allow_reselect);
ADD_PROPERTY(PropertyInfo(Variant::INT, "columns"), "set_columns", "get_columns");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "column_titles_visible"), "set_column_titles_visible", "are_column_titles_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_folding"), "set_hide_folding", "is_folding_hidden");
@@ -4850,6 +4973,7 @@ void Tree::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_custom_button_pressed"));
ADD_SIGNAL(MethodInfo("item_double_clicked"));
ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem")));
+ ADD_SIGNAL(MethodInfo("check_propagated_to_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column")));
//ADD_SIGNAL( MethodInfo("item_double_clicked" ) );
ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked")));
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 2e4e1bd364..8ee2a3c382 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -52,12 +52,6 @@ public:
CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button
};
- enum TextAlign {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT
- };
-
private:
friend class Tree;
@@ -71,7 +65,7 @@ private:
Ref<TextLine> text_buf;
Dictionary opentype_features;
String language;
- Control::StructuredTextParser st_parser = Control::STRUCTURED_TEXT_DEFAULT;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
Array st_args;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED;
bool dirty = true;
@@ -98,7 +92,7 @@ private:
Size2i cached_minimum_size;
bool cached_minimum_size_dirty = true;
- TextAlign text_align = ALIGN_LEFT;
+ HorizontalAlignment text_alignment = HORIZONTAL_ALIGNMENT_LEFT;
Variant meta;
String tooltip;
@@ -140,7 +134,7 @@ private:
Vector<TreeItem *> children_cache;
bool is_root = false; // for tree root
- Tree *tree; // tree (for reference)
+ Tree *tree = nullptr; // tree (for reference)
TreeItem(Tree *p_tree);
@@ -171,7 +165,7 @@ private:
}
if (parent) {
if (!parent->children_cache.is_empty()) {
- parent->children_cache.remove(get_index());
+ parent->children_cache.remove_at(get_index());
}
if (parent->first_child == this) {
parent->first_child = next;
@@ -194,18 +188,8 @@ protected:
return d;
}
- void _remove_child(Object *p_child) {
- remove_child(Object::cast_to<TreeItem>(p_child));
- }
-
- void _move_before(Object *p_item) {
- move_before(Object::cast_to<TreeItem>(p_item));
- }
- void _move_after(Object *p_item) {
- move_after(Object::cast_to<TreeItem>(p_item));
- }
- Variant _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
public:
/* cell mode */
@@ -218,6 +202,14 @@ public:
bool is_checked(int p_column) const;
bool is_indeterminate(int p_column) const;
+ void propagate_check(int p_column, bool p_emit_signal = true);
+
+private:
+ // Check helpers.
+ void _propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal);
+ void _propagate_check_through_parents(int p_column, bool p_emit_signal);
+
+public:
void set_text(int p_column, String p_text);
String get_text(int p_column) const;
@@ -228,8 +220,8 @@ public:
int get_opentype_feature(int p_column, const String &p_name) const;
void clear_opentype_features(int p_column);
- void set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser);
- Control::StructuredTextParser get_structured_text_bidi_override(int p_column) const;
+ void set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser);
+ TextServer::StructuredTextParser get_structured_text_bidi_override(int p_column) const;
void set_structured_text_bidi_override_options(int p_column, Array p_args);
Array get_structured_text_bidi_override_options(int p_column) const;
@@ -256,6 +248,7 @@ public:
int get_button_count(int p_column) const;
String get_button_tooltip(int p_column, int p_idx) const;
Ref<Texture2D> get_button(int p_column, int p_idx) const;
+ int get_button_id(int p_column, int p_idx) const;
void erase_button(int p_column, int p_idx);
int get_button_by_id(int p_column, int p_id) const;
void set_button(int p_column, int p_idx, const Ref<Texture2D> &p_button);
@@ -316,8 +309,8 @@ public:
void set_tooltip(int p_column, const String &p_tooltip);
String get_tooltip(int p_column) const;
- void set_text_align(int p_column, TextAlign p_align);
- TextAlign get_text_align(int p_column) const;
+ void set_text_alignment(int p_column, HorizontalAlignment p_alignment);
+ HorizontalAlignment get_text_alignment(int p_column) const;
void set_expand_right(int p_column, bool p_enable);
bool get_expand_right(int p_column) const;
@@ -359,7 +352,6 @@ public:
};
VARIANT_ENUM_CAST(TreeItem::TreeCellMode);
-VARIANT_ENUM_CAST(TreeItem::TextAlign);
class VBoxContainer;
@@ -387,6 +379,9 @@ private:
TreeItem *selected_item = nullptr;
TreeItem *edited_item = nullptr;
+ TreeItem *popup_pressing_edited_item = nullptr; // Candidate.
+ int popup_pressing_edited_item_column = -1;
+
TreeItem *drop_mode_over = nullptr;
int drop_mode_section = 0;
@@ -436,18 +431,18 @@ private:
bool show_column_titles = false;
- VBoxContainer *popup_editor_vb;
+ VBoxContainer *popup_editor_vb = nullptr;
- Popup *popup_editor;
+ Popup *popup_editor = nullptr;
LineEdit *text_editor = nullptr;
- HSlider *value_editor;
+ HSlider *value_editor = nullptr;
bool updating_value_editor = false;
uint64_t focus_in_id = 0;
PopupMenu *popup_menu = nullptr;
Vector<ColumnInfo> columns;
- Timer *range_click_timer;
+ Timer *range_click_timer = nullptr;
TreeItem *range_item_last = nullptr;
bool range_up_last = false;
void _range_click_timeout();
@@ -561,8 +556,8 @@ private:
int _get_title_button_height() const;
void _scroll_moved(float p_value);
- HScrollBar *h_scroll;
- VScrollBar *v_scroll;
+ HScrollBar *h_scroll = nullptr;
+ VScrollBar *v_scroll = nullptr;
bool h_scroll_enabled = true;
bool v_scroll_enabled = true;
@@ -616,23 +611,6 @@ private:
protected:
static void _bind_methods();
- //bind helpers
- TreeItem *_create_item(Object *p_parent, int p_idx = -1) {
- return create_item(Object::cast_to<TreeItem>(p_parent), p_idx);
- }
-
- TreeItem *_get_next_selected(Object *p_item) {
- return get_next_selected(Object::cast_to<TreeItem>(p_item));
- }
-
- Rect2 _get_item_rect(Object *p_item, int p_column) const {
- return get_item_rect(Object::cast_to<TreeItem>(p_item), p_column);
- }
-
- void _scroll_to_item(Object *p_item) {
- scroll_to_item(Object::cast_to<TreeItem>(p_item));
- }
-
public:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
@@ -698,7 +676,7 @@ public:
Rect2 get_custom_popup_rect() const;
int get_item_offset(TreeItem *p_item) const;
- Rect2 get_item_rect(TreeItem *p_item, int p_column = -1) const;
+ Rect2 get_item_rect(TreeItem *p_item, int p_column = -1, int p_button = -1) const;
bool edit_selected();
bool is_editing();
@@ -708,7 +686,7 @@ public:
TreeItem *get_item_with_text(const String &p_find) const;
Point2 get_scroll() const;
- void scroll_to_item(TreeItem *p_item);
+ void scroll_to_item(TreeItem *p_item, bool p_center_on_item = false);
void set_h_scroll_enabled(bool p_enable);
bool is_h_scroll_enabled() const;
void set_v_scroll_enabled(bool p_enable);
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_stream_player.cpp
index 989aabc549..ca2dad71af 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* video_player.cpp */
+/* video_stream_player.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "video_player.h"
+#include "video_stream_player.h"
#include "core/os/os.h"
#include "scene/scene_string_names.h"
#include "servers/audio_server.h"
-int VideoPlayer::sp_get_channel_count() const {
+int VideoStreamPlayer::sp_get_channel_count() const {
if (playback.is_null()) {
return 0;
}
@@ -42,7 +42,7 @@ int VideoPlayer::sp_get_channel_count() const {
return playback->get_channels();
}
-bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) {
+bool VideoStreamPlayer::mix(AudioFrame *p_buffer, int p_frames) {
// Check the amount resampler can really handle.
// If it cannot, wait "wait_resampler_phase_limit" times.
// This mechanism contributes to smoother pause/unpause operation.
@@ -56,11 +56,11 @@ bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) {
}
// Called from main thread (e.g. VideoStreamPlaybackTheora::update).
-int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) {
+int VideoStreamPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) {
ERR_FAIL_NULL_V(p_udata, 0);
ERR_FAIL_NULL_V(p_data, 0);
- VideoPlayer *vp = (VideoPlayer *)p_udata;
+ VideoStreamPlayer *vp = static_cast<VideoStreamPlayer *>(p_udata);
int todo = MIN(vp->resampler.get_writer_space(), p_frames);
@@ -75,13 +75,13 @@ int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_f
return todo;
}
-void VideoPlayer::_mix_audios(void *p_self) {
+void VideoStreamPlayer::_mix_audios(void *p_self) {
ERR_FAIL_NULL(p_self);
- reinterpret_cast<VideoPlayer *>(p_self)->_mix_audio();
+ static_cast<VideoStreamPlayer *>(p_self)->_mix_audio();
}
// Called from audio thread
-void VideoPlayer::_mix_audio() {
+void VideoStreamPlayer::_mix_audio() {
if (!stream.is_valid()) {
return;
}
@@ -126,7 +126,7 @@ void VideoPlayer::_mix_audio() {
}
}
-void VideoPlayer::_notification(int p_notification) {
+void VideoStreamPlayer::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_ENTER_TREE: {
AudioServer::get_singleton()->add_mix_callback(_mix_audios, this);
@@ -134,7 +134,6 @@ void VideoPlayer::_notification(int p_notification) {
if (stream.is_valid() && autoplay && !Engine::get_singleton()->is_editor_hint()) {
play();
}
-
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -162,7 +161,6 @@ void VideoPlayer::_notification(int p_notification) {
if (!playback->is_playing()) {
emit_signal(SceneStringNames::get_singleton()->finished);
}
-
} break;
case NOTIFICATION_DRAW: {
@@ -175,12 +173,11 @@ void VideoPlayer::_notification(int p_notification) {
Size2 s = expand ? get_size() : texture->get_size();
draw_texture_rect(texture, Rect2(Point2(), s), false);
-
} break;
- };
-};
+ }
+}
-Size2 VideoPlayer::get_minimum_size() const {
+Size2 VideoStreamPlayer::get_minimum_size() const {
if (!expand && !texture.is_null()) {
return texture->get_size();
} else {
@@ -188,17 +185,17 @@ Size2 VideoPlayer::get_minimum_size() const {
}
}
-void VideoPlayer::set_expand(bool p_expand) {
+void VideoStreamPlayer::set_expand(bool p_expand) {
expand = p_expand;
update();
- minimum_size_changed();
+ update_minimum_size();
}
-bool VideoPlayer::has_expand() const {
+bool VideoStreamPlayer::has_expand() const {
return expand;
}
-void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) {
+void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
stop();
AudioServer::get_singleton()->lock();
@@ -241,15 +238,15 @@ void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) {
update();
if (!expand) {
- minimum_size_changed();
+ update_minimum_size();
}
-};
+}
-Ref<VideoStream> VideoPlayer::get_stream() const {
+Ref<VideoStream> VideoStreamPlayer::get_stream() const {
return stream;
-};
+}
-void VideoPlayer::play() {
+void VideoStreamPlayer::play() {
ERR_FAIL_COND(!is_inside_tree());
if (playback.is_null()) {
return;
@@ -257,12 +254,10 @@ void VideoPlayer::play() {
playback->stop();
playback->play();
set_process_internal(true);
- // AudioServer::get_singleton()->stream_set_active(stream_rid,true);
- // AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
last_audio_time = 0;
-};
+}
-void VideoPlayer::stop() {
+void VideoStreamPlayer::stop() {
if (!is_inside_tree()) {
return;
}
@@ -271,94 +266,93 @@ void VideoPlayer::stop() {
}
playback->stop();
- // AudioServer::get_singleton()->stream_set_active(stream_rid,false);
resampler.flush();
set_process_internal(false);
last_audio_time = 0;
-};
+}
-bool VideoPlayer::is_playing() const {
+bool VideoStreamPlayer::is_playing() const {
if (playback.is_null()) {
return false;
}
return playback->is_playing();
-};
+}
-void VideoPlayer::set_paused(bool p_paused) {
+void VideoStreamPlayer::set_paused(bool p_paused) {
paused = p_paused;
if (playback.is_valid()) {
playback->set_paused(p_paused);
set_process_internal(!p_paused);
- };
+ }
last_audio_time = 0;
-};
+}
-bool VideoPlayer::is_paused() const {
+bool VideoStreamPlayer::is_paused() const {
return paused;
}
-void VideoPlayer::set_buffering_msec(int p_msec) {
+void VideoStreamPlayer::set_buffering_msec(int p_msec) {
buffering_ms = p_msec;
}
-int VideoPlayer::get_buffering_msec() const {
+int VideoStreamPlayer::get_buffering_msec() const {
return buffering_ms;
}
-void VideoPlayer::set_audio_track(int p_track) {
+void VideoStreamPlayer::set_audio_track(int p_track) {
audio_track = p_track;
}
-int VideoPlayer::get_audio_track() const {
+int VideoStreamPlayer::get_audio_track() const {
return audio_track;
}
-void VideoPlayer::set_volume(float p_vol) {
+void VideoStreamPlayer::set_volume(float p_vol) {
volume = p_vol;
-};
+}
-float VideoPlayer::get_volume() const {
+float VideoStreamPlayer::get_volume() const {
return volume;
-};
+}
-void VideoPlayer::set_volume_db(float p_db) {
+void VideoStreamPlayer::set_volume_db(float p_db) {
if (p_db < -79) {
set_volume(0);
} else {
set_volume(Math::db2linear(p_db));
}
-};
+}
-float VideoPlayer::get_volume_db() const {
+float VideoStreamPlayer::get_volume_db() const {
if (volume == 0) {
return -80;
} else {
return Math::linear2db(volume);
}
-};
+}
-String VideoPlayer::get_stream_name() const {
+String VideoStreamPlayer::get_stream_name() const {
if (stream.is_null()) {
return "<No Stream>";
}
return stream->get_name();
-};
+}
-float VideoPlayer::get_stream_position() const {
+float VideoStreamPlayer::get_stream_position() const {
if (playback.is_null()) {
return 0;
}
return playback->get_playback_position();
-};
+}
-void VideoPlayer::set_stream_position(float p_position) {
+void VideoStreamPlayer::set_stream_position(float p_position) {
if (playback.is_valid()) {
playback->seek(p_position);
}
}
-Ref<Texture2D> VideoPlayer::get_video_texture() const {
+Ref<Texture2D> VideoStreamPlayer::get_video_texture() const {
if (playback.is_valid()) {
return playback->get_texture();
}
@@ -366,22 +360,22 @@ Ref<Texture2D> VideoPlayer::get_video_texture() const {
return Ref<Texture2D>();
}
-void VideoPlayer::set_autoplay(bool p_enable) {
+void VideoStreamPlayer::set_autoplay(bool p_enable) {
autoplay = p_enable;
-};
+}
-bool VideoPlayer::has_autoplay() const {
+bool VideoStreamPlayer::has_autoplay() const {
return autoplay;
-};
+}
-void VideoPlayer::set_bus(const StringName &p_bus) {
- //if audio is active, must lock this
+void VideoStreamPlayer::set_bus(const StringName &p_bus) {
+ // If audio is active, must lock this.
AudioServer::get_singleton()->lock();
bus = p_bus;
AudioServer::get_singleton()->unlock();
}
-StringName VideoPlayer::get_bus() const {
+StringName VideoStreamPlayer::get_bus() const {
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
if (AudioServer::get_singleton()->get_bus_name(i) == bus) {
return bus;
@@ -390,7 +384,7 @@ StringName VideoPlayer::get_bus() const {
return "Master";
}
-void VideoPlayer::_validate_property(PropertyInfo &p_property) const {
+void VideoStreamPlayer::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "bus") {
String options;
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
@@ -405,51 +399,50 @@ void VideoPlayer::_validate_property(PropertyInfo &p_property) const {
}
}
-void VideoPlayer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_stream", "stream"), &VideoPlayer::set_stream);
- ClassDB::bind_method(D_METHOD("get_stream"), &VideoPlayer::get_stream);
+void VideoStreamPlayer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_stream", "stream"), &VideoStreamPlayer::set_stream);
+ ClassDB::bind_method(D_METHOD("get_stream"), &VideoStreamPlayer::get_stream);
- ClassDB::bind_method(D_METHOD("play"), &VideoPlayer::play);
- ClassDB::bind_method(D_METHOD("stop"), &VideoPlayer::stop);
+ ClassDB::bind_method(D_METHOD("play"), &VideoStreamPlayer::play);
+ ClassDB::bind_method(D_METHOD("stop"), &VideoStreamPlayer::stop);
- ClassDB::bind_method(D_METHOD("is_playing"), &VideoPlayer::is_playing);
+ ClassDB::bind_method(D_METHOD("is_playing"), &VideoStreamPlayer::is_playing);
- ClassDB::bind_method(D_METHOD("set_paused", "paused"), &VideoPlayer::set_paused);
- ClassDB::bind_method(D_METHOD("is_paused"), &VideoPlayer::is_paused);
+ ClassDB::bind_method(D_METHOD("set_paused", "paused"), &VideoStreamPlayer::set_paused);
+ ClassDB::bind_method(D_METHOD("is_paused"), &VideoStreamPlayer::is_paused);
- ClassDB::bind_method(D_METHOD("set_volume", "volume"), &VideoPlayer::set_volume);
- ClassDB::bind_method(D_METHOD("get_volume"), &VideoPlayer::get_volume);
+ ClassDB::bind_method(D_METHOD("set_volume", "volume"), &VideoStreamPlayer::set_volume);
+ ClassDB::bind_method(D_METHOD("get_volume"), &VideoStreamPlayer::get_volume);
- ClassDB::bind_method(D_METHOD("set_volume_db", "db"), &VideoPlayer::set_volume_db);
- ClassDB::bind_method(D_METHOD("get_volume_db"), &VideoPlayer::get_volume_db);
+ ClassDB::bind_method(D_METHOD("set_volume_db", "db"), &VideoStreamPlayer::set_volume_db);
+ ClassDB::bind_method(D_METHOD("get_volume_db"), &VideoStreamPlayer::get_volume_db);
- ClassDB::bind_method(D_METHOD("set_audio_track", "track"), &VideoPlayer::set_audio_track);
- ClassDB::bind_method(D_METHOD("get_audio_track"), &VideoPlayer::get_audio_track);
+ ClassDB::bind_method(D_METHOD("set_audio_track", "track"), &VideoStreamPlayer::set_audio_track);
+ ClassDB::bind_method(D_METHOD("get_audio_track"), &VideoStreamPlayer::get_audio_track);
- ClassDB::bind_method(D_METHOD("get_stream_name"), &VideoPlayer::get_stream_name);
+ ClassDB::bind_method(D_METHOD("get_stream_name"), &VideoStreamPlayer::get_stream_name);
- ClassDB::bind_method(D_METHOD("set_stream_position", "position"), &VideoPlayer::set_stream_position);
- ClassDB::bind_method(D_METHOD("get_stream_position"), &VideoPlayer::get_stream_position);
+ ClassDB::bind_method(D_METHOD("set_stream_position", "position"), &VideoStreamPlayer::set_stream_position);
+ ClassDB::bind_method(D_METHOD("get_stream_position"), &VideoStreamPlayer::get_stream_position);
- ClassDB::bind_method(D_METHOD("set_autoplay", "enabled"), &VideoPlayer::set_autoplay);
- ClassDB::bind_method(D_METHOD("has_autoplay"), &VideoPlayer::has_autoplay);
+ ClassDB::bind_method(D_METHOD("set_autoplay", "enabled"), &VideoStreamPlayer::set_autoplay);
+ ClassDB::bind_method(D_METHOD("has_autoplay"), &VideoStreamPlayer::has_autoplay);
- ClassDB::bind_method(D_METHOD("set_expand", "enable"), &VideoPlayer::set_expand);
- ClassDB::bind_method(D_METHOD("has_expand"), &VideoPlayer::has_expand);
+ ClassDB::bind_method(D_METHOD("set_expand", "enable"), &VideoStreamPlayer::set_expand);
+ ClassDB::bind_method(D_METHOD("has_expand"), &VideoStreamPlayer::has_expand);
- ClassDB::bind_method(D_METHOD("set_buffering_msec", "msec"), &VideoPlayer::set_buffering_msec);
- ClassDB::bind_method(D_METHOD("get_buffering_msec"), &VideoPlayer::get_buffering_msec);
+ ClassDB::bind_method(D_METHOD("set_buffering_msec", "msec"), &VideoStreamPlayer::set_buffering_msec);
+ ClassDB::bind_method(D_METHOD("get_buffering_msec"), &VideoStreamPlayer::get_buffering_msec);
- ClassDB::bind_method(D_METHOD("set_bus", "bus"), &VideoPlayer::set_bus);
- ClassDB::bind_method(D_METHOD("get_bus"), &VideoPlayer::get_bus);
+ ClassDB::bind_method(D_METHOD("set_bus", "bus"), &VideoStreamPlayer::set_bus);
+ ClassDB::bind_method(D_METHOD("get_bus"), &VideoStreamPlayer::get_bus);
- ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoPlayer::get_video_texture);
+ ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoStreamPlayer::get_video_texture);
ADD_SIGNAL(MethodInfo("finished"));
ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream");
- //ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ;
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01"), "set_volume_db", "get_volume_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume", PROPERTY_HINT_RANGE, "0,15,0.01,exp", PROPERTY_USAGE_NONE), "set_volume", "get_volume");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "has_autoplay");
@@ -461,10 +454,8 @@ void VideoPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
}
-VideoPlayer::VideoPlayer() {}
+VideoStreamPlayer::VideoStreamPlayer() {}
-VideoPlayer::~VideoPlayer() {
- // if (stream_rid.is_valid())
- // AudioServer::get_singleton()->free(stream_rid);
- resampler.clear(); //Not necessary here, but make in consistent with other "stream_player" classes
-};
+VideoStreamPlayer::~VideoStreamPlayer() {
+ resampler.clear(); // Not necessary here, but make in consistent with other "stream_player" classes.
+}
diff --git a/scene/gui/video_player.h b/scene/gui/video_stream_player.h
index 0edad296a1..130b2901f1 100644
--- a/scene/gui/video_player.h
+++ b/scene/gui/video_stream_player.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* video_player.h */
+/* video_stream_player.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,16 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VIDEO_PLAYER_H
-#define VIDEO_PLAYER_H
+#ifndef VIDEO_STREAM_PLAYER_H
+#define VIDEO_STREAM_PLAYER_H
#include "scene/gui/control.h"
#include "scene/resources/video_stream.h"
#include "servers/audio/audio_rb_resampler.h"
#include "servers/audio_server.h"
-class VideoPlayer : public Control {
- GDCLASS(VideoPlayer, Control);
+class VideoStreamPlayer : public Control {
+ GDCLASS(VideoStreamPlayer, Control);
struct Output {
AudioFrame vol;
@@ -119,8 +119,8 @@ public:
void set_bus(const StringName &p_bus);
StringName get_bus() const;
- VideoPlayer();
- ~VideoPlayer();
+ VideoStreamPlayer();
+ ~VideoStreamPlayer();
};
-#endif // VIDEO_PLAYER_H
+#endif // VIDEO_STREAM_PLAYER_H
diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp
new file mode 100644
index 0000000000..892d0aba29
--- /dev/null
+++ b/scene/gui/view_panner.cpp
@@ -0,0 +1,184 @@
+/*************************************************************************/
+/* view_panner.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 "view_panner.h"
+
+#include "core/input/input.h"
+#include "core/input/shortcut.h"
+#include "core/os/keyboard.h"
+
+bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) {
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ Vector2 scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_RIGHT) - (mb->get_button_index() == MouseButton::WHEEL_LEFT), (mb->get_button_index() == MouseButton::WHEEL_DOWN) - (mb->get_button_index() == MouseButton::WHEEL_UP));
+ if (scroll_vec != Vector2()) {
+ if (control_scheme == SCROLL_PANS) {
+ if (mb->is_ctrl_pressed()) {
+ scroll_vec.y *= mb->get_factor();
+ callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_pressed()));
+ return true;
+ } else {
+ Vector2 panning;
+ if (mb->is_shift_pressed()) {
+ panning.x += mb->get_factor() * scroll_vec.y;
+ panning.y += mb->get_factor() * scroll_vec.x;
+ } else {
+ panning.y += mb->get_factor() * scroll_vec.y;
+ panning.x += mb->get_factor() * scroll_vec.x;
+ }
+ callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed()));
+ return true;
+ }
+ } else {
+ if (mb->is_ctrl_pressed()) {
+ Vector2 panning;
+ if (mb->is_shift_pressed()) {
+ panning.x += mb->get_factor() * scroll_vec.y;
+ panning.y += mb->get_factor() * scroll_vec.x;
+ } else {
+ panning.y += mb->get_factor() * scroll_vec.y;
+ panning.x += mb->get_factor() * scroll_vec.x;
+ }
+ callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed()));
+ return true;
+ } else if (!mb->is_shift_pressed()) {
+ scroll_vec.y *= mb->get_factor();
+ callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_pressed()));
+ return true;
+ }
+ }
+ }
+
+ // Alt is not used for button presses, so ignore it.
+ if (mb->is_alt_pressed()) {
+ return false;
+ }
+
+ bool is_drag_event = mb->get_button_index() == MouseButton::MIDDLE ||
+ (enable_rmb && mb->get_button_index() == MouseButton::RIGHT) ||
+ (!simple_panning_enabled && mb->get_button_index() == MouseButton::LEFT && is_panning()) ||
+ (force_drag && mb->get_button_index() == MouseButton::LEFT);
+
+ if (is_drag_event) {
+ if (mb->is_pressed()) {
+ is_dragging = true;
+ } else {
+ is_dragging = false;
+ }
+ return mb->get_button_index() != MouseButton::LEFT || mb->is_pressed(); // Don't consume LMB release events (it fixes some selection problems).
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ if (is_dragging) {
+ if (p_canvas_rect != Rect2()) {
+ callback_helper(pan_callback, varray(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect)));
+ } else {
+ callback_helper(pan_callback, varray(mm->get_relative()));
+ }
+ return true;
+ }
+ }
+
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid()) {
+ if (pan_view_shortcut.is_valid() && pan_view_shortcut->matches_event(k)) {
+ pan_key_pressed = k->is_pressed();
+ if (simple_panning_enabled || (Input::get_singleton()->get_mouse_button_mask() & MouseButton::LEFT) != MouseButton::NONE) {
+ is_dragging = pan_key_pressed;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ViewPanner::release_pan_key() {
+ pan_key_pressed = false;
+ is_dragging = false;
+}
+
+void ViewPanner::callback_helper(Callable p_callback, Vector<Variant> p_args) {
+ const Variant **argptr = (const Variant **)alloca(sizeof(Variant *) * p_args.size());
+ for (int i = 0; i < p_args.size(); i++) {
+ argptr[i] = &p_args[i];
+ }
+
+ Variant result;
+ Callable::CallError ce;
+ p_callback.call(argptr, p_args.size(), result, ce);
+}
+
+void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) {
+ scroll_callback = p_scroll_callback;
+ pan_callback = p_pan_callback;
+ zoom_callback = p_zoom_callback;
+}
+
+void ViewPanner::set_control_scheme(ControlScheme p_scheme) {
+ control_scheme = p_scheme;
+}
+
+void ViewPanner::set_enable_rmb(bool p_enable) {
+ enable_rmb = p_enable;
+}
+
+void ViewPanner::set_pan_shortcut(Ref<Shortcut> p_shortcut) {
+ pan_view_shortcut = p_shortcut;
+ pan_key_pressed = false;
+}
+
+void ViewPanner::set_simple_panning_enabled(bool p_enabled) {
+ simple_panning_enabled = p_enabled;
+}
+
+void ViewPanner::setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning) {
+ set_control_scheme(p_scheme);
+ set_pan_shortcut(p_shortcut);
+ set_simple_panning_enabled(p_simple_panning);
+}
+
+bool ViewPanner::is_panning() const {
+ return is_dragging || pan_key_pressed;
+}
+
+void ViewPanner::set_force_drag(bool p_force) {
+ force_drag = p_force;
+}
+
+ViewPanner::ViewPanner() {
+ Array inputs;
+ inputs.append(InputEventKey::create_reference(Key::SPACE));
+
+ pan_view_shortcut.instantiate();
+ pan_view_shortcut->set_events(inputs);
+}
diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h
new file mode 100644
index 0000000000..5b820c5f8f
--- /dev/null
+++ b/scene/gui/view_panner.h
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* view_panner.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 VIEW_PANNER_H
+#define VIEW_PANNER_H
+
+#include "core/object/ref_counted.h"
+
+class InputEvent;
+class Shortcut;
+
+class ViewPanner : public RefCounted {
+ GDCLASS(ViewPanner, RefCounted);
+
+public:
+ enum ControlScheme {
+ SCROLL_ZOOMS,
+ SCROLL_PANS,
+ };
+
+private:
+ bool is_dragging = false;
+ bool pan_key_pressed = false;
+ bool force_drag = false;
+
+ bool enable_rmb = false;
+ bool simple_panning_enabled = false;
+
+ Ref<Shortcut> pan_view_shortcut;
+
+ Callable scroll_callback;
+ Callable pan_callback;
+ Callable zoom_callback;
+
+ void callback_helper(Callable p_callback, Vector<Variant> p_args);
+ ControlScheme control_scheme = SCROLL_ZOOMS;
+
+public:
+ void set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback);
+ void set_control_scheme(ControlScheme p_scheme);
+ void set_enable_rmb(bool p_enable);
+ void set_pan_shortcut(Ref<Shortcut> p_shortcut);
+ void set_simple_panning_enabled(bool p_enabled);
+
+ void setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning);
+
+ bool is_panning() const;
+ void set_force_drag(bool p_force);
+
+ bool gui_input(const Ref<InputEvent> &p_ev, Rect2 p_canvas_rect = Rect2());
+ void release_pan_key();
+
+ ViewPanner();
+};
+
+#endif // VIEW_PANNER_H
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 22e3c3bf24..20f3f82a4e 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -56,77 +56,67 @@ Transform2D CanvasItem::_edit_get_transform() const {
#endif
bool CanvasItem::is_visible_in_tree() const {
- if (!is_inside_tree()) {
- return false;
+ return visible && parent_visible_in_tree;
+}
+
+void CanvasItem::_propagate_visibility_changed(bool p_parent_visible_in_tree) {
+ parent_visible_in_tree = p_parent_visible_in_tree;
+ if (!visible) {
+ return;
+ }
+ if (p_parent_visible_in_tree && first_draw) { // Avoid propagating it twice.
+ first_draw = false;
}
- const CanvasItem *p = this;
+ _handle_visibility_change(p_parent_visible_in_tree);
+}
- while (p) {
- if (!p->visible) {
- return false;
- }
- if (p->window && !p->window->is_visible()) {
- return false;
- }
- p = p->get_parent_item();
+void CanvasItem::set_visible(bool p_visible) {
+ if (visible == p_visible) {
+ return;
}
- return true;
-}
+ visible = p_visible;
-void CanvasItem::_propagate_visibility_changed(bool p_visible) {
- if (p_visible && first_draw) { //avoid propagating it twice
- first_draw = false;
+ if (!parent_visible_in_tree) {
+ notification(NOTIFICATION_VISIBILITY_CHANGED);
+ return;
}
+
+ _handle_visibility_change(p_visible);
+}
+
+void CanvasItem::_handle_visibility_change(bool p_visible) {
+ RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, p_visible);
notification(NOTIFICATION_VISIBILITY_CHANGED);
if (p_visible) {
- update(); //todo optimize
+ update();
} else {
emit_signal(SceneStringNames::get_singleton()->hidden);
}
- _block();
+ _block();
for (int i = 0; i < get_child_count(); i++) {
CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
- if (c && c->visible) { //should the top_levels stop propagation? i think so but..
+ if (c) { // Should the top_levels stop propagation? I think so, but...
c->_propagate_visibility_changed(p_visible);
}
}
-
_unblock();
}
void CanvasItem::show() {
- if (visible) {
- return;
- }
-
- visible = true;
- RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, true);
-
- if (!is_inside_tree()) {
- return;
- }
-
- _propagate_visibility_changed(true);
+ set_visible(true);
}
void CanvasItem::hide() {
- if (!visible) {
- return;
- }
-
- visible = false;
- RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, false);
-
- if (!is_inside_tree()) {
- return;
- }
+ set_visible(false);
+}
- _propagate_visibility_changed(false);
+bool CanvasItem::is_visible() const {
+ return visible;
}
CanvasItem *CanvasItem::current_item_drawn = nullptr;
@@ -142,7 +132,7 @@ 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()) { //todo optimize this!!
+ if (is_visible_in_tree()) {
if (first_draw) {
notification(NOTIFICATION_VISIBILITY_CHANGED);
first_draw = false;
@@ -275,41 +265,55 @@ void CanvasItem::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
ERR_FAIL_COND(!is_inside_tree());
- _update_texture_filter_changed(false);
- _update_texture_repeat_changed(false);
-
first_draw = true;
+
Node *parent = get_parent();
if (parent) {
CanvasItem *ci = Object::cast_to<CanvasItem>(parent);
+
if (ci) {
+ parent_visible_in_tree = ci->is_visible_in_tree();
C = ci->children_items.push_back(this);
- }
- if (!ci) {
- //look for a window
- Viewport *viewport = nullptr;
-
- while (parent) {
- viewport = Object::cast_to<Viewport>(parent);
- if (viewport) {
- break;
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(parent);
+
+ if (cl) {
+ parent_visible_in_tree = cl->is_visible();
+ } else {
+ // Look for a window.
+ Viewport *viewport = nullptr;
+
+ while (parent) {
+ viewport = Object::cast_to<Viewport>(parent);
+ if (viewport) {
+ break;
+ }
+ parent = parent->get_parent();
}
- parent = parent->get_parent();
- }
- ERR_FAIL_COND(!viewport);
+ ERR_FAIL_COND(!viewport);
- window = Object::cast_to<Window>(viewport);
- if (window) {
- window->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed));
+ window = Object::cast_to<Window>(viewport);
+ if (window) {
+ window->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed));
+ parent_visible_in_tree = window->is_visible();
+ } else {
+ parent_visible_in_tree = true;
+ }
}
}
}
+
_enter_canvas();
+
+ _update_texture_filter_changed(false);
+ _update_texture_repeat_changed(false);
+
if (!block_transform_notify && !xform_change.in_list()) {
get_tree()->xform_change_list.add(&xform_change);
}
} break;
+
case NOTIFICATION_MOVED_IN_PARENT: {
if (!is_inside_tree()) {
break;
@@ -322,8 +326,8 @@ void CanvasItem::_notification(int p_what) {
ERR_FAIL_COND(!p);
RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index());
}
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (xform_change.in_list()) {
get_tree()->xform_change_list.remove(&xform_change);
@@ -337,34 +341,21 @@ void CanvasItem::_notification(int p_what) {
window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed));
}
global_invalid = true;
+ parent_visible_in_tree = false;
} break;
- case NOTIFICATION_DRAW:
- case NOTIFICATION_TRANSFORM_CHANGED: {
- } break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
emit_signal(SceneStringNames::get_singleton()->visibility_changed);
} break;
}
}
-void CanvasItem::set_visible(bool p_visible) {
- if (p_visible) {
- show();
- } else {
- hide();
- }
-}
-
void CanvasItem::_window_visibility_changed() {
if (visible) {
_propagate_visibility_changed(window->is_visible());
}
}
-bool CanvasItem::is_visible() const {
- return visible;
-}
-
void CanvasItem::update() {
if (!is_inside_tree()) {
return;
@@ -453,10 +444,29 @@ void CanvasItem::item_rect_changed(bool p_size_changed) {
emit_signal(SceneStringNames::get_singleton()->item_rect_changed);
}
-void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width) {
+void CanvasItem::draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash) {
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+
+ float length = (p_to - p_from).length();
+ if (length < p_dash) {
+ RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width);
+ return;
+ }
+
+ Point2 off = p_from;
+ Vector2 step = p_dash * (p_to - p_from).normalized();
+ int steps = length / p_dash / 2;
+ for (int i = 0; i < steps; i++) {
+ RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, off, (off + step), p_color, p_width);
+ off += 2 * step;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, off, p_to, p_color, p_width);
+}
+
+void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, bool p_antialiased) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width);
+ RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased);
}
void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width, bool p_antialiased) {
@@ -648,16 +658,16 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex
RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid);
}
-void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
+void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_font.is_null());
- p_font->draw_string(canvas_item, p_pos, p_text, p_align, p_width, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags);
+ p_font->draw_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags);
}
-void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
+void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, real_t p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_font.is_null());
- p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_align, p_width, p_max_lines, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags);
+ p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_max_lines, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags);
}
real_t CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
@@ -873,11 +883,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_draw_behind_parent", "enable"), &CanvasItem::set_draw_behind_parent);
ClassDB::bind_method(D_METHOD("is_draw_behind_parent_enabled"), &CanvasItem::is_draw_behind_parent_enabled);
- ClassDB::bind_method(D_METHOD("_set_on_top", "on_top"), &CanvasItem::_set_on_top);
- ClassDB::bind_method(D_METHOD("_is_on_top"), &CanvasItem::_is_on_top);
- //ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform);
-
- ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width"), &CanvasItem::draw_line, DEFVAL(1.0));
+ ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_dashed_line", "from", "to", "color", "width", "dash"), &CanvasItem::draw_dashed_line, DEFVAL(1.0), DEFVAL(2.0));
ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width", "antialiased"), &CanvasItem::draw_arc, DEFVAL(1.0), DEFVAL(false));
@@ -893,8 +900,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()));
- ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "alignment", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &CanvasItem::draw_char, DEFVAL(""), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture"), &CanvasItem::draw_multimesh);
@@ -908,6 +915,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_viewport_transform"), &CanvasItem::get_viewport_transform);
ClassDB::bind_method(D_METHOD("get_viewport_rect"), &CanvasItem::get_viewport_rect);
ClassDB::bind_method(D_METHOD("get_canvas_transform"), &CanvasItem::get_canvas_transform);
+ ClassDB::bind_method(D_METHOD("get_screen_transform"), &CanvasItem::get_screen_transform);
ClassDB::bind_method(D_METHOD("get_local_mouse_position"), &CanvasItem::get_local_mouse_position);
ClassDB::bind_method(D_METHOD("get_global_mouse_position"), &CanvasItem::get_global_mouse_position);
ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasItem::get_canvas);
@@ -948,7 +956,6 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_on_top", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_on_top", "_is_on_top"); //compatibility
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_children"), "set_clip_children", "is_clipping_children");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
@@ -967,6 +974,7 @@ void CanvasItem::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_rect_changed"));
BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED);
+ BIND_CONSTANT(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
BIND_CONSTANT(NOTIFICATION_DRAW);
BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
BIND_CONSTANT(NOTIFICATION_ENTER_CANVAS);
@@ -1004,12 +1012,7 @@ Transform2D CanvasItem::get_viewport_transform() const {
ERR_FAIL_COND_V(!is_inside_tree(), Transform2D());
if (canvas_layer) {
- if (get_viewport()) {
- return get_viewport()->get_final_transform() * canvas_layer->get_transform();
- } else {
- return canvas_layer->get_transform();
- }
-
+ return get_viewport()->get_final_transform() * canvas_layer->get_transform();
} else {
return get_viewport()->get_final_transform() * get_viewport()->get_canvas_transform();
}
@@ -1031,8 +1034,8 @@ void CanvasItem::set_notify_transform(bool p_enable) {
notify_transform = p_enable;
if (notify_transform && is_inside_tree()) {
- //this ensures that invalid globals get resolved, so notifications can be received
- get_global_transform();
+ // This ensures that invalid globals get resolved, so notifications can be received.
+ _ALLOW_DISCARD_ get_global_transform();
}
}
@@ -1290,7 +1293,7 @@ void CanvasTexture::_bind_methods() {
ADD_GROUP("Diffuse", "diffuse_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "diffuse_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_diffuse_texture", "get_diffuse_texture");
- ADD_GROUP("Normalmap", "normal_");
+ ADD_GROUP("NormalMap", "normal_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_texture", "get_normal_texture");
ADD_GROUP("Specular", "specular_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "specular_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_specular_texture", "get_specular_texture");
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 04376ad809..ad64f1ab5e 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,10 +32,8 @@
#define CANVAS_ITEM_H
#include "scene/main/node.h"
-#include "scene/main/scene_tree.h"
#include "scene/resources/canvas_item_material.h"
#include "scene/resources/font.h"
-#include "servers/text_server.h"
class CanvasLayer;
class MultiMesh;
@@ -46,6 +44,8 @@ class World2D;
class CanvasItem : public Node {
GDCLASS(CanvasItem, Node);
+ friend class CanvasLayer;
+
public:
enum TextureFilter {
TEXTURE_FILTER_PARENT_NODE,
@@ -85,6 +85,7 @@ private:
Window *window = nullptr;
bool first_draw = false;
bool visible = true;
+ bool parent_visible_in_tree = false;
bool clip_children = false;
bool pending_update = false;
bool top_level = false;
@@ -107,7 +108,8 @@ private:
void _top_level_raise_self();
- void _propagate_visibility_changed(bool p_visible);
+ void _propagate_visibility_changed(bool p_parent_visible_in_tree);
+ void _handle_visibility_change(bool p_visible);
void _update_callback();
@@ -118,9 +120,6 @@ private:
void _notify_transform(CanvasItem *p_node);
- void _set_on_top(bool p_on_top) { set_draw_behind_parent(!p_on_top); }
- bool _is_on_top() const { return !is_draw_behind_parent_enabled(); }
-
static CanvasItem *current_item_drawn;
friend class Viewport;
void _update_texture_repeat_changed(bool p_propagate);
@@ -215,7 +214,8 @@ public:
/* DRAWING API */
- void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0);
+ void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, real_t p_dash = 2.0);
+ void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false);
void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false);
void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = 1.0, bool p_antialiased = false);
void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false);
@@ -235,8 +235,8 @@ public:
void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture);
- void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, real_t p_width = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0));
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index cd7534f73c..da96246de2 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,10 @@
/*************************************************************************/
#include "canvas_layer.h"
-#include "viewport.h"
+
+#include "scene/main/canvas_item.h"
+#include "scene/main/viewport.h"
+#include "scene/resources/world_2d.h"
void CanvasLayer::set_layer(int p_xform) {
layer = p_xform;
@@ -42,6 +45,36 @@ int CanvasLayer::get_layer() const {
return layer;
}
+void CanvasLayer::set_visible(bool p_visible) {
+ if (p_visible == visible) {
+ return;
+ }
+
+ visible = p_visible;
+ emit_signal(SNAME("visibility_changed"));
+
+ for (int i = 0; i < get_child_count(); i++) {
+ CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
+ if (c) {
+ RenderingServer::get_singleton()->canvas_item_set_visible(c->get_canvas_item(), p_visible && c->is_visible());
+
+ c->_propagate_visibility_changed(p_visible);
+ }
+ }
+}
+
+void CanvasLayer::show() {
+ set_visible(true);
+}
+
+void CanvasLayer::hide() {
+ set_visible(false);
+}
+
+bool CanvasLayer::is_visible() const {
+ return visible;
+}
+
void CanvasLayer::set_transform(const Transform2D &p_xform) {
transform = p_xform;
locrotscale_dirty = true;
@@ -63,7 +96,7 @@ void CanvasLayer::_update_xform() {
}
void CanvasLayer::_update_locrotscale() {
- ofs = transform.elements[2];
+ ofs = transform.columns[2];
rot = transform.get_rotation();
scale = transform.get_scale();
locrotscale_dirty = false;
@@ -137,8 +170,8 @@ void CanvasLayer::_notification(int p_what) {
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
_update_follow_viewport();
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized.");
@@ -146,13 +179,12 @@ void CanvasLayer::_notification(int p_what) {
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
_update_follow_viewport(false);
-
} break;
+
case NOTIFICATION_MOVED_IN_PARENT: {
if (is_inside_tree()) {
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
}
-
} break;
}
}
@@ -264,6 +296,11 @@ void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer);
ClassDB::bind_method(D_METHOD("get_layer"), &CanvasLayer::get_layer);
+ ClassDB::bind_method(D_METHOD("set_visible", "visible"), &CanvasLayer::set_visible);
+ ClassDB::bind_method(D_METHOD("is_visible"), &CanvasLayer::is_visible);
+ ClassDB::bind_method(D_METHOD("show"), &CanvasLayer::show);
+ ClassDB::bind_method(D_METHOD("hide"), &CanvasLayer::hide);
+
ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CanvasLayer::set_transform);
ClassDB::bind_method(D_METHOD("get_transform"), &CanvasLayer::get_transform);
@@ -289,6 +326,7 @@ void CanvasLayer::_bind_methods() {
ADD_GROUP("Layer", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_GROUP("Transform", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
@@ -299,6 +337,8 @@ void CanvasLayer::_bind_methods() {
ADD_GROUP("Follow Viewport", "follow_viewport");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enable"), "set_follow_viewport", "is_following_viewport");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_lesser"), "set_follow_viewport_scale", "get_follow_viewport_scale");
+
+ ADD_SIGNAL(MethodInfo("visibility_changed"));
}
CanvasLayer::CanvasLayer() {
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
index 9d8e0c203d..2493675b31 100644
--- a/scene/main/canvas_layer.h
+++ b/scene/main/canvas_layer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -52,6 +52,7 @@ class CanvasLayer : public Node {
Viewport *vp = nullptr;
int sort_index = 0;
+ bool visible = true;
bool follow_viewport = false;
float follow_viewport_scale = 1.0;
@@ -69,6 +70,11 @@ public:
void set_layer(int p_xform);
int get_layer() const;
+ void set_visible(bool p_visible);
+ bool is_visible() const;
+ void show();
+ void hide();
+
void set_transform(const Transform2D &p_xform);
Transform2D get_transform() const;
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index a4fcc04e20..34b0e19d31 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,7 +46,7 @@ Error HTTPRequest::_parse_url(const String &p_url) {
request_sent = false;
got_response = false;
body_len = -1;
- body.resize(0);
+ body.clear();
downloaded.set(0);
redirections = 0;
@@ -86,9 +86,9 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S
String lowwer_case_header_name = p_header_name.to_lower();
for (int i = 0; i < p_headers.size(); i++) {
- if (p_headers[i].find(":", 0) >= 0) {
+ if (p_headers[i].find(":") > 0) {
Vector<String> parts = p_headers[i].split(":", false, 1);
- if (parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
+ if (parts.size() > 1 && parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
value = parts[1].strip_edges();
break;
}
@@ -99,7 +99,7 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S
}
Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String &p_request_data) {
- // Copy the string into a raw buffer
+ // Copy the string into a raw buffer.
Vector<uint8_t> raw_data;
CharString charstr = p_request_data.utf8();
@@ -134,7 +134,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust
headers = p_custom_headers;
if (accept_gzip) {
- // If the user has specified a different Accept-Encoding, don't overwrite it
+ // If the user has specified an Accept-Encoding header, don't overwrite it.
if (!has_header(headers, "Accept-Encoding")) {
headers.push_back("Accept-Encoding: gzip, deflate");
}
@@ -164,7 +164,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust
}
void HTTPRequest::_thread_func(void *p_userdata) {
- HTTPRequest *hr = (HTTPRequest *)p_userdata;
+ HTTPRequest *hr = static_cast<HTTPRequest *>(p_userdata);
Error err = hr->_request();
@@ -197,12 +197,9 @@ void HTTPRequest::cancel_request() {
thread.wait_to_finish();
}
- if (file) {
- memdelete(file);
- file = nullptr;
- }
+ file.unref();
client->close();
- body.resize(0);
+ body.clear();
got_response = false;
response_code = -1;
request_sent = false;
@@ -220,14 +217,14 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
response_code = client->get_response_code();
List<String> rheaders;
client->get_response_headers(&rheaders);
- response_headers.resize(0);
+ response_headers.clear();
downloaded.set(0);
for (const String &E : rheaders) {
response_headers.push_back(E);
}
if (response_code == 301 || response_code == 302) {
- // Handle redirect
+ // Handle redirect.
if (max_redirects >= 0 && redirections >= max_redirects) {
call_deferred(SNAME("_request_done"), RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray());
@@ -243,13 +240,13 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
}
}
- if (new_request != "") {
- // Process redirect
+ if (!new_request.is_empty()) {
+ // Process redirect.
client->close();
- int new_redirs = redirections + 1; // Because _request() will clear it
+ int new_redirs = redirections + 1; // Because _request() will clear it.
Error err;
if (new_request.begins_with("http")) {
- // New url, request all again
+ // New url, new request.
_parse_url(new_request);
} else {
request_string = new_request;
@@ -260,7 +257,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
request_sent = false;
got_response = false;
body_len = -1;
- body.resize(0);
+ body.clear();
downloaded.set(0);
redirections = new_redirs;
*ret_value = false;
@@ -276,11 +273,11 @@ bool HTTPRequest::_update_connection() {
switch (client->get_status()) {
case HTTPClient::STATUS_DISCONNECTED: {
call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
- return true; // End it, since it's doing something
+ return true; // End it, since it's disconnected.
} break;
case HTTPClient::STATUS_RESOLVING: {
client->poll();
- // Must wait
+ // Must wait.
return false;
} break;
case HTTPClient::STATUS_CANT_RESOLVE: {
@@ -290,9 +287,9 @@ bool HTTPRequest::_update_connection() {
} break;
case HTTPClient::STATUS_CONNECTING: {
client->poll();
- // Must wait
+ // Must wait.
return false;
- } break; // Connecting to IP
+ } break; // Connecting to IP.
case HTTPClient::STATUS_CANT_CONNECT: {
call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
return true;
@@ -301,7 +298,7 @@ bool HTTPRequest::_update_connection() {
case HTTPClient::STATUS_CONNECTED: {
if (request_sent) {
if (!got_response) {
- // No body
+ // No body.
bool ret_value;
@@ -313,16 +310,16 @@ bool HTTPRequest::_update_connection() {
return true;
}
if (body_len < 0) {
- // Chunked transfer is done
+ // Chunked transfer is done.
call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
call_deferred(SNAME("_request_done"), RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray());
return true;
- // Request might have been done
+ // Request might have been done.
} else {
- // Did not request yet, do request
+ // Did not request yet, do request.
int size = request_data.size();
Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size);
@@ -334,13 +331,13 @@ bool HTTPRequest::_update_connection() {
request_sent = true;
return false;
}
- } break; // Connected: break requests only accepted here
+ } break; // Connected: break requests only accepted here.
case HTTPClient::STATUS_REQUESTING: {
- // Must wait, still requesting
+ // Must wait, still requesting.
client->poll();
return false;
- } break; // Request in progress
+ } break; // Request in progress.
case HTTPClient::STATUS_BODY: {
if (!got_response) {
bool ret_value;
@@ -363,9 +360,9 @@ bool HTTPRequest::_update_connection() {
return true;
}
- if (download_to_file != String()) {
+ if (!download_to_file.is_empty()) {
file = FileAccess::open(download_to_file, FileAccess::WRITE);
- if (!file) {
+ if (file.is_null()) {
call_deferred(SNAME("_request_done"), RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray());
return true;
}
@@ -381,7 +378,7 @@ bool HTTPRequest::_update_connection() {
if (chunk.size()) {
downloaded.add(chunk.size());
- if (file) {
+ if (file.is_valid()) {
const uint8_t *r = chunk.ptr();
file->store_buffer(r, chunk.size());
if (file->get_error() != OK) {
@@ -411,7 +408,7 @@ bool HTTPRequest::_update_connection() {
return false;
- } break; // Request resulted in body: break which must be read
+ } break; // Request resulted in body: break which must be read.
case HTTPClient::STATUS_CONNECTION_ERROR: {
call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
@@ -428,7 +425,7 @@ bool HTTPRequest::_update_connection() {
void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
cancel_request();
- // Determine if the request body is compressed
+ // Determine if the request body is compressed.
bool is_compressed;
String content_encoding = get_header_value(p_headers, "Content-Encoding").to_lower();
Compression::Mode mode;
@@ -442,48 +439,44 @@ void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArra
is_compressed = false;
}
- const PackedByteArray *data = nullptr;
-
if (accept_gzip && is_compressed && p_data.size() > 0) {
// Decompress request body
- PackedByteArray *decompressed = memnew(PackedByteArray);
- int result = Compression::decompress_dynamic(decompressed, body_size_limit, p_data.ptr(), p_data.size(), mode);
+ PackedByteArray decompressed;
+ int result = Compression::decompress_dynamic(&decompressed, body_size_limit, p_data.ptr(), p_data.size(), mode);
if (result == OK) {
- data = decompressed;
+ emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, decompressed);
+ return;
} else if (result == -5) {
WARN_PRINT("Decompressed size of HTTP response body exceeded body_size_limit");
p_status = RESULT_BODY_SIZE_LIMIT_EXCEEDED;
- // Just return the raw data if we failed to decompress it
- data = &p_data;
+ // Just return the raw data if we failed to decompress it.
} else {
WARN_PRINT("Failed to decompress HTTP response body");
p_status = RESULT_BODY_DECOMPRESS_FAILED;
- // Just return the raw data if we failed to decompress it
- data = &p_data;
+ // Just return the raw data if we failed to decompress it.
}
- } else {
- data = &p_data;
}
- emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, *data);
+ emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, p_data);
}
void HTTPRequest::_notification(int p_what) {
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- if (use_threads.is_set()) {
- return;
- }
- bool done = _update_connection();
- if (done) {
- set_process_internal(false);
- // cancel_request(); called from _request done now
- }
- }
+ switch (p_what) {
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (use_threads.is_set()) {
+ return;
+ }
+ bool done = _update_connection();
+ if (done) {
+ set_process_internal(false);
+ }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE) {
- if (requesting) {
- cancel_request();
- }
+ case NOTIFICATION_EXIT_TREE: {
+ if (requesting) {
+ cancel_request();
+ }
+ } break;
}
}
@@ -554,12 +547,20 @@ int HTTPRequest::get_body_size() const {
return body_len;
}
-void HTTPRequest::set_timeout(int p_timeout) {
+void HTTPRequest::set_http_proxy(const String &p_host, int p_port) {
+ client->set_http_proxy(p_host, p_port);
+}
+
+void HTTPRequest::set_https_proxy(const String &p_host, int p_port) {
+ client->set_https_proxy(p_host, p_port);
+}
+
+void HTTPRequest::set_timeout(double p_timeout) {
ERR_FAIL_COND(p_timeout < 0);
timeout = p_timeout;
}
-int HTTPRequest::get_timeout() {
+double HTTPRequest::get_timeout() {
return timeout;
}
@@ -602,18 +603,20 @@ void HTTPRequest::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_download_chunk_size", "chunk_size"), &HTTPRequest::set_download_chunk_size);
ClassDB::bind_method(D_METHOD("get_download_chunk_size"), &HTTPRequest::get_download_chunk_size);
+ ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPRequest::set_http_proxy);
+ ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPRequest::set_https_proxy);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file");
ADD_PROPERTY(PropertyInfo(Variant::INT, "download_chunk_size", PROPERTY_HINT_RANGE, "256,16777216"), "set_download_chunk_size", "get_download_chunk_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "accept_gzip"), "set_accept_gzip", "is_accepting_gzip");
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_size_limit", PROPERTY_HINT_RANGE, "-1,2000000000"), "set_body_size_limit", "get_body_size_limit");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,64"), "set_max_redirects", "get_max_redirects");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "timeout", PROPERTY_HINT_RANGE, "0,86400"), "set_timeout", "get_timeout");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "timeout", PROPERTY_HINT_RANGE, "0,3600,0.1,or_greater"), "set_timeout", "get_timeout");
ADD_SIGNAL(MethodInfo("request_completed", PropertyInfo(Variant::INT, "result"), PropertyInfo(Variant::INT, "response_code"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "headers"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "body")));
BIND_ENUM_CONSTANT(RESULT_SUCCESS);
- //BIND_ENUM_CONSTANT( RESULT_NO_BODY );
BIND_ENUM_CONSTANT(RESULT_CHUNKED_BODY_SIZE_MISMATCH);
BIND_ENUM_CONSTANT(RESULT_CANT_CONNECT);
BIND_ENUM_CONSTANT(RESULT_CANT_RESOLVE);
@@ -636,9 +639,3 @@ HTTPRequest::HTTPRequest() {
timer->connect("timeout", callable_mp(this, &HTTPRequest::_timeout));
add_child(timer);
}
-
-HTTPRequest::~HTTPRequest() {
- if (file) {
- memdelete(file);
- }
-}
diff --git a/scene/main/http_request.h b/scene/main/http_request.h
index 673cf3a740..49b4b1b30c 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -84,7 +84,7 @@ private:
String download_to_file;
- FileAccess *file = nullptr;
+ Ref<FileAccess> file;
int body_len = -1;
SafeNumeric<int> downloaded;
@@ -96,7 +96,7 @@ private:
int max_redirects = 8;
- int timeout = 0;
+ double timeout = 0;
void _redirect_request(const String &p_new_url);
@@ -144,18 +144,20 @@ public:
void set_max_redirects(int p_max);
int get_max_redirects() const;
- Timer *timer;
+ Timer *timer = nullptr;
- void set_timeout(int p_timeout);
- int get_timeout();
+ void set_timeout(double p_timeout);
+ double get_timeout();
void _timeout();
int get_downloaded_bytes() const;
int get_body_size() const;
+ void set_http_proxy(const String &p_host, int p_port);
+ void set_https_proxy(const String &p_host, int p_port);
+
HTTPRequest();
- ~HTTPRequest();
};
VARIANT_ENUM_CAST(HTTPRequest::Result);
diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp
index b5ba1899ec..6dd83e4636 100644
--- a/scene/main/instance_placeholder.cpp
+++ b/scene/main/instance_placeholder.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h
index fe20fc4760..8f2eb01773 100644
--- a/scene/main/instance_placeholder.h
+++ b/scene/main/instance_placeholder.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/main/missing_node.cpp b/scene/main/missing_node.cpp
new file mode 100644
index 0000000000..395fdad9e4
--- /dev/null
+++ b/scene/main/missing_node.cpp
@@ -0,0 +1,98 @@
+/*************************************************************************/
+/* missing_node.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 "missing_node.h"
+
+bool MissingNode::_set(const StringName &p_name, const Variant &p_value) {
+ if (is_recording_properties()) {
+ properties.insert(p_name, p_value);
+ return true; //always valid to set (add)
+ } else {
+ if (!properties.has(p_name)) {
+ return false;
+ }
+
+ properties[p_name] = p_value;
+ return true;
+ }
+}
+
+bool MissingNode::_get(const StringName &p_name, Variant &r_ret) const {
+ if (!properties.has(p_name)) {
+ return false;
+ }
+ r_ret = properties[p_name];
+ return true;
+}
+
+void MissingNode::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (const KeyValue<StringName, Variant> &E : properties) {
+ p_list->push_back(PropertyInfo(E.value.get_type(), E.key));
+ }
+}
+
+void MissingNode::set_original_class(const String &p_class) {
+ original_class = p_class;
+}
+
+String MissingNode::get_original_class() const {
+ return original_class;
+}
+
+void MissingNode::set_recording_properties(bool p_enable) {
+ recording_properties = p_enable;
+}
+
+bool MissingNode::is_recording_properties() const {
+ return recording_properties;
+}
+
+TypedArray<String> MissingNode::get_configuration_warnings() const {
+ // The mere existence of this node is warning.
+ TypedArray<String> ret;
+ ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class));
+ ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss."));
+ return ret;
+}
+
+void MissingNode::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingNode::set_original_class);
+ ClassDB::bind_method(D_METHOD("get_original_class"), &MissingNode::get_original_class);
+
+ ClassDB::bind_method(D_METHOD("set_recording_properties", "enable"), &MissingNode::set_recording_properties);
+ ClassDB::bind_method(D_METHOD("is_recording_properties"), &MissingNode::is_recording_properties);
+
+ // Expose, but not save.
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_class", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_class", "get_original_class");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recording_properties", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_recording_properties", "is_recording_properties");
+}
+
+MissingNode::MissingNode() {
+}
diff --git a/scene/main/missing_node.h b/scene/main/missing_node.h
new file mode 100644
index 0000000000..d200fbb47f
--- /dev/null
+++ b/scene/main/missing_node.h
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* missing_node.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 MISSING_NODE_H
+#define MISSING_NODE_H
+
+#include "core/io/missing_resource.h"
+#include "scene/main/node.h"
+
+class MissingNode : public Node {
+ GDCLASS(MissingNode, Node)
+ HashMap<StringName, Variant> properties;
+
+ String original_class;
+ bool recording_properties = false;
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+public:
+ void set_original_class(const String &p_class);
+ String get_original_class() const;
+
+ void set_recording_properties(bool p_enable);
+ bool is_recording_properties() const;
+
+ virtual TypedArray<String> get_configuration_warnings() const override;
+
+ MissingNode();
+};
+
+#endif // MISSING_NODE_H
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 44420fcc31..0c1a62c667 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,10 @@
#include "node.h"
+#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"
@@ -52,12 +54,12 @@ void Node::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_PROCESS: {
GDVIRTUAL_CALL(_process, get_process_delta_time());
-
} break;
+
case NOTIFICATION_PHYSICS_PROCESS: {
GDVIRTUAL_CALL(_physics_process, get_physics_process_delta_time());
-
} break;
+
case NOTIFICATION_ENTER_TREE: {
ERR_FAIL_COND(!get_viewport());
ERR_FAIL_COND(!get_tree());
@@ -77,6 +79,9 @@ void Node::_notification(int p_notification) {
if (data.input) {
add_to_group("_vp_input" + itos(get_viewport()->get_instance_id()));
}
+ if (data.shortcut_input) {
+ add_to_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id()));
+ }
if (data.unhandled_input) {
add_to_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id()));
}
@@ -86,8 +91,8 @@ void Node::_notification(int p_notification) {
get_tree()->node_count++;
orphan_node_count--;
-
} break;
+
case NOTIFICATION_EXIT_TREE: {
ERR_FAIL_COND(!get_viewport());
ERR_FAIL_COND(!get_tree());
@@ -98,6 +103,9 @@ void Node::_notification(int p_notification) {
if (data.input) {
remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id()));
}
+ if (data.shortcut_input) {
+ remove_from_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id()));
+ }
if (data.unhandled_input) {
remove_from_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id()));
}
@@ -110,21 +118,24 @@ void Node::_notification(int p_notification) {
memdelete(data.path_cache);
data.path_cache = nullptr;
}
- if (data.scene_file_path.length()) {
- get_multiplayer()->scene_enter_exit_notify(data.scene_file_path, this, false);
- }
} break;
- case NOTIFICATION_PATH_CHANGED: {
+
+ case NOTIFICATION_PATH_RENAMED: {
if (data.path_cache) {
memdelete(data.path_cache);
data.path_cache = nullptr;
}
} break;
+
case NOTIFICATION_READY: {
if (GDVIRTUAL_IS_OVERRIDDEN(_input)) {
set_process_input(true);
}
+ if (GDVIRTUAL_IS_OVERRIDDEN(_shortcut_input)) {
+ set_process_shortcut_input(true);
+ }
+
if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) {
set_process_unhandled_input(true);
}
@@ -141,23 +152,13 @@ void Node::_notification(int p_notification) {
}
GDVIRTUAL_CALL(_ready);
-
- if (data.scene_file_path.length()) {
- ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer()->scene_enter_exit_notify(data.scene_file_path, this, true);
- }
-
} break;
+
case NOTIFICATION_POSTINITIALIZE: {
data.in_constructor = false;
} break;
- case NOTIFICATION_PREDELETE: {
- set_owner(nullptr);
-
- while (data.owned.size()) {
- data.owned.front()->get()->set_owner(nullptr);
- }
+ case NOTIFICATION_PREDELETE: {
if (data.parent) {
data.parent->remove_child(this);
}
@@ -165,10 +166,8 @@ void Node::_notification(int p_notification) {
// kill children as cleanly as possible
while (data.children.size()) {
Node *child = data.children[data.children.size() - 1]; //begin from the end because its faster and more consistent with creation
- remove_child(child);
memdelete(child);
}
-
} break;
}
}
@@ -219,6 +218,12 @@ void Node::_propagate_enter_tree() {
data.tree->node_added(this);
+ if (data.parent) {
+ Variant c = this;
+ const Variant *cptr = &c;
+ data.parent->emit_signalp(SNAME("child_entered_tree"), &cptr, 1);
+ }
+
data.blocked++;
//block while adding children
@@ -237,11 +242,35 @@ void Node::_propagate_enter_tree() {
}
void Node::_propagate_after_exit_tree() {
+ // Clear owner if it was not part of the pruned branch
+ if (data.owner) {
+ bool found = false;
+ Node *parent = data.parent;
+
+ while (parent) {
+ if (parent == data.owner) {
+ found = true;
+ break;
+ }
+
+ parent = parent->data.parent;
+ }
+
+ if (!found) {
+ if (data.unique_name_in_owner) {
+ _release_unique_name_in_owner();
+ }
+ data.owner->data.owned.erase(data.OW);
+ data.owner = nullptr;
+ }
+ }
+
data.blocked++;
- for (int i = 0; i < data.children.size(); i++) {
+ for (int i = data.children.size() - 1; i >= 0; i--) {
data.children[i]->_propagate_after_exit_tree();
}
data.blocked--;
+
emit_signal(SceneStringNames::get_singleton()->tree_exited);
}
@@ -268,6 +297,12 @@ void Node::_propagate_exit_tree() {
data.tree->node_removed(this);
}
+ if (data.parent) {
+ Variant c = this;
+ const Variant *cptr = &c;
+ data.parent->emit_signalp(SNAME("child_exited_tree"), &cptr, 1);
+ }
+
// exit groups
for (KeyValue<StringName, GroupData> &E : data.grouped) {
@@ -332,7 +367,7 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) {
int motion_from = MIN(p_pos, p_child->data.pos);
int motion_to = MAX(p_pos, p_child->data.pos);
- data.children.remove(p_child->data.pos);
+ data.children.remove_at(p_child->data.pos);
data.children.insert(p_pos, p_child);
if (data.tree) {
@@ -560,84 +595,56 @@ uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc
/***** RPC FUNCTIONS ********/
-void Node::rpc(const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
-
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- rpcp(0, p_method, argptr, argc);
-}
-
-void Node::rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
-
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- rpcp(p_peer_id, p_method, argptr, argc);
-}
-
-Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void 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 Variant();
+ return;
}
- if (p_args[0]->get_type() != Variant::STRING_NAME) {
+ Variant::Type type = p_args[0]->get_type();
+ if (type != Variant::STRING_NAME && type != Variant::STRING) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- return Variant();
+ return;
}
- StringName method = *p_args[0];
+ StringName method = (*p_args[0]).operator StringName();
rpcp(0, method, &p_args[1], p_argcount - 1);
r_error.error = Callable::CallError::CALL_OK;
- return Variant();
}
-Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void 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 Variant();
+ return;
}
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 Variant();
+ return;
}
- if (p_args[1]->get_type() != Variant::STRING_NAME) {
+ Variant::Type type = p_args[1]->get_type();
+ if (type != Variant::STRING_NAME && type != Variant::STRING) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
- return Variant();
+ return;
}
int peer_id = *p_args[0];
- StringName method = *p_args[1];
+ StringName method = (*p_args[1]).operator StringName();
rpcp(peer_id, method, &p_args[2], p_argcount - 2);
r_error.error = Callable::CallError::CALL_OK;
- return Variant();
}
void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
@@ -646,21 +653,10 @@ void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
- if (multiplayer.is_valid()) {
- return multiplayer;
- }
if (!is_inside_tree()) {
return Ref<MultiplayerAPI>();
}
- return get_tree()->get_multiplayer();
-}
-
-Ref<MultiplayerAPI> Node::get_custom_multiplayer() const {
- return multiplayer;
-}
-
-void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- multiplayer = p_multiplayer;
+ return get_tree()->get_multiplayer(get_path());
}
Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
@@ -841,6 +837,26 @@ bool Node::is_processing_input() const {
return data.input;
}
+void Node::set_process_shortcut_input(bool p_enable) {
+ if (p_enable == data.shortcut_input) {
+ return;
+ }
+ data.shortcut_input = p_enable;
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (p_enable) {
+ add_to_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id()));
+ } else {
+ remove_from_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id()));
+ }
+}
+
+bool Node::is_processing_shortcut_input() const {
+ return data.shortcut_input;
+}
+
void Node::set_process_unhandled_input(bool p_enable) {
if (p_enable == data.unhandled_input) {
return;
@@ -892,14 +908,22 @@ void Node::_set_name_nocheck(const StringName &p_name) {
void Node::set_name(const String &p_name) {
String name = p_name.validate_node_name();
- ERR_FAIL_COND(name == "");
+ ERR_FAIL_COND(name.is_empty());
+
+ if (data.unique_name_in_owner && data.owner) {
+ _release_unique_name_in_owner();
+ }
data.name = name;
if (data.parent) {
- data.parent->_validate_child_name(this);
+ data.parent->_validate_child_name(this, true);
}
- propagate_notification(NOTIFICATION_PATH_CHANGED);
+ if (data.unique_name_in_owner && data.owner) {
+ _acquire_unique_name_in_owner();
+ }
+
+ propagate_notification(NOTIFICATION_PATH_RENAMED);
if (is_inside_tree()) {
emit_signal(SNAME("renamed"));
@@ -1036,7 +1060,7 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
String nums;
for (int i = name_string.length() - 1; i >= 0; i--) {
char32_t n = name_string[i];
- if (n >= '0' && n <= '9') {
+ if (is_digit(n)) {
nums = String::chr(name_string[i]) + nums;
} else {
break;
@@ -1144,31 +1168,6 @@ void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) {
data.parent->_move_child(p_sibling, get_index() + 1);
}
-void Node::_propagate_validate_owner() {
- if (data.owner) {
- bool found = false;
- Node *parent = data.parent;
-
- while (parent) {
- if (parent == data.owner) {
- found = true;
- break;
- }
-
- parent = parent->data.parent;
- }
-
- if (!found) {
- data.owner->data.owned.erase(data.OW);
- data.owner = nullptr;
- }
- }
-
- for (int i = 0; i < data.children.size(); i++) {
- data.children[i]->_propagate_validate_owner();
- }
-}
-
void Node::remove_child(Node *p_child) {
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, remove_node() failed. Consider using call_deferred(\"remove_child\", child) instead.");
@@ -1208,7 +1207,7 @@ void Node::remove_child(Node *p_child) {
remove_child_notify(p_child);
p_child->notification(NOTIFICATION_UNPARENTED);
- data.children.remove(idx);
+ data.children.remove_at(idx);
//update pointer and size
child_count = data.children.size();
@@ -1222,9 +1221,6 @@ void Node::remove_child(Node *p_child) {
p_child->data.parent = nullptr;
p_child->data.pos = -1;
- // validate owner
- p_child->_propagate_validate_owner();
-
if (data.inside_tree) {
p_child->_propagate_after_exit_tree();
}
@@ -1307,6 +1303,24 @@ Node *Node::get_node_or_null(const NodePath &p_path) const {
next = root;
}
+ } else if (name.is_node_unique_name()) {
+ if (current->data.owned_unique_nodes.size()) {
+ // Has unique nodes in ownership
+ Node **unique = current->data.owned_unique_nodes.getptr(name);
+ if (!unique) {
+ return nullptr;
+ }
+ next = *unique;
+ } else if (current->data.owner) {
+ Node **unique = current->data.owner->data.owned_unique_nodes.getptr(name);
+ if (!unique) {
+ return nullptr;
+ }
+ next = *unique;
+ } else {
+ return nullptr;
+ }
+
} else {
next = nullptr;
@@ -1331,12 +1345,14 @@ Node *Node::get_node_or_null(const NodePath &p_path) const {
Node *Node::get_node(const NodePath &p_path) const {
Node *node = get_node_or_null(p_path);
- if (p_path.is_absolute()) {
- ERR_FAIL_COND_V_MSG(!node, nullptr,
- vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path()));
- } else {
- ERR_FAIL_COND_V_MSG(!node, nullptr,
- vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path()));
+ if (unlikely(!node)) {
+ if (p_path.is_absolute()) {
+ ERR_FAIL_V_MSG(nullptr,
+ vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path()));
+ } else {
+ ERR_FAIL_V_MSG(nullptr,
+ vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path()));
+ }
}
return node;
@@ -1346,14 +1362,18 @@ bool Node::has_node(const NodePath &p_path) const {
return get_node_or_null(p_path) != nullptr;
}
-Node *Node::find_node(const String &p_mask, bool p_recursive, bool p_owned) const {
+// Finds the first child node (in tree order) whose name matches the given pattern.
+// Can be recursive or not, and limited to owned nodes.
+Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) const {
+ ERR_FAIL_COND_V(p_pattern.is_empty(), nullptr);
+
Node *const *cptr = data.children.ptr();
int ccount = data.children.size();
for (int i = 0; i < ccount; i++) {
if (p_owned && !cptr[i]->data.owner) {
continue;
}
- if (cptr[i]->data.name.operator String().match(p_mask)) {
+ if (cptr[i]->data.name.operator String().match(p_pattern)) {
return cptr[i];
}
@@ -1361,7 +1381,7 @@ Node *Node::find_node(const String &p_mask, bool p_recursive, bool p_owned) cons
continue;
}
- Node *ret = cptr[i]->find_node(p_mask, true, p_owned);
+ Node *ret = cptr[i]->find_child(p_pattern, true, p_owned);
if (ret) {
return ret;
}
@@ -1369,14 +1389,58 @@ Node *Node::find_node(const String &p_mask, bool p_recursive, bool p_owned) cons
return nullptr;
}
+// Finds child nodes based on their name using pattern matching, or class name,
+// or both (either pattern or type can be left empty).
+// Can be recursive or not, and limited to owned nodes.
+TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_type, bool p_recursive, bool p_owned) const {
+ TypedArray<Node> ret;
+ ERR_FAIL_COND_V(p_pattern.is_empty() && p_type.is_empty(), ret);
+
+ Node *const *cptr = data.children.ptr();
+ int ccount = data.children.size();
+ for (int i = 0; i < ccount; i++) {
+ if (p_owned && !cptr[i]->data.owner) {
+ continue;
+ }
+
+ if (!p_pattern.is_empty()) {
+ if (!cptr[i]->data.name.operator String().match(p_pattern)) {
+ continue;
+ } else if (p_type.is_empty()) {
+ ret.append(cptr[i]);
+ }
+ }
+
+ if (cptr[i]->is_class(p_type)) {
+ ret.append(cptr[i]);
+ } else if (cptr[i]->get_script_instance()) {
+ Ref<Script> script = cptr[i]->get_script_instance()->get_script();
+ while (script.is_valid()) {
+ if ((ScriptServer::is_global_class(p_type) && ScriptServer::get_global_class_path(p_type) == script->get_path()) || p_type == script->get_path()) {
+ ret.append(cptr[i]);
+ break;
+ }
+
+ script = script->get_base_script();
+ }
+ }
+
+ if (p_recursive) {
+ ret.append_array(cptr[i]->find_children(p_pattern, p_type, true, p_owned));
+ }
+ }
+
+ return ret;
+}
+
Node *Node::get_parent() const {
return data.parent;
}
-Node *Node::find_parent(const String &p_mask) const {
+Node *Node::find_parent(const String &p_pattern) const {
Node *p = data.parent;
while (p) {
- if (p->data.name.operator String().match(p_mask)) {
+ if (p->data.name.operator String().match(p_pattern)) {
return p;
}
p = p->data.parent;
@@ -1482,8 +1546,56 @@ void Node::_set_owner_nocheck(Node *p_owner) {
data.OW = data.owner->data.owned.back();
}
+void Node::_release_unique_name_in_owner() {
+ ERR_FAIL_NULL(data.owner); // Sanity check.
+ StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String());
+ Node **which = data.owner->data.owned_unique_nodes.getptr(key);
+ if (which == nullptr || *which != this) {
+ return; // Ignore.
+ }
+ data.owner->data.owned_unique_nodes.erase(key);
+}
+
+void Node::_acquire_unique_name_in_owner() {
+ ERR_FAIL_NULL(data.owner); // Sanity check.
+ StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String());
+ Node **which = data.owner->data.owned_unique_nodes.getptr(key);
+ if (which != nullptr && *which != this) {
+ String which_path = is_inside_tree() ? (*which)->get_path() : data.owner->get_path_to(*which);
+ WARN_PRINT(vformat(RTR("Setting node name '%s' to be unique within scene for '%s', but it's already claimed by '%s'.\n'%s' is no longer set as having a unique name."),
+ get_name(), is_inside_tree() ? get_path() : data.owner->get_path_to(this), which_path, which_path));
+ data.unique_name_in_owner = false;
+ return;
+ }
+ data.owner->data.owned_unique_nodes[key] = this;
+}
+
+void Node::set_unique_name_in_owner(bool p_enabled) {
+ if (data.unique_name_in_owner == p_enabled) {
+ return;
+ }
+
+ if (data.unique_name_in_owner && data.owner != nullptr) {
+ _release_unique_name_in_owner();
+ }
+ data.unique_name_in_owner = p_enabled;
+
+ if (data.unique_name_in_owner && data.owner != nullptr) {
+ _acquire_unique_name_in_owner();
+ }
+
+ update_configuration_warnings();
+}
+
+bool Node::is_unique_name_in_owner() const {
+ return data.unique_name_in_owner;
+}
+
void Node::set_owner(Node *p_owner) {
if (data.owner) {
+ if (data.unique_name_in_owner) {
+ _release_unique_name_in_owner();
+ }
data.owner->data.owned.erase(data.OW);
data.OW = nullptr;
data.owner = nullptr;
@@ -1510,6 +1622,10 @@ void Node::set_owner(Node *p_owner) {
ERR_FAIL_COND(!owner_valid);
_set_owner_nocheck(p_owner);
+
+ if (data.unique_name_in_owner) {
+ _acquire_unique_name_in_owner();
+ }
}
Node *Node::get_owner() const {
@@ -1521,7 +1637,7 @@ Node *Node::find_common_parent_with(const Node *p_node) const {
return const_cast<Node *>(p_node);
}
- Set<const Node *> visited;
+ RBSet<const Node *> visited;
const Node *n = this;
@@ -1553,7 +1669,7 @@ NodePath Node::get_path_to(const Node *p_node) const {
return NodePath(".");
}
- Set<const Node *> visited;
+ RBSet<const Node *> visited;
const Node *n = this;
@@ -1647,15 +1763,15 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) {
void Node::remove_from_group(const StringName &p_identifier) {
ERR_FAIL_COND(!data.grouped.has(p_identifier));
- Map<StringName, GroupData>::Element *E = data.grouped.find(p_identifier);
+ HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier);
ERR_FAIL_COND(!E);
if (data.tree) {
- data.tree->remove_from_group(E->key(), this);
+ data.tree->remove_from_group(E->key, this);
}
- data.grouped.erase(E);
+ data.grouped.remove(E);
}
Array Node::_get_groups() const {
@@ -1896,35 +2012,28 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const {
#ifdef TOOLS_ENABLED
void Node::set_property_pinned(const String &p_property, bool p_pinned) {
bool current_pinned = false;
- bool has_pinned = has_meta("_edit_pinned_properties_");
- Array pinned;
- String psa = get_property_store_alias(p_property);
- if (has_pinned) {
- pinned = get_meta("_edit_pinned_properties_");
- current_pinned = pinned.has(psa);
- }
+ Array pinned = get_meta("_edit_pinned_properties_", Array());
+ StringName psa = get_property_store_alias(p_property);
+ current_pinned = pinned.has(psa);
if (current_pinned != p_pinned) {
if (p_pinned) {
pinned.append(psa);
- if (!has_pinned) {
- set_meta("_edit_pinned_properties_", pinned);
- }
} else {
pinned.erase(psa);
- if (pinned.is_empty()) {
- remove_meta("_edit_pinned_properties_");
- }
}
}
+
+ if (pinned.is_empty()) {
+ remove_meta("_edit_pinned_properties_");
+ } else {
+ set_meta("_edit_pinned_properties_", pinned);
+ }
}
bool Node::is_property_pinned(const StringName &p_property) const {
- if (!has_meta("_edit_pinned_properties_")) {
- return false;
- }
- Array pinned = get_meta("_edit_pinned_properties_");
- String psa = get_property_store_alias(p_property);
+ Array pinned = get_meta("_edit_pinned_properties_", Array());
+ StringName psa = get_property_store_alias(p_property);
return pinned.has(psa);
}
@@ -1933,7 +2042,7 @@ StringName Node::get_property_store_alias(const StringName &p_property) const {
}
#endif
-void Node::get_storable_properties(Set<StringName> &r_storable_properties) const {
+void Node::get_storable_properties(RBSet<StringName> &r_storable_properties) const {
List<PropertyInfo> pi;
get_property_list(&pi);
for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
@@ -1979,7 +2088,7 @@ bool Node::get_scene_instance_load_placeholder() const {
return data.use_placeholder;
}
-Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const {
+Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) const {
Node *node = nullptr;
bool instantiated = false;
@@ -1990,7 +2099,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
nip->set_instance_path(ip->get_instance_path());
node = nip;
- } else if ((p_flags & DUPLICATE_USE_INSTANCING) && get_scene_file_path() != String()) {
+ } else if ((p_flags & DUPLICATE_USE_INSTANCING) && !get_scene_file_path().is_empty()) {
Ref<PackedScene> res = ResourceLoader::load(get_scene_file_path());
ERR_FAIL_COND_V(res.is_null(), nullptr);
PackedScene::GenEditState ges = PackedScene::GEN_EDIT_STATE_DISABLED;
@@ -2001,6 +2110,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
#endif
node = res->instantiate(ges);
ERR_FAIL_COND_V(!node, nullptr);
+ node->set_scene_instance_load_placeholder(get_scene_instance_load_placeholder());
instantiated = true;
@@ -2014,7 +2124,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
ERR_FAIL_COND_V(!node, nullptr);
}
- if (get_scene_file_path() != "") { //an instance
+ if (!get_scene_file_path().is_empty()) { //an instance
node->set_scene_file_path(get_scene_file_path());
node->data.editable_instance = data.editable_instance;
}
@@ -2046,7 +2156,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
node_tree.push_back(descendant);
- if (descendant->get_scene_file_path() != "" && instance_roots.has(descendant->get_owner())) {
+ if (!descendant->get_scene_file_path().is_empty() && instance_roots.has(descendant->get_owner())) {
instance_roots.push_back(descendant);
}
}
@@ -2170,11 +2280,11 @@ Node *Node::duplicate(int p_flags) const {
}
#ifdef TOOLS_ENABLED
-Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const {
- return duplicate_from_editor(r_duplimap, Map<RES, RES>());
+Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) const {
+ return duplicate_from_editor(r_duplimap, HashMap<Ref<Resource>, Ref<Resource>>());
}
-Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<RES, RES> &p_resource_remap) const {
+Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const {
Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap);
// This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated.
@@ -2190,7 +2300,7 @@ Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const M
return dupe;
}
-void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_remap) const {
+void Node::remap_node_resources(Node *p_node, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const {
List<PropertyInfo> props;
p_node->get_property_list(&props);
@@ -2200,8 +2310,8 @@ void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_re
}
Variant v = p_node->get(E.name);
- if (v.is_ref()) {
- RES res = v;
+ if (v.is_ref_counted()) {
+ Ref<Resource> res = v;
if (res.is_valid()) {
if (p_resource_remap.has(res)) {
p_node->set(E.name, p_resource_remap[res]);
@@ -2216,7 +2326,7 @@ void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_re
}
}
-void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resource_remap) const {
+void Node::remap_nested_resources(Ref<Resource> p_resource, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const {
List<PropertyInfo> props;
p_resource->get_property_list(&props);
@@ -2226,8 +2336,8 @@ void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resourc
}
Variant v = p_resource->get(E.name);
- if (v.is_ref()) {
- RES res = v;
+ if (v.is_ref_counted()) {
+ Ref<Resource> res = v;
if (res.is_valid()) {
if (p_resource_remap.has(res)) {
p_resource->set(E.name, p_resource_remap[res]);
@@ -2372,47 +2482,11 @@ void Node::_replace_connections_target(Node *p_new_target) {
}
}
-Vector<Variant> Node::make_binds(VARIANT_ARG_DECLARE) {
- Vector<Variant> ret;
-
- if (p_arg1.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg1);
- }
-
- if (p_arg2.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg2);
- }
-
- if (p_arg3.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg3);
- }
-
- if (p_arg4.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg4);
- }
-
- if (p_arg5.get_type() == Variant::NIL) {
- return ret;
- } else {
- ret.push_back(p_arg5);
- }
-
- return ret;
-}
-
bool Node::has_node_and_resource(const NodePath &p_path) const {
if (!has_node(p_path)) {
return false;
}
- RES res;
+ Ref<Resource> res;
Vector<StringName> leftover_path;
Node *node = get_node_and_resource(p_path, res, leftover_path, false);
@@ -2420,7 +2494,7 @@ bool Node::has_node_and_resource(const NodePath &p_path) const {
}
Array Node::_get_node_and_resource(const NodePath &p_path) {
- RES res;
+ Ref<Resource> res;
Vector<StringName> leftover_path;
Node *node = get_node_and_resource(p_path, res, leftover_path, false);
Array result;
@@ -2442,9 +2516,9 @@ Array Node::_get_node_and_resource(const NodePath &p_path) {
return result;
}
-Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const {
+Node *Node::get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const {
Node *node = get_node(p_path);
- r_res = RES();
+ r_res = Ref<Resource>();
r_leftover_subpath = Vector<StringName>();
if (!node) {
return nullptr;
@@ -2460,7 +2534,7 @@ Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<Str
return nullptr;
}
- RES new_res = new_res_v;
+ Ref<Resource> new_res = new_res_v;
if (new_res.is_null()) { // No longer a resource, assume property
break;
@@ -2534,11 +2608,11 @@ static void _Node_debug_sn(Object *p_obj) {
}
#endif // DEBUG_ENABLED
-void Node::_print_stray_nodes() {
- print_stray_nodes();
+void Node::_print_orphan_nodes() {
+ print_orphan_nodes();
}
-void Node::print_stray_nodes() {
+void Node::print_orphan_nodes() {
#ifdef DEBUG_ENABLED
ObjectDB::debug_objects(_Node_debug_sn);
#endif
@@ -2604,16 +2678,16 @@ void Node::clear_internal_tree_resource_paths() {
}
TypedArray<String> Node::get_configuration_warnings() const {
+ TypedArray<String> ret;
+
Vector<String> warnings;
if (GDVIRTUAL_CALL(_get_configuration_warnings, warnings)) {
- TypedArray<String> ret;
- ret.resize(warnings.size());
for (int i = 0; i < warnings.size(); i++) {
- ret[i] = warnings[i];
+ ret.push_back(warnings[i]);
}
- return ret;
}
- return Array();
+
+ return ret;
}
String Node::get_configuration_warnings_as_string() const {
@@ -2664,6 +2738,15 @@ void Node::_call_input(const Ref<InputEvent> &p_event) {
}
input(p_event);
}
+
+void Node::_call_shortcut_input(const Ref<InputEvent> &p_event) {
+ GDVIRTUAL_CALL(_shortcut_input, p_event);
+ if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) {
+ return;
+ }
+ shortcut_input(p_event);
+}
+
void Node::_call_unhandled_input(const Ref<InputEvent> &p_event) {
GDVIRTUAL_CALL(_unhandled_input, p_event);
if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) {
@@ -2671,6 +2754,7 @@ void Node::_call_unhandled_input(const Ref<InputEvent> &p_event) {
}
unhandled_input(p_event);
}
+
void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) {
GDVIRTUAL_CALL(_unhandled_key_input, p_event);
if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) {
@@ -2682,6 +2766,9 @@ void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) {
void Node::input(const Ref<InputEvent> &p_event) {
}
+void Node::shortcut_input(const Ref<InputEvent> &p_key_event) {
+}
+
void Node::unhandled_input(const Ref<InputEvent> &p_event) {
}
@@ -2707,8 +2794,9 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node);
ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null);
ClassDB::bind_method(D_METHOD("get_parent"), &Node::get_parent);
- ClassDB::bind_method(D_METHOD("find_node", "mask", "recursive", "owned"), &Node::find_node, DEFVAL(true), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("find_parent", "mask"), &Node::find_parent);
+ ClassDB::bind_method(D_METHOD("find_child", "pattern", "recursive", "owned"), &Node::find_child, DEFVAL(true), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("find_children", "pattern", "type", "recursive", "owned"), &Node::find_children, DEFVAL(""), DEFVAL(true), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("find_parent", "pattern"), &Node::find_parent);
ClassDB::bind_method(D_METHOD("has_node_and_resource", "path"), &Node::has_node_and_resource);
ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource);
@@ -2743,6 +2831,8 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing);
ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input);
ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input);
+ ClassDB::bind_method(D_METHOD("set_process_shortcut_input", "enable"), &Node::set_process_shortcut_input);
+ ClassDB::bind_method(D_METHOD("is_processing_shortcut_input"), &Node::is_processing_shortcut_input);
ClassDB::bind_method(D_METHOD("set_process_unhandled_input", "enable"), &Node::set_process_unhandled_input);
ClassDB::bind_method(D_METHOD("is_processing_unhandled_input"), &Node::is_processing_unhandled_input);
ClassDB::bind_method(D_METHOD("set_process_unhandled_key_input", "enable"), &Node::set_process_unhandled_key_input);
@@ -2750,7 +2840,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Node::set_process_mode);
ClassDB::bind_method(D_METHOD("get_process_mode"), &Node::get_process_mode);
ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process);
- ClassDB::bind_method(D_METHOD("print_stray_nodes"), &Node::_print_stray_nodes);
+ ClassDB::bind_method(D_METHOD("print_orphan_nodes"), &Node::_print_orphan_nodes);
ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded);
ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded);
@@ -2784,8 +2874,6 @@ 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("get_custom_multiplayer"), &Node::get_custom_multiplayer);
- ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer);
ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
@@ -2794,6 +2882,9 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path);
ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path);
+ ClassDB::bind_method(D_METHOD("set_unique_name_in_owner", "enable"), &Node::set_unique_name_in_owner);
+ ClassDB::bind_method(D_METHOD("is_unique_name_in_owner"), &Node::is_unique_name_in_owner);
+
#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_set_property_pinned", "property", "pinned"), &Node::set_property_pinned);
#endif
@@ -2829,7 +2920,7 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_INSTANCED);
BIND_CONSTANT(NOTIFICATION_DRAG_BEGIN);
BIND_CONSTANT(NOTIFICATION_DRAG_END);
- BIND_CONSTANT(NOTIFICATION_PATH_CHANGED);
+ BIND_CONSTANT(NOTIFICATION_PATH_RENAMED);
BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS);
BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE);
@@ -2846,6 +2937,9 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_WM_CLOSE_REQUEST);
BIND_CONSTANT(NOTIFICATION_WM_GO_BACK_REQUEST);
BIND_CONSTANT(NOTIFICATION_WM_SIZE_CHANGED);
+ BIND_CONSTANT(NOTIFICATION_WM_DPI_CHANGE);
+ BIND_CONSTANT(NOTIFICATION_VP_MOUSE_ENTER);
+ BIND_CONSTANT(NOTIFICATION_VP_MOUSE_EXIT);
BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING);
BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
@@ -2877,12 +2971,14 @@ void Node::_bind_methods() {
ADD_SIGNAL(MethodInfo("tree_entered"));
ADD_SIGNAL(MethodInfo("tree_exiting"));
ADD_SIGNAL(MethodInfo("tree_exited"));
+ ADD_SIGNAL(MethodInfo("child_entered_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
+ ADD_SIGNAL(MethodInfo("child_exited_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "unique_name_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_unique_name_in_owner", "is_unique_name_in_owner");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_owner", "get_owner");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "", "get_multiplayer");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_custom_multiplayer", "get_custom_multiplayer");
ADD_GROUP("Process", "process_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,When Paused,Always,Disabled"), "set_process_mode", "get_process_mode");
@@ -2898,6 +2994,7 @@ void Node::_bind_methods() {
GDVIRTUAL_BIND(_ready);
GDVIRTUAL_BIND(_get_configuration_warnings);
GDVIRTUAL_BIND(_input, "event");
+ GDVIRTUAL_BIND(_shortcut_input, "event");
GDVIRTUAL_BIND(_unhandled_input, "event");
GDVIRTUAL_BIND(_unhandled_key_input, "event");
}
diff --git a/scene/main/node.h b/scene/main/node.h
index 2dd32a2e1d..8505d2618f 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,11 +31,8 @@
#ifndef NODE_H
#define NODE_H
-#include "core/config/project_settings.h"
-#include "core/object/class_db.h"
-#include "core/object/script_language.h"
#include "core/string/node_path.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "core/variant/typed_array.h"
#include "scene/main/scene_tree.h"
@@ -46,7 +43,6 @@ class PropertyTweener;
class Node : public Object {
GDCLASS(Node, Object);
- OBJ_CATEGORY("Nodes");
public:
enum ProcessMode {
@@ -103,6 +99,9 @@ private:
Node *parent = nullptr;
Node *owner = nullptr;
Vector<Node *> children;
+ HashMap<StringName, Node *> owned_unique_nodes;
+ bool unique_name_in_owner = false;
+
int internal_children_front = 0;
int internal_children_back = 0;
int pos = -1;
@@ -120,7 +119,7 @@ private:
Viewport *viewport = nullptr;
- Map<StringName, GroupData> grouped;
+ HashMap<StringName, GroupData> grouped;
List<Node *>::Element *OW = nullptr; // Owned element.
List<Node *> owned;
@@ -140,6 +139,7 @@ private:
bool process_internal = false;
bool input = false;
+ bool shortcut_input = false;
bool unhandled_input = false;
bool unhandled_key_input = false;
@@ -172,19 +172,18 @@ private:
void _propagate_ready();
void _propagate_exit_tree();
void _propagate_after_exit_tree();
- void _propagate_validate_owner();
- void _print_stray_nodes();
+ void _print_orphan_nodes();
void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification);
Array _get_node_and_resource(const NodePath &p_path);
void _duplicate_signals(const Node *p_original, Node *p_copy) const;
- Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = nullptr) const;
+ Node *_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap = nullptr) const;
TypedArray<Node> _get_children(bool p_include_internal = true) const;
Array _get_groups() const;
- Variant _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- Variant _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ 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);
_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; }
@@ -197,6 +196,9 @@ private:
_FORCE_INLINE_ bool _can_process(bool p_paused) const;
_FORCE_INLINE_ bool _is_enabled() const;
+ void _release_unique_name_in_owner();
+ void _acquire_unique_name_in_owner();
+
protected:
void _block() { data.blocked++; }
void _unblock() { data.blocked--; }
@@ -213,7 +215,6 @@ protected:
static String _get_name_num_separator();
friend class SceneState;
- friend class MultiplayerReplicator;
void _add_child_nocheck(Node *p_child, const StringName &p_name);
void _set_owner_nocheck(Node *p_owner);
@@ -221,11 +222,13 @@ protected:
//call from SceneTree
void _call_input(const Ref<InputEvent> &p_event);
+ void _call_shortcut_input(const Ref<InputEvent> &p_event);
void _call_unhandled_input(const Ref<InputEvent> &p_event);
void _call_unhandled_key_input(const Ref<InputEvent> &p_event);
protected:
virtual void input(const Ref<InputEvent> &p_event);
+ virtual void shortcut_input(const Ref<InputEvent> &p_key_event);
virtual void unhandled_input(const Ref<InputEvent> &p_event);
virtual void unhandled_key_input(const Ref<InputEvent> &p_key_event);
@@ -237,6 +240,7 @@ protected:
GDVIRTUAL0RC(Vector<String>, _get_configuration_warnings)
GDVIRTUAL1(_input, Ref<InputEvent>)
+ GDVIRTUAL1(_shortcut_input, Ref<InputEvent>)
GDVIRTUAL1(_unhandled_input, Ref<InputEvent>)
GDVIRTUAL1(_unhandled_key_input, Ref<InputEvent>)
@@ -256,7 +260,7 @@ public:
NOTIFICATION_INSTANCED = 20,
NOTIFICATION_DRAG_BEGIN = 21,
NOTIFICATION_DRAG_END = 22,
- NOTIFICATION_PATH_CHANGED = 23,
+ NOTIFICATION_PATH_RENAMED = 23,
//NOTIFICATION_TRANSLATION_CHANGED = 24, moved below
NOTIFICATION_INTERNAL_PROCESS = 25,
NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
@@ -273,6 +277,8 @@ public:
NOTIFICATION_WM_GO_BACK_REQUEST = 1007,
NOTIFICATION_WM_SIZE_CHANGED = 1008,
NOTIFICATION_WM_DPI_CHANGE = 1009,
+ NOTIFICATION_VP_MOUSE_ENTER = 1010,
+ NOTIFICATION_VP_MOUSE_EXIT = 1011,
NOTIFICATION_OS_MEMORY_WARNING = MainLoop::NOTIFICATION_OS_MEMORY_WARNING,
NOTIFICATION_TRANSLATION_CHANGED = MainLoop::NOTIFICATION_TRANSLATION_CHANGED,
@@ -304,12 +310,13 @@ public:
bool has_node(const NodePath &p_path) const;
Node *get_node(const NodePath &p_path) const;
Node *get_node_or_null(const NodePath &p_path) const;
- Node *find_node(const String &p_mask, bool p_recursive = true, bool p_owned = true) const;
+ Node *find_child(const String &p_pattern, bool p_recursive = true, bool p_owned = true) const;
+ TypedArray<Node> find_children(const String &p_pattern, const String &p_type = "", bool p_recursive = true, bool p_owned = true) const;
bool has_node_and_resource(const NodePath &p_path) const;
- Node *get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const;
+ Node *get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const;
Node *get_parent() const;
- Node *find_parent(const String &p_mask) const;
+ Node *find_parent(const String &p_pattern) const;
_FORCE_INLINE_ SceneTree *get_tree() const {
ERR_FAIL_COND_V(!data.tree, nullptr);
@@ -345,6 +352,9 @@ public:
Node *get_owner() const;
void get_owned_by(Node *p_by, List<Node *> *p_owned);
+ void set_unique_name_in_owner(bool p_enabled);
+ bool is_unique_name_in_owner() const;
+
void remove_and_skip();
int get_index(bool p_include_internal = true) const;
@@ -368,7 +378,7 @@ public:
bool is_property_pinned(const StringName &p_property) const;
virtual StringName get_property_store_alias(const StringName &p_property) const;
#endif
- void get_storable_properties(Set<StringName> &r_storable_properties) const;
+ void get_storable_properties(RBSet<StringName> &r_storable_properties) const;
virtual String to_string() override;
@@ -399,6 +409,9 @@ public:
void set_process_input(bool p_enable);
bool is_processing_input() const;
+ void set_process_shortcut_input(bool p_enable);
+ bool is_processing_shortcut_input() const;
+
void set_process_unhandled_input(bool p_enable);
bool is_processing_unhandled_input() const;
@@ -407,10 +420,10 @@ public:
Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const;
#ifdef TOOLS_ENABLED
- Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const;
- Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<RES, RES> &p_resource_remap) const;
- void remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_remap) const;
- void remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resource_remap) const;
+ Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) const;
+ Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
+ void remap_node_resources(Node *p_node, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
+ void remap_nested_resources(Ref<Resource> p_resource, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
#endif
// used by editors, to save what has changed only
@@ -423,7 +436,11 @@ public:
void set_scene_instance_load_placeholder(bool p_enable);
bool get_scene_instance_load_placeholder() const;
- static Vector<Variant> make_binds(VARIANT_ARG_LIST);
+ template <typename... VarArgs>
+ Vector<Variant> make_binds(VarArgs... p_args) {
+ Vector<Variant> binds = { p_args... };
+ return binds;
+ }
void replace_by(Node *p_node, bool p_keep_data = false);
@@ -435,7 +452,7 @@ public:
void request_ready();
- static void print_stray_nodes();
+ static void print_orphan_nodes();
#ifdef TOOLS_ENABLED
String validate_child_name(Node *p_child);
@@ -468,20 +485,36 @@ public:
bool is_displayed_folded() const;
/* NETWORK */
- void set_multiplayer_authority(int p_peer_id, bool p_recursive = true);
+ virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true);
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(const StringName &p_method, VARIANT_ARG_LIST); // RPC, honors RPCMode, TransferMode, channel
- void rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); // RPC to specific peer(s), honors RPCMode, TransferMode, channel
+ 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));
+ }
+
+ 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));
+ }
+
void rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
Ref<MultiplayerAPI> get_multiplayer() const;
- Ref<MultiplayerAPI> get_custom_multiplayer() const;
- void set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
Node();
~Node();
@@ -489,6 +522,6 @@ public:
VARIANT_ENUM_CAST(Node::DuplicateFlags);
-typedef Set<Node *, Node::Comparator> NodeSet;
+typedef RBSet<Node *, Node::Comparator> NodeSet;
#endif
diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp
index c44b55284d..b3595c6227 100644
--- a/scene/main/resource_preloader.cpp
+++ b/scene/main/resource_preloader.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,7 +41,7 @@ void ResourcePreloader::_set_resources(const Array &p_data) {
for (int i = 0; i < resdata.size(); i++) {
String name = names[i];
- RES resource = resdata[i];
+ Ref<Resource> resource = resdata[i];
ERR_CONTINUE(!resource.is_valid());
resources[name] = resource;
@@ -55,14 +55,14 @@ Array ResourcePreloader::_get_resources() const {
arr.resize(resources.size());
names.resize(resources.size());
- Set<String> sorted_names;
+ RBSet<String> sorted_names;
- for (const KeyValue<StringName, RES> &E : resources) {
+ for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
sorted_names.insert(E.key);
}
int i = 0;
- for (Set<String>::Element *E = sorted_names.front(); E; E = E->next()) {
+ for (RBSet<String>::Element *E = sorted_names.front(); E; E = E->next()) {
names.set(i, E->get());
arr[i] = resources[E->get()];
i++;
@@ -74,7 +74,7 @@ Array ResourcePreloader::_get_resources() const {
return res;
}
-void ResourcePreloader::add_resource(const StringName &p_name, const RES &p_resource) {
+void ResourcePreloader::add_resource(const StringName &p_name, const Ref<Resource> &p_resource) {
ERR_FAIL_COND(p_resource.is_null());
if (resources.has(p_name)) {
StringName new_name;
@@ -104,7 +104,7 @@ void ResourcePreloader::remove_resource(const StringName &p_name) {
void ResourcePreloader::rename_resource(const StringName &p_from_name, const StringName &p_to_name) {
ERR_FAIL_COND(!resources.has(p_from_name));
- RES res = resources[p_from_name];
+ Ref<Resource> res = resources[p_from_name];
resources.erase(p_from_name);
add_resource(p_to_name, res);
@@ -114,8 +114,8 @@ bool ResourcePreloader::has_resource(const StringName &p_name) const {
return resources.has(p_name);
}
-RES ResourcePreloader::get_resource(const StringName &p_name) const {
- ERR_FAIL_COND_V(!resources.has(p_name), RES());
+Ref<Resource> ResourcePreloader::get_resource(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!resources.has(p_name), Ref<Resource>());
return resources[p_name];
}
@@ -123,15 +123,16 @@ Vector<String> ResourcePreloader::_get_resource_list() const {
Vector<String> res;
res.resize(resources.size());
int i = 0;
- for (Map<StringName, RES>::Element *E = resources.front(); E; E = E->next(), i++) {
- res.set(i, E->key());
+ for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
+ res.set(i, E.key);
+ i++;
}
return res;
}
void ResourcePreloader::get_resource_list(List<StringName> *p_list) {
- for (const KeyValue<StringName, RES> &E : resources) {
+ for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
p_list->push_back(E.key);
}
}
diff --git a/scene/main/resource_preloader.h b/scene/main/resource_preloader.h
index 1b7ea3fb9f..fe59bc8ae3 100644
--- a/scene/main/resource_preloader.h
+++ b/scene/main/resource_preloader.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,7 +36,7 @@
class ResourcePreloader : public Node {
GDCLASS(ResourcePreloader, Node);
- Map<StringName, RES> resources;
+ HashMap<StringName, Ref<Resource>> resources;
void _set_resources(const Array &p_data);
Array _get_resources() const;
@@ -46,11 +46,11 @@ protected:
static void _bind_methods();
public:
- void add_resource(const StringName &p_name, const RES &p_resource);
+ void add_resource(const StringName &p_name, const Ref<Resource> &p_resource);
void remove_resource(const StringName &p_name);
void rename_resource(const StringName &p_from_name, const StringName &p_to_name);
bool has_resource(const StringName &p_name) const;
- RES get_resource(const StringName &p_name) const;
+ Ref<Resource> get_resource(const StringName &p_name) const;
void get_resource_list(List<StringName> *p_list);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index a122241cd0..231b672f63 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,6 +36,7 @@
#include "core/io/dir_access.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"
@@ -43,10 +44,13 @@
#include "node.h"
#include "scene/animation/tween.h"
#include "scene/debugger/scene_debugger.h"
+#include "scene/main/viewport.h"
#include "scene/resources/font.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "scene/resources/packed_scene.h"
+#include "scene/resources/world_2d.h"
+#include "scene/resources/world_3d.h"
#include "scene/scene_string_names.h"
#include "servers/display_server.h"
#include "servers/navigation_server_3d.h"
@@ -125,32 +129,32 @@ void SceneTree::node_renamed(Node *p_node) {
}
SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_node) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
E = group_map.insert(p_group, Group());
}
- ERR_FAIL_COND_V_MSG(E->get().nodes.find(p_node) != -1, &E->get(), "Already in group: " + p_group + ".");
- E->get().nodes.push_back(p_node);
- //E->get().last_tree_version=0;
- E->get().changed = true;
- return &E->get();
+ ERR_FAIL_COND_V_MSG(E->value.nodes.has(p_node), &E->value, "Already in group: " + p_group + ".");
+ E->value.nodes.push_back(p_node);
+ //E->value.last_tree_version=0;
+ E->value.changed = true;
+ return &E->value;
}
void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
ERR_FAIL_COND(!E);
- E->get().nodes.erase(p_node);
- if (E->get().nodes.is_empty()) {
- group_map.erase(E);
+ E->value.nodes.erase(p_node);
+ if (E->value.nodes.is_empty()) {
+ group_map.remove(E);
}
}
void SceneTree::make_group_changed(const StringName &p_group) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (E) {
- E->get().changed = true;
+ E->value.changed = true;
}
}
@@ -169,17 +173,17 @@ void SceneTree::_flush_ugc() {
ugc_locked = true;
while (unique_group_calls.size()) {
- Map<UGCall, Vector<Variant>>::Element *E = unique_group_calls.front();
+ HashMap<UGCall, Vector<Variant>, UGCall>::Iterator E = unique_group_calls.begin();
- Variant v[VARIANT_ARG_MAX];
- for (int i = 0; i < E->get().size(); i++) {
- v[i] = E->get()[i];
+ const Variant **argptrs = (const Variant **)alloca(E->value.size() * sizeof(Variant *));
+
+ for (int i = 0; i < E->value.size(); i++) {
+ argptrs[i] = &E->value[i];
}
- static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
- call_group_flags(GROUP_CALL_REALTIME, E->key().group, E->key().call, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+ call_group_flagsp(GROUP_CALL_DEFAULT, E->key.group, E->key.call, argptrs, E->value.size());
- unique_group_calls.erase(E);
+ unique_group_calls.remove(E);
}
ugc_locked = false;
@@ -206,17 +210,17 @@ void SceneTree::_update_group_order(Group &g, bool p_use_priority) {
g.changed = false;
}
-void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, VARIANT_ARG_DECLARE) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, const Variant **p_args, int p_argcount) {
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return;
}
- Group &g = E->get();
+ Group &g = E->value;
if (g.nodes.is_empty()) {
return;
}
- if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) {
+ if (p_call_flags & GROUP_CALL_UNIQUE && p_call_flags & GROUP_CALL_DEFERRED) {
ERR_FAIL_COND(ugc_locked);
UGCall ug;
@@ -227,14 +231,9 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou
return;
}
- VARIANT_ARGPTRS;
-
Vector<Variant> args;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- args.push_back(*argptr[i]);
+ for (int i = 0; i < p_argcount; i++) {
+ args.push_back(*p_args[i]);
}
unique_group_calls[ug] = args;
@@ -255,10 +254,11 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
- nodes[i]->call(p_function, VARIANT_ARG_PASS);
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
+ Callable::CallError ce;
+ nodes[i]->callp(p_function, p_args, p_argcount, ce);
} else {
- MessageQueue::get_singleton()->push_call(nodes[i], p_function, VARIANT_ARG_PASS);
+ MessageQueue::get_singleton()->push_callp(nodes[i], p_function, p_args, p_argcount);
}
}
@@ -268,10 +268,11 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
- nodes[i]->call(p_function, VARIANT_ARG_PASS);
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
+ Callable::CallError ce;
+ nodes[i]->callp(p_function, p_args, p_argcount, ce);
} else {
- MessageQueue::get_singleton()->push_call(nodes[i], p_function, VARIANT_ARG_PASS);
+ MessageQueue::get_singleton()->push_callp(nodes[i], p_function, p_args, p_argcount);
}
}
}
@@ -283,11 +284,11 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou
}
void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return;
}
- Group &g = E->get();
+ Group &g = E->value;
if (g.nodes.is_empty()) {
return;
}
@@ -306,7 +307,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->notification(p_notification);
} else {
MessageQueue::get_singleton()->push_notification(nodes[i], p_notification);
@@ -319,7 +320,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->notification(p_notification);
} else {
MessageQueue::get_singleton()->push_notification(nodes[i], p_notification);
@@ -334,11 +335,11 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
}
void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return;
}
- Group &g = E->get();
+ Group &g = E->value;
if (g.nodes.is_empty()) {
return;
}
@@ -357,7 +358,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->set(p_name, p_value);
} else {
MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value);
@@ -370,7 +371,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
continue;
}
- if (p_call_flags & GROUP_CALL_REALTIME) {
+ if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
nodes[i]->set(p_name, p_value);
} else {
MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value);
@@ -384,16 +385,12 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
}
}
-void SceneTree::call_group(const StringName &p_group, const StringName &p_function, VARIANT_ARG_DECLARE) {
- call_group_flags(0, p_group, p_function, VARIANT_ARG_PASS);
-}
-
void SceneTree::notify_group(const StringName &p_group, int p_notification) {
notify_group_flags(0, p_group, p_notification);
}
void SceneTree::set_group(const StringName &p_group, const String &p_name, const Variant &p_value) {
- set_group_flags(0, p_group, p_name, p_value);
+ set_group_flags(GROUP_CALL_DEFAULT, p_group, p_name, p_value);
}
void SceneTree::initialize() {
@@ -416,7 +413,7 @@ bool SceneTree::physics_process(double p_time) {
emit_signal(SNAME("physics_frame"));
_notify_group_pause(SNAME("physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
- call_group_flags(GROUP_CALL_REALTIME, SNAME("_picking_viewports"), SNAME("_process_picking"));
+ call_group(SNAME("_picking_viewports"), SNAME("_process_picking"));
_notify_group_pause(SNAME("physics_process"), Node::NOTIFICATION_PHYSICS_PROCESS);
_flush_ugc();
MessageQueue::get_singleton()->flush(); //small little hack
@@ -441,6 +438,9 @@ bool SceneTree::process(double p_time) {
if (multiplayer_poll) {
multiplayer->poll();
+ for (KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
+ E.value->poll();
+ }
}
emit_signal(SNAME("process_frame"));
@@ -482,7 +482,7 @@ bool SceneTree::process(double p_time) {
}
E->get()->set_time_left(time_left);
- if (time_left < 0) {
+ if (time_left <= 0) {
E->get()->emit_signal(SNAME("timeout"));
timers.erase(E);
}
@@ -510,7 +510,7 @@ bool SceneTree::process(double p_time) {
cpath = fallback->get_path();
}
if (cpath != env_path) {
- if (env_path != String()) {
+ if (!env_path.is_empty()) {
fallback = ResourceLoader::load(env_path);
if (fallback.is_null()) {
//could not load fallback, set as empty
@@ -535,7 +535,7 @@ void SceneTree::process_tweens(float p_delta, bool p_physics) {
for (List<Ref<Tween>>::Element *E = tweens.front(); E;) {
List<Ref<Tween>>::Element *N = E->next();
// Don't process if paused or process mode doesn't match.
- if ((paused && E->get()->should_pause()) || (p_physics == (E->get()->get_process_mode() == Tween::TWEEN_PROCESS_IDLE))) {
+ if (!E->get()->can_process(paused) || (p_physics == (E->get()->get_process_mode() == Tween::TWEEN_PROCESS_IDLE))) {
if (E == L) {
break;
}
@@ -612,6 +612,7 @@ void SceneTree::_notification(int p_notification) {
get_root()->propagate_notification(p_notification);
}
} break;
+
case NOTIFICATION_OS_MEMORY_WARNING:
case NOTIFICATION_OS_IME_UPDATE:
case NOTIFICATION_WM_ABOUT:
@@ -620,13 +621,11 @@ void SceneTree::_notification(int p_notification) {
case NOTIFICATION_APPLICATION_PAUSED:
case NOTIFICATION_APPLICATION_FOCUS_IN:
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
- get_root()->propagate_notification(p_notification); //pass these to nodes, since they are mirrored
+ // Pass these to nodes, since they are mirrored.
+ get_root()->propagate_notification(p_notification);
} break;
-
- default:
- break;
- };
-};
+ }
+}
void SceneTree::set_auto_accept_quit(bool p_enable) {
accept_quit = p_enable;
@@ -819,11 +818,11 @@ bool SceneTree::is_paused() const {
}
void SceneTree::_notify_group_pause(const StringName &p_group, int p_notification) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return;
}
- Group &g = E->get();
+ Group &g = E->value;
if (g.nodes.is_empty()) {
return;
}
@@ -863,11 +862,11 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio
}
void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_call_type, const Ref<InputEvent> &p_input, Viewport *p_viewport) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return;
}
- Group &g = E->get();
+ Group &g = E->value;
if (g.nodes.is_empty()) {
return;
}
@@ -901,6 +900,9 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
case CALL_INPUT_TYPE_INPUT:
n->_call_input(p_input);
break;
+ case CALL_INPUT_TYPE_SHORTCUT_INPUT:
+ n->_call_shortcut_input(p_input);
+ break;
case CALL_INPUT_TYPE_UNHANDLED_INPUT:
n->_call_unhandled_input(p_input);
break;
@@ -916,46 +918,32 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
}
}
-Variant SceneTree::_call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void SceneTree::_call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;
- ERR_FAIL_COND_V(p_argcount < 3, Variant());
- ERR_FAIL_COND_V(!p_args[0]->is_num(), Variant());
- ERR_FAIL_COND_V(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING, Variant());
- ERR_FAIL_COND_V(p_args[2]->get_type() != Variant::STRING_NAME && p_args[2]->get_type() != Variant::STRING, Variant());
+ ERR_FAIL_COND(p_argcount < 3);
+ ERR_FAIL_COND(!p_args[0]->is_num());
+ ERR_FAIL_COND(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING);
+ ERR_FAIL_COND(p_args[2]->get_type() != Variant::STRING_NAME && p_args[2]->get_type() != Variant::STRING);
int flags = *p_args[0];
StringName group = *p_args[1];
StringName method = *p_args[2];
- Variant v[VARIANT_ARG_MAX];
-
- for (int i = 0; i < MIN(p_argcount - 3, 5); i++) {
- v[i] = *p_args[i + 3];
- }
- static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
- call_group_flags(flags, group, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
- return Variant();
+ call_group_flagsp(flags, group, method, p_args + 3, p_argcount - 3);
}
-Variant SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;
- ERR_FAIL_COND_V(p_argcount < 2, Variant());
- ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING, Variant());
- ERR_FAIL_COND_V(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING, Variant());
+ ERR_FAIL_COND(p_argcount < 2);
+ ERR_FAIL_COND(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING);
+ ERR_FAIL_COND(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING);
StringName group = *p_args[0];
StringName method = *p_args[1];
- Variant v[VARIANT_ARG_MAX];
-
- for (int i = 0; i < MIN(p_argcount - 2, 5); i++) {
- v[i] = *p_args[i + 2];
- }
- static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
- call_group_flags(0, group, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
- return Variant();
+ call_group_flagsp(GROUP_CALL_DEFAULT, group, method, p_args + 2, p_argcount - 2);
}
int64_t SceneTree::get_frame() const {
@@ -964,20 +952,20 @@ int64_t SceneTree::get_frame() const {
Array SceneTree::_get_nodes_in_group(const StringName &p_group) {
Array ret;
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return ret;
}
- _update_group_order(E->get()); //update order just in case
- int nc = E->get().nodes.size();
+ _update_group_order(E->value); //update order just in case
+ int nc = E->value.nodes.size();
if (nc == 0) {
return ret;
}
ret.resize(nc);
- Node **ptr = E->get().nodes.ptrw();
+ Node **ptr = E->value.nodes.ptrw();
for (int i = 0; i < nc; i++) {
ret[i] = ptr[i];
}
@@ -990,32 +978,32 @@ bool SceneTree::has_group(const StringName &p_identifier) const {
}
Node *SceneTree::get_first_node_in_group(const StringName &p_group) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
- return nullptr; //no group
+ return nullptr; // No group.
}
- _update_group_order(E->get()); //update order just in case
+ _update_group_order(E->value); // Update order just in case.
- if (E->get().nodes.size() == 0) {
+ if (E->value.nodes.is_empty()) {
return nullptr;
}
- return E->get().nodes[0];
+ return E->value.nodes[0];
}
void SceneTree::get_nodes_in_group(const StringName &p_group, List<Node *> *p_list) {
- Map<StringName, Group>::Element *E = group_map.find(p_group);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return;
}
- _update_group_order(E->get()); //update order just in case
- int nc = E->get().nodes.size();
+ _update_group_order(E->value); //update order just in case
+ int nc = E->value.nodes.size();
if (nc == 0) {
return;
}
- Node **ptr = E->get().nodes.ptrw();
+ Node **ptr = E->value.nodes.ptrw();
for (int i = 0; i < nc; i++) {
p_list->push_back(ptr[i]);
}
@@ -1128,9 +1116,7 @@ Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_a
}
Ref<Tween> SceneTree::create_tween() {
- Ref<Tween> tween;
- tween.instantiate();
- tween->set_valid(true);
+ Ref<Tween> tween = memnew(Tween(true));
tweens.push_back(tween);
return tween;
}
@@ -1148,8 +1134,50 @@ Array SceneTree::get_processed_tweens() {
return ret;
}
-Ref<MultiplayerAPI> SceneTree::get_multiplayer() const {
- return multiplayer;
+Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const {
+ Ref<MultiplayerAPI> out = multiplayer;
+ for (const KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
+ const Vector<StringName> snames = E.key.get_names();
+ const Vector<StringName> tnames = p_for_path.get_names();
+ if (tnames.size() < snames.size()) {
+ continue;
+ }
+ const StringName *sptr = snames.ptr();
+ const StringName *nptr = tnames.ptr();
+ bool valid = true;
+ for (int i = 0; i < snames.size(); i++) {
+ if (sptr[i] != nptr[i]) {
+ valid = false;
+ break;
+ }
+ }
+ if (valid) {
+ out = E.value;
+ break;
+ }
+ }
+ return out;
+}
+
+void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path) {
+ if (p_root_path.is_empty()) {
+ ERR_FAIL_COND(!p_multiplayer.is_valid());
+ if (multiplayer.is_valid()) {
+ multiplayer->set_root_path(NodePath());
+ }
+ multiplayer = p_multiplayer;
+ multiplayer->set_root_path("/" + root->get_name());
+ } else {
+ 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);
+ }
+ }
+ }
}
void SceneTree::set_multiplayer_poll_enabled(bool p_enabled) {
@@ -1160,13 +1188,6 @@ bool SceneTree::is_multiplayer_poll_enabled() const {
return multiplayer_poll;
}
-void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- ERR_FAIL_COND(!p_multiplayer.is_valid());
-
- multiplayer = p_multiplayer;
- multiplayer->set_root_node(root);
-}
-
void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root"), &SceneTree::get_root);
ClassDB::bind_method(D_METHOD("has_group", "name"), &SceneTree::has_group);
@@ -1229,8 +1250,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
- ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer"), &SceneTree::set_multiplayer);
- ClassDB::bind_method(D_METHOD("get_multiplayer"), &SceneTree::get_multiplayer);
+ ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer", "root_path"), &SceneTree::set_multiplayer, DEFVAL(NodePath()));
+ ClassDB::bind_method(D_METHOD("get_multiplayer", "for_path"), &SceneTree::get_multiplayer, DEFVAL(NodePath()));
ClassDB::bind_method(D_METHOD("set_multiplayer_poll_enabled", "enabled"), &SceneTree::set_multiplayer_poll_enabled);
ClassDB::bind_method(D_METHOD("is_multiplayer_poll_enabled"), &SceneTree::is_multiplayer_poll_enabled);
@@ -1240,7 +1261,6 @@ void SceneTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_edited_scene_root", "get_edited_scene_root");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_current_scene", "get_current_scene");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "", "get_root");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_multiplayer", "get_multiplayer");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multiplayer_poll"), "set_multiplayer_poll_enabled", "is_multiplayer_poll_enabled");
ADD_SIGNAL(MethodInfo("tree_changed"));
@@ -1253,11 +1273,9 @@ void SceneTree::_bind_methods() {
ADD_SIGNAL(MethodInfo("process_frame"));
ADD_SIGNAL(MethodInfo("physics_frame"));
- ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "screen")));
-
BIND_ENUM_CONSTANT(GROUP_CALL_DEFAULT);
BIND_ENUM_CONSTANT(GROUP_CALL_REVERSE);
- BIND_ENUM_CONSTANT(GROUP_CALL_REALTIME);
+ BIND_ENUM_CONSTANT(GROUP_CALL_DEFERRED);
BIND_ENUM_CONSTANT(GROUP_CALL_UNIQUE);
}
@@ -1279,7 +1297,7 @@ void SceneTree::add_idle_callback(IdleCallback p_callback) {
void SceneTree::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
if (p_function == "change_scene") {
- DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
List<String> directories;
directories.push_back(dir_access->get_current_dir());
@@ -1290,7 +1308,7 @@ void SceneTree::get_argument_options(const StringName &p_function, int p_idx, Li
dir_access->list_dir_begin();
String filename = dir_access->get_next();
- while (filename != "") {
+ while (!filename.is_empty()) {
if (filename == "." || filename == "..") {
filename = dir_access->get_next();
continue;
@@ -1328,6 +1346,8 @@ SceneTree::SceneTree() {
root = memnew(Window);
root->set_process_mode(Node::PROCESS_MODE_PAUSABLE);
root->set_name("root");
+ root->set_title(ProjectSettings::get_singleton()->get("application/config/name"));
+
#ifndef _3D_DISABLED
if (!root->get_world_3d().is_valid()) {
root->set_world_3d(Ref<World3D>(memnew(World3D)));
@@ -1355,9 +1375,9 @@ SceneTree::SceneTree() {
const bool use_occlusion_culling = GLOBAL_DEF("rendering/occlusion_culling/use_occlusion_culling", false);
root->set_use_occlusion_culling(use_occlusion_culling);
- float lod_threshold = GLOBAL_DEF("rendering/mesh_lod/lod_change/threshold_pixels", 1.0);
+ float mesh_lod_threshold = GLOBAL_DEF("rendering/mesh_lod/lod_change/threshold_pixels", 1.0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/mesh_lod/lod_change/threshold_pixels", PropertyInfo(Variant::FLOAT, "rendering/mesh_lod/lod_change/threshold_pixels", PROPERTY_HINT_RANGE, "0,1024,0.1"));
- root->set_lod_threshold(lod_threshold);
+ root->set_mesh_lod_threshold(mesh_lod_threshold);
bool snap_2d_transforms = GLOBAL_DEF("rendering/2d/snap/snap_2d_transforms_to_pixel", false);
root->set_snap_2d_transforms_to_pixel(snap_2d_transforms);
@@ -1400,7 +1420,7 @@ SceneTree::SceneTree() {
ResourceLoader::get_recognized_extensions_for_type("Environment", &exts);
String ext_hint;
for (const String &E : exts) {
- if (ext_hint != String()) {
+ if (!ext_hint.is_empty()) {
ext_hint += ",";
}
ext_hint += "*." + E;
@@ -1410,7 +1430,7 @@ SceneTree::SceneTree() {
// Setup property.
ProjectSettings::get_singleton()->set_custom_property_info("rendering/environment/defaults/default_environment", PropertyInfo(Variant::STRING, "rendering/viewport/default_environment", PROPERTY_HINT_FILE, ext_hint));
env_path = env_path.strip_edges();
- if (env_path != String()) {
+ if (!env_path.is_empty()) {
Ref<Environment> env = ResourceLoader::load(env_path);
if (env.is_valid()) {
root->get_world_3d()->set_fallback_environment(env);
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 7a4f9f9c52..bdcfd2d35a 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,13 +31,10 @@
#ifndef SCENE_TREE_H
#define SCENE_TREE_H
-#include "core/multiplayer/multiplayer_api.h"
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
#include "core/templates/self_list.h"
#include "scene/resources/mesh.h"
-#include "scene/resources/world_2d.h"
-#include "scene/resources/world_3d.h"
#undef Window
@@ -46,8 +43,10 @@ class Node;
class Window;
class Material;
class Mesh;
+class MultiplayerAPI;
class SceneDebugger;
class Tween;
+class Viewport;
class SceneTreeTimer : public RefCounted {
GDCLASS(SceneTreeTimer, RefCounted);
@@ -103,7 +102,7 @@ private:
bool paused = false;
int root_lock = 0;
- Map<StringName, Group> group_map;
+ HashMap<StringName, Group> group_map;
bool _quit = false;
bool initialized = false;
@@ -116,22 +115,26 @@ private:
int node_count = 0;
#ifdef TOOLS_ENABLED
- Node *edited_scene_root;
+ Node *edited_scene_root = nullptr;
#endif
struct UGCall {
StringName group;
StringName call;
+ static uint32_t hash(const UGCall &p_val) {
+ return p_val.group.hash() ^ p_val.call.hash();
+ }
+ bool operator==(const UGCall &p_with) const { return group == p_with.group && call == p_with.call; }
bool operator<(const UGCall &p_with) const { return group == p_with.group ? call < p_with.call : group < p_with.group; }
};
// Safety for when a node is deleted while a group is being called.
int call_lock = 0;
- Set<Node *> call_skip; // Skip erased nodes.
+ RBSet<Node *> call_skip; // Skip erased nodes.
List<ObjectID> delete_queue;
- Map<UGCall, Vector<Variant>> unique_group_calls;
+ HashMap<UGCall, Vector<Variant>, UGCall> unique_group_calls;
bool ugc_locked = false;
void _flush_ugc();
@@ -139,7 +142,7 @@ private:
Array _get_nodes_in_group(const StringName &p_group);
- Node *current_scene;
+ Node *current_scene = nullptr;
Color debug_collisions_color;
Color debug_collision_contact_color;
@@ -152,7 +155,6 @@ private:
int collision_debug_contacts;
void _change_scene(Node *p_to);
- //void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2);
List<Ref<SceneTreeTimer>> timers;
List<Ref<Tween>> tweens;
@@ -160,6 +162,7 @@ private:
///network///
Ref<MultiplayerAPI> multiplayer;
+ HashMap<NodePath, Ref<MultiplayerAPI>> custom_multiplayers;
bool multiplayer_poll = true;
static SceneTree *singleton;
@@ -176,8 +179,8 @@ private:
void make_group_changed(const StringName &p_group);
void _notify_group_pause(const StringName &p_group, int p_notification);
- Variant _call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- Variant _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void _call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
void _flush_delete_queue();
// Optimization.
@@ -205,6 +208,7 @@ private:
enum CallInputType {
CALL_INPUT_TYPE_INPUT,
+ CALL_INPUT_TYPE_SHORTCUT_INPUT,
CALL_INPUT_TYPE_UNHANDLED_INPUT,
CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT,
};
@@ -224,20 +228,42 @@ public:
enum GroupCallFlags {
GROUP_CALL_DEFAULT = 0,
GROUP_CALL_REVERSE = 1,
- GROUP_CALL_REALTIME = 2,
+ GROUP_CALL_DEFERRED = 2,
GROUP_CALL_UNIQUE = 4,
};
_FORCE_INLINE_ Window *get_root() const { return root; }
- void call_group_flags(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, VARIANT_ARG_LIST);
+ void call_group_flagsp(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, const Variant **p_args, int p_argcount);
void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification);
void set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value);
- void call_group(const StringName &p_group, const StringName &p_function, VARIANT_ARG_LIST);
+ // `notify_group()` is immediate by default since Godot 4.0.
void notify_group(const StringName &p_group, int p_notification);
+ // `set_group()` is immediate by default since Godot 4.0.
void set_group(const StringName &p_group, const String &p_name, const Variant &p_value);
+ template <typename... VarArgs>
+ // `call_group()` is immediate by default since Godot 4.0.
+ void call_group(const StringName &p_group, const StringName &p_function, 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];
+ }
+ call_group_flagsp(GROUP_CALL_DEFAULT, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+
+ template <typename... VarArgs>
+ void call_group_flags(uint32_t p_flags, const StringName &p_group, const StringName &p_function, 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];
+ }
+ call_group_flagsp(p_flags, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+
void flush_transform_notifications();
virtual void initialize() override;
@@ -332,10 +358,10 @@ public:
//network API
- Ref<MultiplayerAPI> get_multiplayer() const;
+ Ref<MultiplayerAPI> get_multiplayer(const NodePath &p_for_path = NodePath()) const;
+ void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path = NodePath());
void set_multiplayer_poll_enabled(bool p_enabled);
bool is_multiplayer_poll_enabled() const;
- void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
static void add_idle_callback(IdleCallback p_callback);
diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp
index 9477e300d1..0049359cad 100644
--- a/scene/main/shader_globals_override.cpp
+++ b/scene/main/shader_globals_override.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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::PLANE;
+ pinfo.type = Variant::QUATERNION;
} break;
case RS::GLOBAL_VAR_TYPE_RECT2: {
pinfo.type = Variant::RECT2;
@@ -221,6 +221,7 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const
}
void ShaderGlobalsOverride::_activate() {
+ ERR_FAIL_NULL(get_tree());
List<Node *> nodes;
get_tree()->get_nodes_in_group(SceneStringNames::get_singleton()->shader_overrides_group_active, &nodes);
if (nodes.size() == 0) {
@@ -228,44 +229,45 @@ void ShaderGlobalsOverride::_activate() {
active = true;
add_to_group(SceneStringNames::get_singleton()->shader_overrides_group_active);
- const StringName *K = nullptr;
- while ((K = overrides.next(K))) {
- Override *o = overrides.getptr(*K);
+ for (const KeyValue<StringName, Override> &E : overrides) {
+ const Override *o = &E.value;
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(*K, tex_rid);
+ RS::get_singleton()->global_variable_set_override(E.key, tex_rid);
} else {
- RS::get_singleton()->global_variable_set_override(*K, o->override);
+ RS::get_singleton()->global_variable_set_override(E.key, o->override);
}
}
- }
- update_configuration_warnings(); //may have activated
+ update_configuration_warnings(); //may have activated
+ }
}
}
void ShaderGlobalsOverride::_notification(int p_what) {
- if (p_what == Node3D::NOTIFICATION_ENTER_TREE) {
- add_to_group(SceneStringNames::get_singleton()->shader_overrides_group);
- _activate();
+ switch (p_what) {
+ case Node3D::NOTIFICATION_ENTER_TREE: {
+ add_to_group(SceneStringNames::get_singleton()->shader_overrides_group);
+ _activate();
+ } break;
- } else if (p_what == Node3D::NOTIFICATION_EXIT_TREE) {
- if (active) {
- //remove overrides
- const StringName *K = nullptr;
- while ((K = overrides.next(K))) {
- Override *o = overrides.getptr(*K);
- if (o->in_use) {
- RS::get_singleton()->global_variable_set_override(*K, Variant());
+ case Node3D::NOTIFICATION_EXIT_TREE: {
+ if (active) {
+ //remove overrides
+ 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());
+ }
}
}
- }
- remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group_active);
- remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group);
- get_tree()->call_group(SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed
- active = false;
+ remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group_active);
+ remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group);
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed
+ active = false;
+ } break;
}
}
@@ -273,7 +275,7 @@ TypedArray<String> ShaderGlobalsOverride::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!active) {
- warnings.push_back(TTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."));
+ warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."));
}
return warnings;
diff --git a/scene/main/shader_globals_override.h b/scene/main/shader_globals_override.h
index ab4b9de727..af99bf9aa7 100644
--- a/scene/main/shader_globals_override.h
+++ b/scene/main/shader_globals_override.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
index 154e4cf683..5a5747e122 100644
--- a/scene/main/timer.cpp
+++ b/scene/main/timer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,6 +43,7 @@ void Timer::_notification(int p_what) {
autostart = false;
}
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
if (!processing || timer_process_callback == TIMER_PROCESS_PHYSICS || !is_processing_internal()) {
return;
@@ -58,8 +59,8 @@ void Timer::_notification(int p_what) {
emit_signal(SNAME("timeout"));
}
-
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!processing || timer_process_callback == TIMER_PROCESS_IDLE || !is_physics_processing_internal()) {
return;
@@ -74,7 +75,6 @@ void Timer::_notification(int p_what) {
}
emit_signal(SNAME("timeout"));
}
-
} break;
}
}
@@ -184,7 +184,7 @@ TypedArray<String> Timer::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (wait_time < 0.05 - CMP_EPSILON) {
- warnings.push_back(TTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
+ warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
}
return warnings;
diff --git a/scene/main/timer.h b/scene/main/timer.h
index e2f34042dd..8785d31a8a 100644
--- a/scene/main/timer.h
+++ b/scene/main/timer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 1ecc3c762a..a2399c8b5a 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,7 @@
#include "viewport.h"
+#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/debugger/engine_debugger.h"
#include "core/object/message_queue.h"
@@ -48,6 +49,7 @@
#include "scene/gui/label.h"
#include "scene/gui/popup.h"
#include "scene/gui/popup_menu.h"
+#include "scene/gui/subviewport_container.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
#include "scene/resources/mesh.h"
@@ -55,6 +57,7 @@
#include "scene/resources/world_2d.h"
#include "scene/scene_string_names.h"
#include "servers/audio_server.h"
+#include "servers/rendering/rendering_server_globals.h"
void ViewportTexture::setup_local_to_scene() {
Node *local_scene = get_local_scene();
@@ -236,8 +239,8 @@ void Viewport::_sub_window_update(Window *p_window) {
int font_size = p_window->get_theme_font_size(SNAME("title_font_size"));
Color title_color = p_window->get_theme_color(SNAME("title_color"));
int title_height = p_window->get_theme_constant(SNAME("title_height"));
- int close_h_ofs = p_window->get_theme_constant(SNAME("close_h_ofs"));
- int close_v_ofs = p_window->get_theme_constant(SNAME("close_v_ofs"));
+ int close_h_ofs = p_window->get_theme_constant(SNAME("close_h_offset"));
+ int close_v_ofs = p_window->get_theme_constant(SNAME("close_v_offset"));
TextLine title_text = TextLine(p_window->atr(p_window->get_title()), title_font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
title_text.set_width(r.size.width - panel->get_minimum_size().x - close_h_ofs);
@@ -290,7 +293,7 @@ void Viewport::_sub_window_grab_focus(Window *p_window) {
if (p_window->get_flag(Window::FLAG_NO_FOCUS)) {
// Can only move to foreground, but no focus granted.
SubWindow sw = gui.sub_windows[index];
- gui.sub_windows.remove(index);
+ gui.sub_windows.remove_at(index);
gui.sub_windows.push_back(sw);
index = gui.sub_windows.size() - 1;
_sub_window_update_order();
@@ -318,7 +321,7 @@ void Viewport::_sub_window_grab_focus(Window *p_window) {
{ // Move to foreground.
SubWindow sw = gui.sub_windows[index];
- gui.sub_windows.remove(index);
+ gui.sub_windows.remove_at(index);
gui.sub_windows.push_back(sw);
index = gui.sub_windows.size() - 1;
_sub_window_update_order();
@@ -335,7 +338,7 @@ void Viewport::_sub_window_remove(Window *p_window) {
for (int i = 0; i < gui.sub_windows.size(); i++) {
if (gui.sub_windows[i].window == p_window) {
RS::get_singleton()->free(gui.sub_windows[i].canvas_item);
- gui.sub_windows.remove(i);
+ gui.sub_windows.remove_at(i);
break;
}
}
@@ -370,8 +373,6 @@ void Viewport::_sub_window_remove(Window *p_window) {
void Viewport::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- gui.embedding_subwindows = gui.embed_subwindows_hint;
-
if (get_parent()) {
parent = get_parent()->get_viewport();
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, parent->get_viewport_rid());
@@ -395,22 +396,23 @@ void Viewport::_notification(int p_what) {
#ifndef _3D_DISABLED
PhysicsServer3D::get_singleton()->space_set_debug_contacts(find_world_3d()->get_space(), get_tree()->get_collision_debug_contact_count());
contact_3d_debug_multimesh = RenderingServer::get_singleton()->multimesh_create();
- RenderingServer::get_singleton()->multimesh_allocate_data(contact_3d_debug_multimesh, get_tree()->get_collision_debug_contact_count(), RS::MULTIMESH_TRANSFORM_3D, true);
+ RenderingServer::get_singleton()->multimesh_allocate_data(contact_3d_debug_multimesh, get_tree()->get_collision_debug_contact_count(), RS::MULTIMESH_TRANSFORM_3D, false);
RenderingServer::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, 0);
RenderingServer::get_singleton()->multimesh_set_mesh(contact_3d_debug_multimesh, get_tree()->get_debug_contact_mesh()->get_rid());
contact_3d_debug_instance = RenderingServer::get_singleton()->instance_create();
RenderingServer::get_singleton()->instance_set_base(contact_3d_debug_instance, contact_3d_debug_multimesh);
RenderingServer::get_singleton()->instance_set_scenario(contact_3d_debug_instance, find_world_3d()->get_scenario());
- //RenderingServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, RS::INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS, true);
+ RenderingServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, RS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true);
#endif // _3D_DISABLED
+ set_physics_process_internal(true);
}
-
} break;
+
case NOTIFICATION_READY: {
#ifndef _3D_DISABLED
if (audio_listener_3d_set.size() && !audio_listener_3d) {
AudioListener3D *first = nullptr;
- for (Set<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) {
+ for (RBSet<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) {
if (first == nullptr || first->is_greater_than(E->get())) {
first = E->get();
}
@@ -424,7 +426,7 @@ void Viewport::_notification(int p_what) {
if (camera_3d_set.size() && !camera_3d) {
// There are cameras but no current camera, pick first in tree and make it current.
Camera3D *first = nullptr;
- for (Set<Camera3D *>::Element *E = camera_3d_set.front(); E; E = E->next()) {
+ for (RBSet<Camera3D *>::Element *E = camera_3d_set.front(); E; E = E->next()) {
if (first == nullptr || first->is_greater_than(E->get())) {
first = E->get();
}
@@ -436,6 +438,7 @@ void Viewport::_notification(int p_what) {
}
#endif // _3D_DISABLED
} break;
+
case NOTIFICATION_EXIT_TREE: {
_gui_cancel_tooltip();
@@ -459,7 +462,12 @@ void Viewport::_notification(int p_what) {
RS::get_singleton()->viewport_set_active(viewport, false);
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, RID());
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (!get_tree()) {
+ return;
+ }
+
if (get_tree()->is_debugging_collisions_hint() && contact_2d_debug.is_valid()) {
RenderingServer::get_singleton()->canvas_item_clear(contact_2d_debug);
RenderingServer::get_singleton()->canvas_item_set_draw_index(contact_2d_debug, 0xFFFFF); //very high index
@@ -487,20 +495,29 @@ void Viewport::_notification(int p_what) {
}
#endif // _3D_DISABLED
} break;
- case NOTIFICATION_WM_MOUSE_EXIT: {
- _drop_physics_mouseover();
- // Unlike on loss of focus (NOTIFICATION_WM_WINDOW_FOCUS_OUT), do not
- // drop the gui mouseover here, as a scrollbar may be dragged while the
- // mouse is outside the window (without the window having lost focus).
- // See bug #39634
+ case NOTIFICATION_VP_MOUSE_ENTER: {
+ gui.mouse_in_viewport = true;
} break;
- case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
+
+ case NOTIFICATION_VP_MOUSE_EXIT: {
+ gui.mouse_in_viewport = false;
_drop_physics_mouseover();
+ _drop_mouse_over();
+ // When the mouse exits the viewport, we want to end mouse_over, but
+ // not mouse_focus, because, for example, we want to continue
+ // dragging a scrollbar even if the mouse has left the viewport.
+ } break;
+ case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
+ _drop_physics_mouseover();
if (gui.mouse_focus && !gui.forced_mouse_focus) {
_drop_mouse_focus();
}
+ // When the window focus changes, we want to end mouse_focus, but
+ // not the mouse_over. Note: The OS will trigger a separate mouse
+ // exit event if the change in focus results in the mouse exiting
+ // the window.
} break;
}
}
@@ -518,6 +535,13 @@ void Viewport::_process_picking() {
_drop_physics_mouseover(true);
+#ifndef _3D_DISABLED
+ Vector2 last_pos(1e20, 1e20);
+ CollisionObject3D *last_object = nullptr;
+ ObjectID last_id;
+ PhysicsDirectSpaceState3D::RayResult result;
+#endif // _3D_DISABLED
+
PhysicsDirectSpaceState2D *ss2d = PhysicsServer2D::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
if (physics_has_last_mousepos) {
@@ -623,7 +647,7 @@ void Viewport::_process_picking() {
uint64_t frame = get_tree()->get_frame();
PhysicsDirectSpaceState2D::ShapeResult res[64];
- for (Set<CanvasLayer *>::Element *E = canvas_layers.front(); E; E = E->next()) {
+ for (RBSet<CanvasLayer *>::Element *E = canvas_layers.front(); E; E = E->next()) {
Transform2D canvas_transform;
ObjectID canvas_layer_id;
if (E->get()) {
@@ -651,23 +675,23 @@ void Viewport::_process_picking() {
if (co && co->can_process()) {
bool send_event = true;
if (is_mouse) {
- Map<ObjectID, uint64_t>::Element *F = physics_2d_mouseover.find(res[i].collider_id);
+ HashMap<ObjectID, uint64_t>::Iterator F = physics_2d_mouseover.find(res[i].collider_id);
if (!F) {
physics_2d_mouseover.insert(res[i].collider_id, frame);
co->_mouse_enter();
} else {
- F->get() = frame;
+ F->value = frame;
// It was already hovered, so don't send the event if it's faked.
if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
send_event = false;
}
}
- Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *SF = physics_2d_shape_mouseover.find(Pair(res[i].collider_id, res[i].shape));
+ HashMap<Pair<ObjectID, int>, uint64_t, PairHash<ObjectID, int>>::Iterator SF = physics_2d_shape_mouseover.find(Pair(res[i].collider_id, res[i].shape));
if (!SF) {
physics_2d_shape_mouseover.insert(Pair(res[i].collider_id, res[i].shape), frame);
co->_mouse_shape_enter(res[i].shape);
} else {
- SF->get() = frame;
+ SF->value = frame;
}
}
@@ -685,10 +709,6 @@ void Viewport::_process_picking() {
}
#ifndef _3D_DISABLED
- Vector2 last_pos(1e20, 1e20);
- CollisionObject3D *last_object = nullptr;
- ObjectID last_id;
- PhysicsDirectSpaceState3D::RayResult result;
bool captured = false;
if (physics_object_capture.is_valid()) {
@@ -785,7 +805,7 @@ void Viewport::update_canvas_items() {
}
void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_2d_override, const Rect2i &p_to_screen_rect, const Transform2D &p_stretch_transform, bool p_allocated) {
- if (size == p_size && size_allocated == p_allocated && stretch_transform == p_stretch_transform && p_size_2d_override == size_2d_override && to_screen_rect != p_to_screen_rect) {
+ if (size == p_size && size_allocated == p_allocated && stretch_transform == p_stretch_transform && p_size_2d_override == size_2d_override && to_screen_rect == p_to_screen_rect) {
return;
}
@@ -1018,8 +1038,8 @@ Transform2D Viewport::get_final_transform() const {
void Viewport::_update_canvas_items(Node *p_node) {
if (p_node != this) {
- Viewport *vp = Object::cast_to<Viewport>(p_node);
- if (vp) {
+ Window *w = Object::cast_to<Window>(p_node);
+ if (w && (!w->is_inside_tree() || !w->is_embedded())) {
return;
}
@@ -1084,8 +1104,8 @@ Transform2D Viewport::_get_input_pre_xform() const {
Transform2D pre_xf;
if (to_screen_rect.size.x != 0 && to_screen_rect.size.y != 0) {
- pre_xf.elements[2] = -to_screen_rect.position;
- pre_xf.scale(size / to_screen_rect.size);
+ pre_xf.columns[2] = -to_screen_rect.position;
+ pre_xf.scale(Vector2(size) / to_screen_rect.size);
}
return pre_xf;
@@ -1104,9 +1124,10 @@ Vector2 Viewport::get_mouse_position() const {
return gui.last_mouse_pos;
}
-void Viewport::warp_mouse(const Vector2 &p_pos) {
- Vector2 gpos = (get_final_transform().affine_inverse() * _get_input_pre_xform()).affine_inverse().xform(p_pos);
- Input::get_singleton()->warp_mouse_position(gpos);
+void Viewport::warp_mouse(const Vector2 &p_position) {
+ Transform2D xform = get_screen_transform();
+ Vector2 gpos = xform.xform(p_position).round();
+ Input::get_singleton()->warp_mouse(gpos);
}
void Viewport::_gui_sort_roots() {
@@ -1217,8 +1238,9 @@ void Viewport::_gui_show_tooltip() {
base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- panel->set_transient(false);
+ panel->set_transient(true);
panel->set_flag(Window::FLAG_NO_FOCUS, true);
+ panel->set_flag(Window::FLAG_POPUP, false);
panel->set_wrap_controls(true);
panel->add_child(base_tooltip);
@@ -1248,7 +1270,10 @@ void Viewport::_gui_show_tooltip() {
gui.tooltip_popup->set_position(r.position);
gui.tooltip_popup->set_size(r.size);
- gui.tooltip_popup->show();
+ DisplayServer::WindowID active_popup = DisplayServer::get_singleton()->window_get_active_popup();
+ if (active_popup == DisplayServer::INVALID_WINDOW_ID || active_popup == window->get_window_id()) {
+ gui.tooltip_popup->show();
+ }
gui.tooltip_popup->child_controls_changed();
}
@@ -1439,8 +1464,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (mb.is_valid()) {
gui.key_event_accepted = false;
- Control *over = nullptr;
-
Point2 mpos = mb->get_position();
gui.last_mouse_pos = mpos;
if (mb->is_pressed()) {
@@ -1515,8 +1538,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) {
// Alternate drop use (when using force_drag(), as proposed by #5342).
+ gui.drag_successful = false;
if (gui.mouse_focus) {
- _gui_drop(gui.mouse_focus, pos, false);
+ gui.drag_successful = _gui_drop(gui.mouse_focus, pos, false);
}
gui.drag_data = Variant();
@@ -1534,8 +1558,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_cancel_tooltip();
} else {
if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) {
+ gui.drag_successful = false;
if (gui.drag_mouse_over) {
- _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false);
+ gui.drag_successful = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false);
}
Control *drag_preview = _gui_get_drag_preview();
@@ -1579,31 +1604,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_call_input(mouse_focus, mb);
}
- // In case the mouse was released after for example dragging a scrollbar,
- // check whether the current control is different from the stored one. If
- // it is different, rather than wait for it to be updated the next time the
- // mouse is moved, notify the control so that it can e.g. drop the highlight.
- // This code is duplicated from the mm.is_valid()-case further below.
- if (gui.mouse_focus) {
- over = gui.mouse_focus;
- } else {
- over = gui_find_control(mpos);
- }
-
- if (gui.mouse_focus_mask == MouseButton::NONE && over != gui.mouse_over) {
- if (gui.mouse_over) {
- _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
- }
-
- _gui_cancel_tooltip();
-
- if (over) {
- _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER);
- }
- }
-
- gui.mouse_over = over;
-
set_input_as_handled();
}
}
@@ -1615,8 +1615,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.last_mouse_pos = mpos;
- Control *over = nullptr;
-
// Drag & drop.
if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
gui.drag_accum += mm->get_relative();
@@ -1664,54 +1662,48 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- // These sections of code are reused in the mb.is_valid() case further up
- // for the purpose of notifying controls about potential changes in focus
- // when the mousebutton is released.
- if (gui.mouse_focus) {
- over = gui.mouse_focus;
- } else {
+ Control *over = nullptr;
+ if (gui.mouse_in_viewport) {
over = gui_find_control(mpos);
}
if (over != gui.mouse_over) {
- if (gui.mouse_over) {
- _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
- }
-
+ _drop_mouse_over();
_gui_cancel_tooltip();
if (over) {
_gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER);
+ gui.mouse_over = over;
}
}
- gui.mouse_over = over;
+ if (gui.mouse_focus) {
+ over = gui.mouse_focus;
+ }
DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape();
if (over) {
Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
Size2 pos = localizer.xform(mpos);
- Vector2 speed = localizer.basis_xform(mm->get_speed());
+ Vector2 velocity = localizer.basis_xform(mm->get_velocity());
Vector2 rel = localizer.basis_xform(mm->get_relative());
mm = mm->xformed_by(Transform2D()); // Make a copy.
mm->set_global_position(mpos);
- mm->set_speed(speed);
+ mm->set_velocity(velocity);
mm->set_relative(rel);
if (mm->get_button_mask() == MouseButton::NONE) {
// Nothing pressed.
- bool can_tooltip = true;
-
bool is_tooltip_shown = false;
if (gui.tooltip_popup) {
- if (can_tooltip && gui.tooltip_control) {
+ if (gui.tooltip_control) {
String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform().xform_inv(mpos));
-
+ tooltip = tooltip.strip_edges();
if (tooltip.length() == 0) {
_gui_cancel_tooltip();
} else if (gui.tooltip_label) {
@@ -1734,7 +1726,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- if (can_tooltip && !is_tooltip_shown) {
+ if (!is_tooltip_shown && over->can_process()) {
if (gui.tooltip_timer.is_valid()) {
gui.tooltip_timer->release_connections();
gui.tooltip_timer = Ref<SceneTreeTimer>();
@@ -1859,22 +1851,22 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (viewport_under) {
- Transform2D ai = (viewport_under->get_final_transform().affine_inverse() * viewport_under->_get_input_pre_xform());
- viewport_pos = ai.xform(viewport_pos);
+ if (viewport_under != this) {
+ Transform2D ai = (viewport_under->get_final_transform().affine_inverse() * viewport_under->_get_input_pre_xform());
+ viewport_pos = ai.xform(viewport_pos);
+ }
// Find control under at position.
gui.drag_mouse_over = viewport_under->gui_find_control(viewport_pos);
if (gui.drag_mouse_over) {
Transform2D localizer = gui.drag_mouse_over->get_global_transform_with_canvas().affine_inverse();
gui.drag_mouse_over_pos = localizer.xform(viewport_pos);
- if ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
- bool can_drop = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, true);
+ bool can_drop = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, true);
- if (!can_drop) {
- ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN;
- } else {
- ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP;
- }
+ if (!can_drop) {
+ ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN;
+ } else {
+ ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP;
}
}
@@ -1952,12 +1944,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (over->can_process()) {
Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
Size2 pos = localizer.xform(drag_event->get_position());
- Vector2 speed = localizer.basis_xform(drag_event->get_speed());
+ Vector2 velocity = localizer.basis_xform(drag_event->get_velocity());
Vector2 rel = localizer.basis_xform(drag_event->get_relative());
drag_event = drag_event->xformed_by(Transform2D()); // Make a copy.
- drag_event->set_speed(speed);
+ drag_event->set_velocity(velocity);
drag_event->set_relative(rel);
drag_event->set_position(pos);
@@ -1991,30 +1983,58 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (from && p_event->is_pressed()) {
Control *next = nullptr;
- if (p_event->is_action_pressed("ui_focus_next", true, true)) {
- next = from->find_next_valid_focus();
- }
+ Ref<InputEventJoypadMotion> joypadmotion_event = p_event;
+ if (joypadmotion_event.is_valid()) {
+ Input *input = Input::get_singleton();
- if (p_event->is_action_pressed("ui_focus_prev", true, true)) {
- next = from->find_prev_valid_focus();
- }
+ if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) {
+ next = from->find_next_valid_focus();
+ }
- if (p_event->is_action_pressed("ui_up", true, true)) {
- next = from->_get_focus_neighbor(SIDE_TOP);
- }
+ if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) {
+ next = from->find_prev_valid_focus();
+ }
- if (p_event->is_action_pressed("ui_left", true, true)) {
- next = from->_get_focus_neighbor(SIDE_LEFT);
- }
+ if (p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) {
+ next = from->_get_focus_neighbor(SIDE_TOP);
+ }
- if (p_event->is_action_pressed("ui_right", true, true)) {
- next = from->_get_focus_neighbor(SIDE_RIGHT);
- }
+ if (p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) {
+ next = from->_get_focus_neighbor(SIDE_LEFT);
+ }
- if (p_event->is_action_pressed("ui_down", true, true)) {
- next = from->_get_focus_neighbor(SIDE_BOTTOM);
- }
+ if (p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) {
+ next = from->_get_focus_neighbor(SIDE_RIGHT);
+ }
+ if (p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) {
+ next = from->_get_focus_neighbor(SIDE_BOTTOM);
+ }
+ } else {
+ if (p_event->is_action_pressed("ui_focus_next", true, true)) {
+ next = from->find_next_valid_focus();
+ }
+
+ if (p_event->is_action_pressed("ui_focus_prev", true, true)) {
+ next = from->find_prev_valid_focus();
+ }
+
+ if (p_event->is_action_pressed("ui_up", true, true)) {
+ next = from->_get_focus_neighbor(SIDE_TOP);
+ }
+
+ if (p_event->is_action_pressed("ui_left", true, true)) {
+ next = from->_get_focus_neighbor(SIDE_LEFT);
+ }
+
+ if (p_event->is_action_pressed("ui_right", true, true)) {
+ next = from->_get_focus_neighbor(SIDE_RIGHT);
+ }
+
+ if (p_event->is_action_pressed("ui_down", true, true)) {
+ next = from->_get_focus_neighbor(SIDE_BOTTOM);
+ }
+ }
if (next) {
next->grab_focus();
set_input_as_handled();
@@ -2023,6 +2043,17 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
+void Viewport::_gui_cleanup_internal_state(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ if (!mb->is_pressed()) {
+ gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask.
+ }
+ }
+}
+
List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) {
gui.roots_order_dirty = true;
return gui.roots.push_back(p_control);
@@ -2042,6 +2073,7 @@ void Viewport::_gui_force_drag(Control *p_base, const Variant &p_data, Control *
if (p_control) {
_gui_set_drag_preview(p_base, p_control);
}
+ _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN);
}
void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
@@ -2091,7 +2123,7 @@ void Viewport::_gui_hide_control(Control *p_control) {
}
if (gui.key_focus == p_control) {
- _gui_remove_focus();
+ gui_release_focus();
}
if (gui.mouse_over == p_control) {
gui.mouse_over = nullptr;
@@ -2141,15 +2173,7 @@ Window *Viewport::get_base_window() const {
}
void Viewport::_gui_remove_focus_for_window(Node *p_window) {
if (get_base_window() == p_window) {
- _gui_remove_focus();
- }
-}
-
-void Viewport::_gui_remove_focus() {
- if (gui.key_focus) {
- Node *f = gui.key_focus;
- gui.key_focus = nullptr;
- f->notification(Control::NOTIFICATION_FOCUS_EXIT, true);
+ gui_release_focus();
}
}
@@ -2162,7 +2186,7 @@ void Viewport::_gui_control_grab_focus(Control *p_control) {
// No need for change.
return;
}
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, "_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window());
+ get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window());
gui.key_focus = p_control;
emit_signal(SNAME("gui_focus_changed"), p_control);
p_control->notification(Control::NOTIFICATION_FOCUS_ENTER);
@@ -2176,6 +2200,13 @@ void Viewport::_gui_accept_event() {
}
}
+void Viewport::_drop_mouse_over() {
+ if (gui.mouse_over) {
+ _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
+ gui.mouse_over = nullptr;
+ }
+}
+
void Viewport::_drop_mouse_focus() {
Control *c = gui.mouse_focus;
MouseButton mask = gui.mouse_focus_mask;
@@ -2219,14 +2250,14 @@ void Viewport::_drop_physics_mouseover(bool p_paused_only) {
}
void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference) {
- List<Map<ObjectID, uint64_t>::Element *> to_erase;
+ List<ObjectID> to_erase;
- for (Map<ObjectID, uint64_t>::Element *E = physics_2d_mouseover.front(); E; E = E->next()) {
- if (!p_clean_all_frames && E->get() == p_frame_reference) {
+ for (const KeyValue<ObjectID, uint64_t> &E : physics_2d_mouseover) {
+ if (!p_clean_all_frames && E.value == p_frame_reference) {
continue;
}
- Object *o = ObjectDB::get_instance(E->key());
+ Object *o = ObjectDB::get_instance(E.key);
if (o) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
if (co && co->is_inside_tree()) {
@@ -2236,7 +2267,7 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
co->_mouse_exit();
}
}
- to_erase.push_back(E);
+ to_erase.push_back(E.key);
}
while (to_erase.size()) {
@@ -2245,24 +2276,24 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
}
// Per-shape.
- List<Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *> shapes_to_erase;
+ List<Pair<ObjectID, int>> shapes_to_erase;
- for (Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *E = physics_2d_shape_mouseover.front(); E; E = E->next()) {
- if (!p_clean_all_frames && E->get() == p_frame_reference) {
+ for (KeyValue<Pair<ObjectID, int>, uint64_t> &E : physics_2d_shape_mouseover) {
+ if (!p_clean_all_frames && E.value == p_frame_reference) {
continue;
}
- Object *o = ObjectDB::get_instance(E->key().first);
+ Object *o = ObjectDB::get_instance(E.key.first);
if (o) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
if (co && co->is_inside_tree()) {
if (p_clean_all_frames && p_paused_only && co->can_process()) {
continue;
}
- co->_mouse_shape_exit(E->key().second);
+ co->_mouse_shape_exit(E.key.second);
}
}
- shapes_to_erase.push_back(E);
+ shapes_to_erase.push_back(E.key);
}
while (shapes_to_erase.size()) {
@@ -2271,10 +2302,6 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
}
}
-Control *Viewport::_gui_get_focus_owner() {
- return gui.key_focus;
-}
-
void Viewport::_gui_grab_click_focus(Control *p_control) {
gui.mouse_click_grabber = p_control;
call_deferred(SNAME("_post_gui_grab_click_focus"));
@@ -2344,7 +2371,7 @@ void Viewport::push_text_input(const String &p_text) {
}
Viewport::SubWindowResize Viewport::_sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point) {
- if (p_subwindow->get_flag(Window::FLAG_BORDERLESS)) {
+ if (p_subwindow->get_flag(Window::FLAG_BORDERLESS) || p_subwindow->get_flag(Window::FLAG_RESIZE_DISABLED)) {
return SUB_WINDOW_RESIZE_DISABLED;
}
@@ -2540,7 +2567,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
bool click_on_window = false;
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
- SubWindow &sw = gui.sub_windows.write[i];
+ SubWindow sw = gui.sub_windows.write[i];
// Clicked inside window?
@@ -2556,8 +2583,8 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
if (title_bar.has_point(mb->get_position())) {
click_on_window = true;
- int close_h_ofs = sw.window->get_theme_constant(SNAME("close_h_ofs"));
- int close_v_ofs = sw.window->get_theme_constant(SNAME("close_v_ofs"));
+ int close_h_ofs = sw.window->get_theme_constant(SNAME("close_h_offset"));
+ int close_v_ofs = sw.window->get_theme_constant(SNAME("close_v_offset"));
Ref<Texture2D> close_icon = sw.window->get_theme_icon(SNAME("close"));
Rect2 close_rect;
@@ -2674,7 +2701,7 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
ev = p_event;
}
- if (is_embedding_subwindows() && _sub_windows_forward_input(p_event)) {
+ if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) {
set_input_as_handled();
return;
}
@@ -2689,6 +2716,9 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
if (!is_input_handled()) {
_gui_input_event(ev);
+ } else {
+ // Cleanup internal GUI state after accepting event during _input().
+ _gui_cleanup_internal_state(ev);
}
event_count++;
@@ -2714,11 +2744,18 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local
ev = p_event;
}
+ // Shortcut Input.
+ if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr) {
+ get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, ev, this);
+ }
+
// Unhandled Input.
- get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, ev, this);
+ if (!is_input_handled()) {
+ get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, ev, this);
+ }
- // Unhandled key Input - used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, etc.
- if (!is_input_handled() && (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr)) {
+ // Unhandled key Input - Used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, and to handle Unicode input with Alt / Ctrl modifiers after handling shortcuts.
+ if (!is_input_handled() && (Object::cast_to<InputEventKey>(*ev) != nullptr)) {
get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, ev, this);
}
@@ -2762,6 +2799,14 @@ Vector2 Viewport::get_camera_rect_size() const {
}
void Viewport::set_disable_input(bool p_disable) {
+ if (p_disable == disable_input) {
+ return;
+ }
+ if (p_disable) {
+ _drop_mouse_focus();
+ _drop_mouse_over();
+ _gui_cancel_tooltip();
+ }
disable_input = p_disable;
}
@@ -2777,7 +2822,7 @@ TypedArray<String> Viewport::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (size.x == 0 || size.y == 0) {
- warnings.push_back(TTR("Viewport size must be greater than 0 to render anything."));
+ warnings.push_back(RTR("Viewport size must be greater than 0 to render anything."));
}
return warnings;
}
@@ -2790,6 +2835,19 @@ int Viewport::gui_get_canvas_sort_index() {
return gui.canvas_sort_index++;
}
+void Viewport::gui_release_focus() {
+ if (gui.key_focus) {
+ Control *f = gui.key_focus;
+ gui.key_focus = nullptr;
+ f->notification(Control::NOTIFICATION_FOCUS_EXIT, true);
+ f->update();
+ }
+}
+
+Control *Viewport::gui_get_focus_owner() {
+ return gui.key_focus;
+}
+
void Viewport::set_msaa(MSAA p_msaa) {
ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
if (msaa == p_msaa) {
@@ -2828,13 +2886,13 @@ bool Viewport::is_using_debanding() const {
return use_debanding;
}
-void Viewport::set_lod_threshold(float p_pixels) {
- lod_threshold = p_pixels;
- RS::get_singleton()->viewport_set_lod_threshold(viewport, lod_threshold);
+void Viewport::set_mesh_lod_threshold(float p_pixels) {
+ mesh_lod_threshold = p_pixels;
+ RS::get_singleton()->viewport_set_mesh_lod_threshold(viewport, mesh_lod_threshold);
}
-float Viewport::get_lod_threshold() const {
- return lod_threshold;
+float Viewport::get_mesh_lod_threshold() const {
+ return mesh_lod_threshold;
}
void Viewport::set_use_occlusion_culling(bool p_use_occlusion_culling) {
@@ -2895,6 +2953,10 @@ bool Viewport::gui_is_dragging() const {
return gui.dragging;
}
+bool Viewport::gui_is_drag_successful() const {
+ return gui.drag_successful;
+}
+
void Viewport::set_input_as_handled() {
_drop_physics_mouseover();
@@ -3017,14 +3079,10 @@ Viewport *Viewport::get_parent_viewport() const {
return get_parent()->get_viewport();
}
-void Viewport::set_embed_subwindows_hint(bool p_embed) {
+void Viewport::set_embedding_subwindows(bool p_embed) {
gui.embed_subwindows_hint = p_embed;
}
-bool Viewport::get_embed_subwindows_hint() const {
- return gui.embed_subwindows_hint;
-}
-
bool Viewport::is_embedding_subwindows() const {
return gui.embed_subwindows_hint;
}
@@ -3065,6 +3123,10 @@ Viewport::SDFScale Viewport::get_sdf_scale() const {
return sdf_scale;
}
+Transform2D Viewport::get_screen_transform() const {
+ return _get_input_pre_xform().affine_inverse() * get_final_transform();
+}
+
#ifndef _3D_DISABLED
AudioListener3D *Viewport::get_audio_listener_3d() const {
return audio_listener_3d;
@@ -3117,7 +3179,7 @@ void Viewport::_audio_listener_3d_remove(AudioListener3D *p_listener) {
void Viewport::_audio_listener_3d_make_next_current(AudioListener3D *p_exclude) {
if (audio_listener_3d_set.size() > 0) {
- for (Set<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) {
+ for (RBSet<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) {
if (p_exclude == E->get()) {
continue;
}
@@ -3205,7 +3267,7 @@ void Viewport::_camera_3d_remove(Camera3D *p_camera) {
}
void Viewport::_camera_3d_make_next_current(Camera3D *p_exclude) {
- for (Set<Camera3D *>::Element *E = camera_3d_set.front(); E; E = E->next()) {
+ for (RBSet<Camera3D *>::Element *E = camera_3d_set.front(); E; E = E->next()) {
if (p_exclude == E->get()) {
continue;
}
@@ -3467,17 +3529,60 @@ bool Viewport::is_using_xr() {
return use_xr;
}
-void Viewport::set_scale_3d(float p_scale_3d) {
+void Viewport::set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode) {
+ if (scaling_3d_mode == p_scaling_3d_mode) {
+ return;
+ }
+
+ scaling_3d_mode = p_scaling_3d_mode;
+ RS::get_singleton()->viewport_set_scaling_3d_mode(viewport, (RS::ViewportScaling3DMode)(int)p_scaling_3d_mode);
+}
+
+Viewport::Scaling3DMode Viewport::get_scaling_3d_mode() const {
+ return scaling_3d_mode;
+}
+
+void Viewport::set_scaling_3d_scale(float p_scaling_3d_scale) {
// Clamp to reasonable values that are actually useful.
// Values above 2.0 don't serve a practical purpose since the viewport
// isn't displayed with mipmaps.
- scale_3d = CLAMP(p_scale_3d, 0.1, 2.0);
+ scaling_3d_scale = CLAMP(p_scaling_3d_scale, 0.1, 2.0);
- RS::get_singleton()->viewport_set_scale_3d(viewport, scale_3d);
+ RS::get_singleton()->viewport_set_scaling_3d_scale(viewport, scaling_3d_scale);
}
-float Viewport::get_scale_3d() const {
- return scale_3d;
+float Viewport::get_scaling_3d_scale() const {
+ return scaling_3d_scale;
+}
+
+void Viewport::set_fsr_sharpness(float p_fsr_sharpness) {
+ if (fsr_sharpness == p_fsr_sharpness) {
+ return;
+ }
+
+ if (p_fsr_sharpness < 0.0f) {
+ p_fsr_sharpness = 0.0f;
+ }
+
+ fsr_sharpness = p_fsr_sharpness;
+ RS::get_singleton()->viewport_set_fsr_sharpness(viewport, p_fsr_sharpness);
+}
+
+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) {
+ return;
+ }
+
+ fsr_mipmap_bias = p_fsr_mipmap_bias;
+ RS::get_singleton()->viewport_set_fsr_mipmap_bias(viewport, p_fsr_mipmap_bias);
+}
+
+float Viewport::get_fsr_mipmap_bias() const {
+ return fsr_mipmap_bias;
}
#endif // _3D_DISABLED
@@ -3530,10 +3635,14 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
- ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Viewport::warp_mouse);
+ ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Viewport::warp_mouse);
ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data);
ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging);
+ ClassDB::bind_method(D_METHOD("gui_is_drag_successful"), &Viewport::gui_is_drag_successful);
+
+ ClassDB::bind_method(D_METHOD("gui_release_focus"), &Viewport::gui_release_focus);
+ ClassDB::bind_method(D_METHOD("gui_get_focus_owner"), &Viewport::gui_get_focus_owner);
ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input);
ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled);
@@ -3568,8 +3677,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_filter", "mode"), &Viewport::set_default_canvas_item_texture_filter);
ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_filter"), &Viewport::get_default_canvas_item_texture_filter);
- ClassDB::bind_method(D_METHOD("set_embed_subwindows_hint", "enable"), &Viewport::set_embed_subwindows_hint);
- ClassDB::bind_method(D_METHOD("get_embed_subwindows_hint"), &Viewport::get_embed_subwindows_hint);
+ ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows);
ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows);
ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_repeat", "mode"), &Viewport::set_default_canvas_item_texture_repeat);
@@ -3581,8 +3689,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sdf_scale", "scale"), &Viewport::set_sdf_scale);
ClassDB::bind_method(D_METHOD("get_sdf_scale"), &Viewport::get_sdf_scale);
- ClassDB::bind_method(D_METHOD("set_lod_threshold", "pixels"), &Viewport::set_lod_threshold);
- ClassDB::bind_method(D_METHOD("get_lod_threshold"), &Viewport::get_lod_threshold);
+ ClassDB::bind_method(D_METHOD("set_mesh_lod_threshold", "pixels"), &Viewport::set_mesh_lod_threshold);
+ ClassDB::bind_method(D_METHOD("get_mesh_lod_threshold"), &Viewport::get_mesh_lod_threshold);
ClassDB::bind_method(D_METHOD("_process_picking"), &Viewport::_process_picking);
@@ -3604,13 +3712,20 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &Viewport::set_use_xr);
ClassDB::bind_method(D_METHOD("is_using_xr"), &Viewport::is_using_xr);
- ClassDB::bind_method(D_METHOD("set_scale_3d", "scale"), &Viewport::set_scale_3d);
- ClassDB::bind_method(D_METHOD("get_scale_3d"), &Viewport::get_scale_3d);
+ ClassDB::bind_method(D_METHOD("set_scaling_3d_mode", "scaling_3d_mode"), &Viewport::set_scaling_3d_mode);
+ ClassDB::bind_method(D_METHOD("get_scaling_3d_mode"), &Viewport::get_scaling_3d_mode);
+
+ ClassDB::bind_method(D_METHOD("set_scaling_3d_scale", "scale"), &Viewport::set_scaling_3d_scale);
+ ClassDB::bind_method(D_METHOD("get_scaling_3d_scale"), &Viewport::get_scaling_3d_scale);
+
+ 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);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scale_3d", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scale_3d", "get_scale_3d");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d");
#endif // _3D_DISABLED
@@ -3624,19 +3739,29 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), "set_screen_space_aa", "get_screen_space_aa");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
+#ifndef _3D_DISABLED
+ 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, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
+#endif
ADD_GROUP("Canvas Items", "canvas_item_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat");
ADD_GROUP("Audio Listener", "audio_listener_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_2d"), "set_as_audio_listener_2d", "is_audio_listener_2d");
+#ifndef _3D_DISABLED
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d");
+#endif
ADD_GROUP("Physics", "physics_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking"), "set_physics_object_picking", "get_physics_object_picking");
ADD_GROUP("GUI", "gui_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_disable_input"), "set_disable_input", "is_input_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_snap_controls_to_pixels"), "set_snap_controls_to_pixels", "is_snap_controls_to_pixels_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_embed_subwindows"), "set_embed_subwindows_hint", "get_embed_subwindows_hint");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_embed_subwindows"), "set_embedding_subwindows", "is_embedding_subwindows");
ADD_GROUP("SDF", "sdf_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%"), "set_sdf_oversize", "get_sdf_oversize");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_scale", PROPERTY_HINT_ENUM, "100%,50%,25%"), "set_sdf_scale", "get_sdf_scale");
@@ -3662,6 +3787,10 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1024);
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_MAX);
+ BIND_ENUM_CONSTANT(SCALING_3D_MODE_BILINEAR);
+ BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR);
+ BIND_ENUM_CONSTANT(SCALING_3D_MODE_MAX);
+
BIND_ENUM_CONSTANT(MSAA_DISABLED);
BIND_ENUM_CONSTANT(MSAA_2X);
BIND_ENUM_CONSTANT(MSAA_4X);
@@ -3694,6 +3823,7 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS);
BIND_ENUM_CONSTANT(DEBUG_DRAW_SCENE_LUMINANCE);
BIND_ENUM_CONSTANT(DEBUG_DRAW_SSAO);
+ BIND_ENUM_CONSTANT(DEBUG_DRAW_SSIL);
BIND_ENUM_CONSTANT(DEBUG_DRAW_PSSM_SPLITS);
BIND_ENUM_CONSTANT(DEBUG_DRAW_DECAL_ATLAS);
BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI);
@@ -3752,12 +3882,13 @@ Viewport::Viewport() {
set_shadow_atlas_quadrant_subdiv(2, SHADOW_ATLAS_QUADRANT_SUBDIV_16);
set_shadow_atlas_quadrant_subdiv(3, SHADOW_ATLAS_QUADRANT_SUBDIV_64);
- set_lod_threshold(lod_threshold);
+ set_mesh_lod_threshold(mesh_lod_threshold);
String id = itos(get_instance_id());
input_group = "_vp_input" + id;
gui_input_group = "_vp_gui_input" + id;
unhandled_input_group = "_vp_unhandled_input" + id;
+ shortcut_input_group = "_vp_shortcut_input" + id;
unhandled_key_input_group = "_vp_unhandled_key_input" + id;
// Window tooltip.
@@ -3765,7 +3896,16 @@ Viewport::Viewport() {
ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers
#ifndef _3D_DISABLED
- set_scale_3d(GLOBAL_GET("rendering/3d/viewport/scale"));
+ Viewport::Scaling3DMode scaling_3d_mode = (Viewport::Scaling3DMode)(int)GLOBAL_GET("rendering/scaling_3d/mode");
+ set_scaling_3d_mode(scaling_3d_mode);
+
+ set_scaling_3d_scale(GLOBAL_GET("rendering/scaling_3d/scale"));
+
+ 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);
#endif // _3D_DISABLED
set_sdf_oversize(sdf_oversize); // Set to server.
@@ -3773,7 +3913,7 @@ Viewport::Viewport() {
Viewport::~Viewport() {
// Erase itself from viewport textures.
- for (Set<ViewportTexture *>::Element *E = viewport_textures.front(); E; E = E->next()) {
+ for (RBSet<ViewportTexture *>::Element *E = viewport_textures.front(); E; E = E->next()) {
E->get()->vp = nullptr;
}
RenderingServer::get_singleton()->free(viewport);
@@ -3783,6 +3923,11 @@ Viewport::~Viewport() {
void SubViewport::set_size(const Size2i &p_size) {
_set_size(p_size, _get_size_2d_override(), Rect2i(), _stretch_transform(), true);
+
+ SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
+ if (c) {
+ c->update_minimum_size();
+ }
}
Size2i SubViewport::get_size() const {
@@ -3843,12 +3988,29 @@ Transform2D SubViewport::_stretch_transform() {
return transform;
}
-void SubViewport::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
+Transform2D SubViewport::get_screen_transform() const {
+ Transform2D container_transform = Transform2D();
+ SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
+ if (c) {
+ if (c->is_stretch_enabled()) {
+ container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
+ }
+ container_transform = c->get_viewport()->get_screen_transform() * c->get_global_transform_with_canvas() * container_transform;
+ } else {
+ WARN_PRINT_ONCE("SubViewport is not a child of a SubViewportContainer. get_screen_transform doesn't return the actual screen position.");
}
- if (p_what == NOTIFICATION_EXIT_TREE) {
- RS::get_singleton()->viewport_set_active(get_viewport_rid(), false);
+ return container_transform * Viewport::get_screen_transform();
+}
+
+void SubViewport::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ RS::get_singleton()->viewport_set_active(get_viewport_rid(), false);
+ } break;
}
}
@@ -3868,8 +4030,8 @@ void SubViewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &SubViewport::set_clear_mode);
ClassDB::bind_method(D_METHOD("get_clear_mode"), &SubViewport::get_clear_mode);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_2d_override"), "set_size_2d_override", "get_size_2d_override");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size_2d_override"), "set_size_2d_override", "get_size_2d_override");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled");
ADD_GROUP("Render Target", "render_target_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_clear_mode", PROPERTY_HINT_ENUM, "Always,Never,Next Frame"), "set_clear_mode", "get_clear_mode");
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 5320aea02a..c1e4b30c20 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -89,6 +89,12 @@ class Viewport : public Node {
GDCLASS(Viewport, Node);
public:
+ enum Scaling3DMode {
+ SCALING_3D_MODE_BILINEAR,
+ SCALING_3D_MODE_FSR,
+ SCALING_3D_MODE_MAX
+ };
+
enum ShadowAtlasQuadrantSubdiv {
SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED,
SHADOW_ATLAS_QUADRANT_SUBDIV_1,
@@ -142,6 +148,7 @@ public:
DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
DEBUG_DRAW_SCENE_LUMINANCE,
DEBUG_DRAW_SSAO,
+ DEBUG_DRAW_SSIL,
DEBUG_DRAW_PSSM_SPLITS,
DEBUG_DRAW_DECAL_ATLAS,
DEBUG_DRAW_SDFGI,
@@ -196,7 +203,7 @@ private:
AudioListener2D *audio_listener_2d = nullptr;
Camera2D *camera_2d = nullptr;
- Set<CanvasLayer *> canvas_layers;
+ RBSet<CanvasLayer *> canvas_layers;
RID viewport;
RID current_canvas;
@@ -223,7 +230,6 @@ private:
Rect2 last_vp_rect;
bool transparent_bg = false;
- bool filter;
bool gen_mipmaps = false;
bool snap_controls_to_pixels = true;
@@ -252,9 +258,9 @@ private:
bool local_input_handled = false;
// Collider to frame
- Map<ObjectID, uint64_t> physics_2d_mouseover;
+ HashMap<ObjectID, uint64_t> physics_2d_mouseover;
// Collider & shape to frame
- Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>> physics_2d_shape_mouseover;
+ HashMap<Pair<ObjectID, int>, uint64_t, PairHash<ObjectID, int>> physics_2d_shape_mouseover;
// Cleans up colliders corresponding to old frames or all of them.
void _cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference = 0);
@@ -263,6 +269,7 @@ private:
Rect2i to_screen_rect;
StringName input_group;
StringName gui_input_group;
+ StringName shortcut_input_group;
StringName unhandled_input_group;
StringName unhandled_key_input_group;
@@ -284,12 +291,17 @@ private:
MSAA msaa = MSAA_DISABLED;
ScreenSpaceAA screen_space_aa = SCREEN_SPACE_AA_DISABLED;
+
+ 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;
bool use_debanding = false;
- float lod_threshold = 1.0;
+ float mesh_lod_threshold = 1.0;
bool use_occlusion_culling = false;
Ref<ViewportTexture> default_texture;
- Set<ViewportTexture *> viewport_textures;
+ RBSet<ViewportTexture *> viewport_textures;
SDFOversize sdf_oversize = SDF_OVERSIZE_120_PERCENT;
SDFScale sdf_scale = SDF_SCALE_50_PERCENT;
@@ -323,6 +335,7 @@ private:
// info used when this is a window
bool forced_mouse_focus = false; //used for menu buttons
+ bool mouse_in_viewport = true;
bool key_event_accepted = false;
Control *mouse_focus = nullptr;
Control *last_mouse_focus = nullptr;
@@ -348,8 +361,8 @@ private:
List<Control *> roots;
int canvas_sort_index = 0; //for sorting items with canvas as root
bool dragging = false;
+ bool drag_successful = false;
bool embed_subwindows_hint = false;
- bool embedding_subwindows = false;
Window *subwindow_focused = nullptr;
SubWindowDrag subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
@@ -360,7 +373,7 @@ private:
SubWindowResize subwindow_resize_mode;
Rect2i subwindow_resize_from_rect;
- Vector<SubWindow> sub_windows;
+ Vector<SubWindow> sub_windows; // Don't obtain references or pointers to the elements, as their location can change.
} gui;
DefaultCanvasItemTextureFilter default_canvas_item_texture_filter = DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
@@ -375,6 +388,7 @@ private:
Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform);
void _gui_input_event(Ref<InputEvent> p_event);
+ void _gui_cleanup_internal_state(Ref<InputEvent> p_event);
_FORCE_INLINE_ Transform2D _get_input_pre_xform() const;
@@ -398,7 +412,6 @@ private:
Control *_gui_get_drag_preview();
void _gui_remove_focus_for_window(Node *p_window);
- void _gui_remove_focus();
void _gui_unfocus_control(Control *p_control);
bool _gui_control_has_focus(const Control *p_control);
void _gui_control_grab_focus(Control *p_control);
@@ -406,8 +419,6 @@ private:
void _post_gui_grab_click_focus();
void _gui_accept_event();
- Control *_gui_get_focus_owner();
-
bool _gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check);
friend class AudioListener2D;
@@ -421,6 +432,7 @@ private:
void _canvas_layer_add(CanvasLayer *p_canvas_layer);
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
+ void _drop_mouse_over();
void _drop_mouse_focus();
void _drop_physics_mouseover(bool p_paused_only = false);
@@ -503,11 +515,23 @@ public:
void set_screen_space_aa(ScreenSpaceAA p_screen_space_aa);
ScreenSpaceAA get_screen_space_aa() const;
+ void set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode);
+ Scaling3DMode get_scaling_3d_mode() const;
+
+ void set_scaling_3d_scale(float p_scaling_3d_scale);
+ float get_scaling_3d_scale() const;
+
+ 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_use_debanding(bool p_use_debanding);
bool is_using_debanding() const;
- void set_lod_threshold(float p_pixels);
- float get_lod_threshold() const;
+ void set_mesh_lod_threshold(float p_pixels);
+ float get_mesh_lod_threshold() const;
void set_use_occlusion_culling(bool p_us_occlusion_culling);
bool is_using_occlusion_culling() const;
@@ -523,7 +547,7 @@ public:
bool is_input_disabled() const;
Vector2 get_mouse_position() const;
- void warp_mouse(const Vector2 &p_pos);
+ void warp_mouse(const Vector2 &p_position);
void set_physics_object_picking(bool p_enable);
bool get_physics_object_picking();
@@ -533,6 +557,9 @@ public:
void gui_reset_canvas_sort_index();
int gui_get_canvas_sort_index();
+ void gui_release_focus();
+ Control *gui_get_focus_owner();
+
TypedArray<String> get_configuration_warnings() const override;
void set_debug_draw(DebugDraw p_debug_draw);
@@ -556,6 +583,7 @@ public:
bool is_handling_input_locally() const;
bool gui_is_dragging() const;
+ bool gui_is_drag_successful() const;
Control *gui_find_control(const Point2 &p_global);
@@ -573,8 +601,7 @@ public:
virtual DisplayServer::WindowID get_window_id() const = 0;
- void set_embed_subwindows_hint(bool p_embed);
- bool get_embed_subwindows_hint() const;
+ void set_embedding_subwindows(bool p_embed);
bool is_embedding_subwindows() const;
Viewport *get_parent_viewport() const;
@@ -582,12 +609,13 @@ public:
void pass_mouse_focus_to(Viewport *p_viewport, Control *p_control);
+ virtual Transform2D get_screen_transform() const;
+
#ifndef _3D_DISABLED
bool use_xr = false;
- float scale_3d = 1.0;
friend class AudioListener3D;
AudioListener3D *audio_listener_3d = nullptr;
- Set<AudioListener3D *> audio_listener_3d_set;
+ RBSet<AudioListener3D *> audio_listener_3d_set;
bool is_audio_listener_3d_enabled = false;
RID internal_audio_listener_3d;
AudioListener3D *get_audio_listener_3d() const;
@@ -622,7 +650,7 @@ public:
friend class Camera3D;
Camera3D *camera_3d = nullptr;
- Set<Camera3D *> camera_3d_set;
+ RBSet<Camera3D *> camera_3d_set;
Camera3D *get_camera_3d() const;
void _camera_3d_transform_changed_notify();
void _camera_3d_set(Camera3D *p_camera);
@@ -655,9 +683,6 @@ public:
void set_use_xr(bool p_use_xr);
bool is_using_xr();
-
- void set_scale_3d(float p_scale_3d);
- float get_scale_3d() const;
#endif // _3D_DISABLED
Viewport();
@@ -709,9 +734,12 @@ public:
void set_clear_mode(ClearMode p_mode);
ClearMode get_clear_mode() const;
+ virtual Transform2D get_screen_transform() const override;
+
SubViewport();
~SubViewport();
};
+VARIANT_ENUM_CAST(Viewport::Scaling3DMode);
VARIANT_ENUM_CAST(SubViewport::UpdateMode);
VARIANT_ENUM_CAST(Viewport::ShadowAtlasQuadrantSubdiv);
VARIANT_ENUM_CAST(Viewport::MSAA);
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 5da5a183f7..d8264e7d33 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,7 @@
#include "window.h"
+#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/string/translation.h"
#include "scene/gui/control.h"
@@ -41,7 +42,16 @@ void Window::set_title(const String &p_title) {
if (embedder) {
embedder->_sub_window_update(this);
} else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
- DisplayServer::get_singleton()->window_set_title(atr(p_title), window_id);
+ String tr_title = atr(p_title);
+#ifdef DEBUG_ENABLED
+ if (window_id == DisplayServer::MAIN_WINDOW_ID) {
+ // Append a suffix to the window title to denote that the project is running
+ // from a debug build (including the editor). Since this results in lower performance,
+ // this should be clearly presented to the user.
+ tr_title = vformat("%s (DEBUG)", tr_title);
+ }
+#endif
+ DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
}
}
@@ -88,6 +98,10 @@ Size2i Window::get_size() const {
return size;
}
+void Window::reset_size() {
+ set_size(Size2i());
+}
+
Size2i Window::get_real_size() const {
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
return DisplayServer::get_singleton()->window_get_real_size(window_id);
@@ -230,8 +244,18 @@ void Window::_make_window() {
DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id);
DisplayServer::get_singleton()->window_set_max_size(max_size, window_id);
DisplayServer::get_singleton()->window_set_min_size(min_size, window_id);
- DisplayServer::get_singleton()->window_set_title(atr(title), window_id);
+ String tr_title = atr(title);
+#ifdef DEBUG_ENABLED
+ if (window_id == DisplayServer::MAIN_WINDOW_ID) {
+ // Append a suffix to the window title to denote that the project is running
+ // from a debug build (including the editor). Since this results in lower performance,
+ // this should be clearly presented to the user.
+ tr_title = vformat("%s (DEBUG)", tr_title);
+ }
+#endif
+ DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id);
+ DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive);
_update_window_size();
@@ -239,7 +263,7 @@ void Window::_make_window() {
DisplayServer::get_singleton()->window_set_transient(window_id, transient_parent->window_id);
}
- for (Set<Window *>::Element *E = transient_children.front(); E; E = E->next()) {
+ for (RBSet<Window *>::Element *E = transient_children.front(); E; E = E->next()) {
if (E->get()->window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_transient(E->get()->window_id, transient_parent->window_id);
}
@@ -266,7 +290,7 @@ void Window::_clear_window() {
DisplayServer::get_singleton()->window_set_transient(window_id, DisplayServer::INVALID_WINDOW_ID);
}
- for (Set<Window *>::Element *E = transient_children.front(); E; E = E->next()) {
+ for (RBSet<Window *>::Element *E = transient_children.front(); E; E = E->next()) {
if (E->get()->window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_transient(E->get()->window_id, DisplayServer::INVALID_WINDOW_ID);
}
@@ -277,6 +301,11 @@ void Window::_clear_window() {
DisplayServer::get_singleton()->delete_sub_window(window_id);
window_id = DisplayServer::INVALID_WINDOW_ID;
+ // If closing window was focused and has a parent, return focus.
+ if (focused && transient_parent) {
+ transient_parent->grab_focus();
+ }
+
_update_viewport_size();
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
}
@@ -311,9 +340,11 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
case DisplayServer::WINDOW_EVENT_MOUSE_ENTER: {
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
emit_signal(SNAME("mouse_entered"));
+ notification(NOTIFICATION_VP_MOUSE_ENTER);
DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape
} break;
case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: {
+ notification(NOTIFICATION_VP_MOUSE_EXIT);
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
emit_signal(SNAME("mouse_exited"));
} break;
@@ -405,8 +436,12 @@ void Window::set_visible(bool p_visible) {
//update transient exclusive
if (transient_parent) {
if (exclusive && visible) {
- ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child.");
- transient_parent->exclusive_child = this;
+#ifdef TOOLS_ENABLED
+ if (!(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this))) {
+ ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child.");
+ transient_parent->exclusive_child = this;
+ }
+#endif
} else {
if (transient_parent->exclusive_child == this) {
transient_parent->exclusive_child = nullptr;
@@ -495,6 +530,10 @@ void Window::set_exclusive(bool p_exclusive) {
exclusive = p_exclusive;
+ if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) {
+ DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive);
+ }
+
if (transient_parent) {
if (p_exclusive && is_inside_tree() && is_visible()) {
ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child.");
@@ -556,9 +595,12 @@ void Window::_update_viewport_size() {
float font_oversampling = 1.0;
if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) {
- stretch_transform = Transform2D();
+ font_oversampling = content_scale_factor;
final_size = size;
+ final_size_override = Size2(size) / content_scale_factor;
+ stretch_transform = Transform2D();
+ stretch_transform.scale(Size2(content_scale_factor, content_scale_factor));
} else {
//actual screen video mode
Size2 video_mode = size;
@@ -630,9 +672,9 @@ void Window::_update_viewport_size() {
} break;
case CONTENT_SCALE_MODE_CANVAS_ITEMS: {
final_size = screen_size;
- final_size_override = viewport_size;
+ final_size_override = viewport_size / content_scale_factor;
attach_to_screen_rect = Rect2(margin, screen_size);
- font_oversampling = screen_size.x / viewport_size.x;
+ font_oversampling = (screen_size.x / viewport_size.x) * content_scale_factor;
Size2 scale = Vector2(screen_size) / Vector2(final_size_override);
stretch_transform.scale(scale);
@@ -702,18 +744,16 @@ void Window::_notification(int p_what) {
bool embedded = false;
{
embedder = _get_embedder();
-
if (embedder) {
embedded = true;
-
if (!visible) {
- embedder = nullptr; //not yet since not visible
+ embedder = nullptr; // Not yet since not visible.
}
}
}
if (embedded) {
- //create as embedded
+ // Create as embedded.
if (embedder) {
embedder->_sub_window_register(this);
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE);
@@ -721,22 +761,22 @@ void Window::_notification(int p_what) {
}
} else {
- if (get_parent() == nullptr) {
- //it's the root window!
- visible = true; //always visible
+ if (!get_parent()) {
+ // It's the root window!
+ visible = true; // Always visible.
window_id = DisplayServer::MAIN_WINDOW_ID;
DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id);
_update_from_window();
- //since this window already exists (created on start), we must update pos and size from it
+ // Since this window already exists (created on start), we must update pos and size from it.
{
position = DisplayServer::get_singleton()->window_get_position(window_id);
size = DisplayServer::get_singleton()->window_get_size(window_id);
}
- _update_viewport_size(); //then feed back to the viewport
+ _update_viewport_size(); // Then feed back to the viewport.
_update_window_callbacks();
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_VISIBLE);
} else {
- //create
+ // Create.
if (visible) {
_make_window();
}
@@ -752,20 +792,32 @@ void Window::_notification(int p_what) {
RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
}
} break;
+
case NOTIFICATION_READY: {
if (wrap_controls) {
_update_child_controls();
}
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED: {
if (embedder) {
embedder->_sub_window_update(this);
} else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
- DisplayServer::get_singleton()->window_set_title(atr(title), window_id);
+ String tr_title = atr(title);
+#ifdef DEBUG_ENABLED
+ if (window_id == DisplayServer::MAIN_WINDOW_ID) {
+ // Append a suffix to the window title to denote that the project is running
+ // from a debug build (including the editor). Since this results in lower performance,
+ // this should be clearly presented to the user.
+ tr_title = vformat("%s (DEBUG)", tr_title);
+ }
+#endif
+ DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
}
child_controls_changed();
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (transient) {
_clear_transient();
@@ -821,6 +873,16 @@ Window::ContentScaleAspect Window::get_content_scale_aspect() const {
return content_scale_aspect;
}
+void Window::set_content_scale_factor(real_t p_factor) {
+ ERR_FAIL_COND(p_factor <= 0);
+ content_scale_factor = p_factor;
+ _update_viewport_size();
+}
+
+real_t Window::get_content_scale_factor() const {
+ return content_scale_factor;
+}
+
void Window::set_use_font_oversampling(bool p_oversampling) {
if (is_inside_tree() && window_id != DisplayServer::MAIN_WINDOW_ID) {
ERR_FAIL_MSG("Only the root window can set and use font oversampling.");
@@ -893,7 +955,7 @@ bool Window::_can_consume_input_events() const {
void Window::_window_input(const Ref<InputEvent> &p_ev) {
if (EngineDebugger::is_active()) {
- //quit from game window using F8
+ // Quit from game window using F8.
Ref<InputEventKey> k = p_ev;
if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::F8) {
EngineDebugger::get_singleton()->send_message("request_quit", Array());
@@ -901,15 +963,7 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) {
}
if (exclusive_child != nullptr) {
- /*
- Window *focus_target = exclusive_child;
- focus_target->grab_focus();
- while (focus_target->exclusive_child != nullptr) {
- focus_target = focus_target->exclusive_child;
- focus_target->grab_focus();
- }*/
-
- if (!is_embedding_subwindows()) { //not embedding, no need for event
+ if (!is_embedding_subwindows()) { // Not embedding, no need for event.
return;
}
}
@@ -927,7 +981,7 @@ void Window::_window_input_text(const String &p_text) {
}
void Window::_window_drop_files(const Vector<String> &p_files) {
- emit_signal(SNAME("files_dropped"), p_files, current_screen);
+ emit_signal(SNAME("files_dropped"), p_files);
}
Viewport *Window::get_parent_viewport() const {
@@ -991,7 +1045,9 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio
Rect2i popup_rect;
popup_rect.size = Vector2i(MIN(size_ratio.x, p_size.x), MIN(size_ratio.y, p_size.y));
- popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
+ if (parent_rect != Rect2()) {
+ popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
+ }
popup(popup_rect);
}
@@ -1012,12 +1068,13 @@ void Window::popup_centered(const Size2i &p_minsize) {
}
Rect2i popup_rect;
- if (p_minsize == Size2i()) {
- popup_rect.size = _get_contents_minimum_size();
- } else {
- popup_rect.size = p_minsize;
+ Size2 contents_minsize = _get_contents_minimum_size();
+ popup_rect.size.x = MAX(p_minsize.x, contents_minsize.x);
+ popup_rect.size.y = MAX(p_minsize.y, contents_minsize.y);
+
+ if (parent_rect != Rect2()) {
+ popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
}
- popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
popup(popup_rect);
}
@@ -1025,6 +1082,7 @@ void Window::popup_centered(const Size2i &p_minsize) {
void Window::popup_centered_ratio(float p_ratio) {
ERR_FAIL_COND(!is_inside_tree());
ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window.");
+ ERR_FAIL_COND_MSG(p_ratio <= 0.0 || p_ratio > 1.0, "Ratio must be between 0.0 and 1.0!");
Rect2 parent_rect;
@@ -1038,8 +1096,10 @@ void Window::popup_centered_ratio(float p_ratio) {
}
Rect2i popup_rect;
- popup_rect.size = parent_rect.size * p_ratio;
- popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
+ if (parent_rect != Rect2()) {
+ popup_rect.size = parent_rect.size * p_ratio;
+ popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
+ }
popup(popup_rect);
}
@@ -1047,6 +1107,14 @@ void Window::popup_centered_ratio(float p_ratio) {
void Window::popup(const Rect2i &p_screen_rect) {
emit_signal(SNAME("about_to_popup"));
+ if (!_get_embedder() && get_flag(FLAG_POPUP)) {
+ // Send a focus-out notification when opening a Window Manager Popup.
+ SceneTree *scene_tree = get_tree();
+ if (scene_tree) {
+ scene_tree->notify_group("_viewports", NOTIFICATION_WM_WINDOW_FOCUS_OUT);
+ }
+ }
+
// Update window size to calculate the actual window size based on contents minimum size and minimum size.
_update_window_size();
@@ -1398,6 +1466,15 @@ 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 = _get_embedder()->get_screen_transform() * embedder_transform;
+ }
+ return embedder_transform * Viewport::get_screen_transform();
+}
+
void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title);
@@ -1410,6 +1487,7 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &Window::set_size);
ClassDB::bind_method(D_METHOD("get_size"), &Window::get_size);
+ ClassDB::bind_method(D_METHOD("reset_size"), &Window::reset_size);
ClassDB::bind_method(D_METHOD("get_real_size"), &Window::get_real_size);
@@ -1463,6 +1541,9 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_content_scale_aspect", "aspect"), &Window::set_content_scale_aspect);
ClassDB::bind_method(D_METHOD("get_content_scale_aspect"), &Window::get_content_scale_aspect);
+ ClassDB::bind_method(D_METHOD("set_content_scale_factor", "factor"), &Window::set_content_scale_factor);
+ ClassDB::bind_method(D_METHOD("get_content_scale_factor"), &Window::get_content_scale_factor);
+
ClassDB::bind_method(D_METHOD("set_use_font_oversampling", "enable"), &Window::set_use_font_oversampling);
ClassDB::bind_method(D_METHOD("is_using_font_oversampling"), &Window::is_using_font_oversampling);
@@ -1525,6 +1606,7 @@ void Window::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "always_on_top"), "set_flag", "get_flag", FLAG_ALWAYS_ON_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_flag", "get_flag", FLAG_TRANSPARENT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unfocusable"), "set_flag", "get_flag", FLAG_NO_FOCUS);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "popup_window"), "set_flag", "get_flag", FLAG_POPUP);
ADD_GROUP("Limits", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size"), "set_min_size", "get_min_size");
@@ -1534,6 +1616,7 @@ void Window::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Canvas Items,Viewport"), "set_content_scale_mode", "get_content_scale_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,Keep Width,Keep Height,Expand"), "set_content_scale_aspect", "get_content_scale_aspect");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor"), "set_content_scale_factor", "get_content_scale_factor");
ADD_GROUP("Theme", "theme_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
@@ -1552,6 +1635,7 @@ void Window::_bind_methods() {
ADD_SIGNAL(MethodInfo("go_back_requested"));
ADD_SIGNAL(MethodInfo("visibility_changed"));
ADD_SIGNAL(MethodInfo("about_to_popup"));
+ ADD_SIGNAL(MethodInfo("theme_changed"));
BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
@@ -1559,12 +1643,14 @@ void Window::_bind_methods() {
BIND_ENUM_CONSTANT(MODE_MINIMIZED);
BIND_ENUM_CONSTANT(MODE_MAXIMIZED);
BIND_ENUM_CONSTANT(MODE_FULLSCREEN);
+ BIND_ENUM_CONSTANT(MODE_EXCLUSIVE_FULLSCREEN);
BIND_ENUM_CONSTANT(FLAG_RESIZE_DISABLED);
BIND_ENUM_CONSTANT(FLAG_BORDERLESS);
BIND_ENUM_CONSTANT(FLAG_ALWAYS_ON_TOP);
BIND_ENUM_CONSTANT(FLAG_TRANSPARENT);
BIND_ENUM_CONSTANT(FLAG_NO_FOCUS);
+ BIND_ENUM_CONSTANT(FLAG_POPUP);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_DISABLED);
diff --git a/scene/main/window.h b/scene/main/window.h
index def6eab7b8..80dd9a854c 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,6 +46,7 @@ public:
MODE_MINIMIZED = DisplayServer::WINDOW_MODE_MINIMIZED,
MODE_MAXIMIZED = DisplayServer::WINDOW_MODE_MAXIMIZED,
MODE_FULLSCREEN = DisplayServer::WINDOW_MODE_FULLSCREEN,
+ MODE_EXCLUSIVE_FULLSCREEN = DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN,
};
enum Flags {
@@ -54,6 +55,7 @@ public:
FLAG_ALWAYS_ON_TOP = DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP,
FLAG_TRANSPARENT = DisplayServer::WINDOW_FLAG_TRANSPARENT,
FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS,
+ FLAG_POPUP = DisplayServer::WINDOW_FLAG_POPUP,
FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX,
};
@@ -112,6 +114,7 @@ private:
Size2i content_scale_size;
ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED;
ContentScaleAspect content_scale_aspect = CONTENT_SCALE_ASPECT_IGNORE;
+ real_t content_scale_factor = 1.0;
void _make_window();
void _clear_window();
@@ -128,7 +131,7 @@ private:
void _make_transient();
Window *transient_parent = nullptr;
Window *exclusive_child = nullptr;
- Set<Window *> transient_children;
+ RBSet<Window *> transient_children;
friend class Control;
Ref<Theme> theme;
@@ -178,6 +181,7 @@ public:
void set_size(const Size2i &p_size);
Size2i get_size() const;
+ void reset_size();
Size2i get_real_size() const;
@@ -229,6 +233,9 @@ public:
void set_content_scale_aspect(ContentScaleAspect p_aspect);
ContentScaleAspect get_content_scale_aspect() const;
+ void set_content_scale_factor(real_t p_factor);
+ real_t get_content_scale_factor() const;
+
void set_use_font_oversampling(bool p_oversampling);
bool is_using_font_oversampling() const;
@@ -284,6 +291,8 @@ public:
Ref<Font> get_theme_default_font() const;
int get_theme_default_font_size() const;
+ virtual Transform2D get_screen_transform() const override;
+
Rect2i get_parent_rect() const;
virtual DisplayServer::WindowID get_window_id() const override;
diff --git a/scene/multiplayer/SCsub b/scene/multiplayer/SCsub
new file mode 100644
index 0000000000..fc61250247
--- /dev/null
+++ b/scene/multiplayer/SCsub
@@ -0,0 +1,5 @@
+#!/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
new file mode 100644
index 0000000000..a9b9ffa989
--- /dev/null
+++ b/scene/multiplayer/multiplayer_spawner.cpp
@@ -0,0 +1,231 @@
+/*************************************************************************/
+/* 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"
+
+void MultiplayerSpawner::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("spawn", "data"), &MultiplayerSpawner::spawn, DEFVAL(Variant()));
+
+ 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::ARRAY, "replication", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_spawnable_scenes", "get_spawnable_scenes");
+
+ 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");
+
+ ClassDB::bind_method(D_METHOD("set_auto_spawning", "enabled"), &MultiplayerSpawner::set_auto_spawning);
+ ClassDB::bind_method(D_METHOD("is_auto_spawning"), &MultiplayerSpawner::is_auto_spawning);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_spawn"), "set_auto_spawning", "is_auto_spawning");
+
+ 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 (auto_spawn) {
+ 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 = get_scene_id(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);
+}
+
+void MultiplayerSpawner::set_auto_spawning(bool p_enabled) {
+ auto_spawn = p_enabled;
+ _update_spawn_node();
+}
+
+bool MultiplayerSpawner::is_auto_spawning() const {
+ return auto_spawn;
+}
+
+TypedArray<PackedScene> MultiplayerSpawner::get_spawnable_scenes() {
+ return spawnable_scenes;
+}
+
+void MultiplayerSpawner::set_spawnable_scenes(TypedArray<PackedScene> p_scenes) {
+ spawnable_scenes = p_scenes;
+}
+
+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::get_scene_id(const String &p_scene) const {
+ for (int i = 0; i < spawnable_scenes.size(); i++) {
+ Ref<PackedScene> ps = spawnable_scenes[i];
+ ERR_CONTINUE(ps.is_null());
+ if (ps->get_path() == p_scene) {
+ return i;
+ }
+ }
+ return INVALID_ID;
+}
+
+int MultiplayerSpawner::get_spawn_id(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_INDEX_V(p_id, spawnable_scenes.size(), nullptr);
+ Ref<PackedScene> scene = spawnable_scenes[p_id];
+ 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
new file mode 100644
index 0000000000..ac35df7ff3
--- /dev/null
+++ b/scene/multiplayer/multiplayer_spawner.h
@@ -0,0 +1,101 @@
+/*************************************************************************/
+/* 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/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:
+ TypedArray<PackedScene> spawnable_scenes;
+ RBSet<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;
+ bool auto_spawn = false;
+ 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);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ Node *get_spawn_node() const { return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; }
+ TypedArray<PackedScene> get_spawnable_scenes();
+ void set_spawnable_scenes(TypedArray<PackedScene> p_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; }
+ bool is_auto_spawning() const;
+ void set_auto_spawning(bool p_enabled);
+
+ const Variant get_spawn_argument(const ObjectID &p_id) const;
+ int get_spawn_id(const ObjectID &p_id) const;
+ int get_scene_id(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
new file mode 100644
index 0000000000..33e845a7a3
--- /dev/null
+++ b/scene/multiplayer/multiplayer_synchronizer.cpp
@@ -0,0 +1,163 @@
+/*************************************************************************/
+/* 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() {
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node) {
+ get_multiplayer()->replication_stop(node, this);
+ }
+}
+
+void MultiplayerSynchronizer::_start() {
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node) {
+ get_multiplayer()->replication_start(node, this);
+ }
+}
+
+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;
+}
+
+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);
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "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);
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001"), "set_replication_interval", "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);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig"), "set_replication_config", "get_replication_config");
+}
+
+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;
+ }
+}
+
+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::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);
+}
diff --git a/scene/multiplayer/multiplayer_synchronizer.h b/scene/multiplayer/multiplayer_synchronizer.h
new file mode 100644
index 0000000000..e856745379
--- /dev/null
+++ b/scene/multiplayer/multiplayer_synchronizer.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* 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);
+
+private:
+ Ref<SceneReplicationConfig> replication_config;
+ NodePath root_path;
+ uint64_t interval_msec = 0;
+
+ static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
+ void _start();
+ void _stop();
+
+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;
+
+ MultiplayerSynchronizer() {}
+};
+
+#endif // MULTIPLAYER_SYNCHRONIZER_H
diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp
new file mode 100644
index 0000000000..a7e84b6bca
--- /dev/null
+++ b/scene/multiplayer/scene_cache_interface.cpp
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* 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;
+}
+
+bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node, false);
+ // See if the path is cached.
+ PathSentCache *psc = path_send_cache.getptr(p_path);
+ if (!psc) {
+ // Path is not cached, create.
+ path_send_cache[p_path] = PathSentCache();
+ psc = path_send_cache.getptr(p_path);
+ psc->id = last_send_cache_id++;
+ }
+ r_id = psc->id;
+
+ 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 RBSet<int>::Element *E = multiplayer->get_connected_peers().front(); E; E = E->next()) {
+ if (p_peer_id < 0 && E->get() == -p_peer_id) {
+ continue; // Continue, excluded.
+ }
+ if (p_peer_id > 0 && E->get() != p_peer_id) {
+ continue; // Continue, not for this peer.
+ }
+
+ HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(E->get());
+ if (!F) {
+ peers_to_add.push_back(E->get()); // 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, p_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
new file mode 100644
index 0000000000..3116233b5b
--- /dev/null
+++ b/scene/multiplayer/scene_cache_interface.h
@@ -0,0 +1,82 @@
+/*************************************************************************/
+/* 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, NodePath p_path, int p_target, int &p_id) 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
new file mode 100644
index 0000000000..55266c53ad
--- /dev/null
+++ b/scene/multiplayer/scene_replication_interface.cpp
@@ -0,0 +1,419 @@
+/*************************************************************************/
+/* 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()) {
+ _send_spawn(rep_state->get_node(oid), rep_state->get_spawner(oid), p_id);
+ }
+ for (const ObjectID &oid : rep_state->get_path_only_nodes()) {
+ Node *node = rep_state->get_node(oid);
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
+ ERR_CONTINUE(!node || !sync);
+ if (sync->is_multiplayer_authority()) {
+ rep_state->peer_add_node(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);
+ return _send_spawn(node, spawner, 0);
+}
+
+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);
+ Error err = rep_state->config_del_spawn(node, spawner);
+ ERR_FAIL_COND_V(err != OK, err);
+ return _send_despawn(node, 0);
+}
+
+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);
+ rep_state->config_add_sync(node, sync);
+ // 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);
+ } else if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
+ // Either it's a spawn or a static sync, in any case add it to the list of known nodes.
+ rep_state->peer_add_node(0, p_obj->get_instance_id());
+ }
+ 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(!p_obj || !sync, ERR_INVALID_PARAMETER);
+ return rep_state->config_del_sync(node, sync);
+}
+
+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::_send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer) {
+ ERR_FAIL_COND_V(p_peer < 0, ERR_BUG);
+ ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
+ ERR_FAIL_COND_V(!p_spawner || !p_node, ERR_BUG);
+
+ const ObjectID oid = p_node->get_instance_id();
+ uint32_t nid = rep_state->ensure_net_id(oid);
+
+ // Prepare custom arg and scene_id
+ uint8_t scene_id = p_spawner->get_spawn_id(oid);
+ bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
+ Variant spawn_arg = p_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 && synchronizer->get_replication_config().is_valid()) {
+ 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.");
+ }
+
+ // Prepare simplified path.
+ NodePath rel_path = multiplayer->get_root_path().rel_path_to(p_spawner->get_path());
+
+ int path_id = 0;
+ multiplayer->send_object_cache(p_spawner, rel_path, p_peer, path_id);
+
+ // Encode name and parent ID.
+ 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;
+ }
+ Error err = _send_raw(ptr, ofs, p_peer, true);
+ ERR_FAIL_COND_V(err, err);
+ return rep_state->peer_add_node(p_peer, oid);
+}
+
+Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) {
+ 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]);
+ Error err = _send_raw(ptr, ofs, p_peer, true);
+ ERR_FAIL_COND_V(err, err);
+ return rep_state->peer_del_node(p_peer, oid);
+}
+
+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 RBSet<ObjectID> &known = rep_state->get_known_nodes(p_peer);
+ if (known.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 : known) {
+ if (!rep_state->update_sync_time(oid, p_msec)) {
+ continue; // nothing to sync.
+ }
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
+ ERR_CONTINUE(!sync);
+ Node *node = rep_state->get_node(oid);
+ ERR_CONTINUE(!node);
+ 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) {
+ uint32_t net_id = rep_state->get_net_id(oid);
+ if (net_id == 0 || (net_id & 0x80000000)) {
+ // First time path based ID.
+ NodePath rel_path = multiplayer->get_root_path().rel_path_to(sync->get_path());
+ int path_id = 0;
+ multiplayer->send_object_cache(sync, rel_path, p_peer, path_id);
+ ERR_CONTINUE_MSG(net_id && net_id != (uint32_t(path_id) | 0x80000000), "This should never happen!");
+ net_id = path_id;
+ rep_state->set_net_id(oid, net_id | 0x80000000);
+ }
+ 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
new file mode 100644
index 0000000000..60ac95c93c
--- /dev/null
+++ b/scene/multiplayer/scene_replication_interface.h
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* 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 _send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer);
+ Error _send_despawn(Node *p_node, int p_peer);
+ Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable);
+
+ 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
new file mode 100644
index 0000000000..e5cc57ff31
--- /dev/null
+++ b/scene/multiplayer/scene_replication_state.cpp
@@ -0,0 +1,254 @@
+/*************************************************************************/
+/* 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.known_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;
+}
+
+const RBSet<ObjectID> SceneReplicationState::get_known_nodes(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), RBSet<ObjectID>());
+ return peers_info[p_peer].known_nodes;
+}
+
+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);
+ // The spawner may be notified after the synchronizer.
+ path_only_nodes.erase(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);
+ 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();
+ // If it doesn't have a spawner, we might need to assign ID for this node using it's path.
+ if (tobj.spawner.is_null()) {
+ path_only_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();
+ if (path_only_nodes.has(oid)) {
+ p_node->disconnect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack));
+ _untrack(oid);
+ path_only_nodes.erase(oid);
+ }
+ return OK;
+}
+
+Error SceneReplicationState::peer_add_node(int p_peer, const ObjectID &p_id) {
+ if (p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].known_nodes.insert(p_id);
+ } else {
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.known_nodes.insert(p_id);
+ }
+ }
+ return OK;
+}
+
+Error SceneReplicationState::peer_del_node(int p_peer, const ObjectID &p_id) {
+ if (p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].known_nodes.erase(p_id);
+ } else {
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.known_nodes.erase(p_id);
+ }
+ }
+ return OK;
+}
+
+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
new file mode 100644
index 0000000000..33f72363ac
--- /dev/null
+++ b/scene/multiplayer/scene_replication_state.h
@@ -0,0 +1,121 @@
+/*************************************************************************/
+/* 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 {
+ RBSet<ObjectID> known_nodes;
+ HashMap<uint32_t, ObjectID> recv_nodes;
+ uint16_t last_sent_sync = 0;
+ uint16_t last_recv_sync = 0;
+ };
+
+ RBSet<int> known_peers;
+ uint32_t last_net_id = 0;
+ HashMap<ObjectID, TrackedNode> tracked_nodes;
+ HashMap<int, PeerInfo> peers_info;
+ RBSet<ObjectID> spawned_nodes;
+ RBSet<ObjectID> path_only_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 RBSet<int> get_peers() const { return known_peers; }
+ const RBSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; }
+ const RBSet<ObjectID> &get_path_only_nodes() const { return path_only_nodes; }
+
+ 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);
+
+ const RBSet<ObjectID> get_known_nodes(int p_peer);
+ 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_node(int p_peer, const ObjectID &p_id);
+ Error peer_del_node(int p_peer, const ObjectID &p_id);
+
+ 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
new file mode 100644
index 0000000000..84700a82f3
--- /dev/null
+++ b/scene/multiplayer/scene_rpc_interface.cpp
@@ -0,0 +1,511 @@
+/*************************************************************************/
+/* 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) + ".");
+ }
+
+ NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
+ ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
+
+ // 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, from_path, 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.
+ 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
new file mode 100644
index 0000000000..86e1d0d280
--- /dev/null
+++ b/scene/multiplayer/scene_rpc_interface.h
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* 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/property_utils.cpp b/scene/property_utils.cpp
index 7df601492b..a9b7e9acbe 100644
--- a/scene/property_utils.cpp
+++ b/scene/property_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -50,7 +50,7 @@ bool PropertyUtils::is_property_value_different(const Variant &p_a, const Varian
}
}
-Variant PropertyUtils::get_property_default_value(const Object *p_object, const StringName &p_property, const Vector<SceneState::PackState> *p_states_stack_cache, bool p_update_exports, const Node *p_owner, bool *r_is_class_default) {
+Variant PropertyUtils::get_property_default_value(const Object *p_object, const StringName &p_property, bool *r_is_valid, const Vector<SceneState::PackState> *p_states_stack_cache, bool p_update_exports, const Node *p_owner, bool *r_is_class_default) {
// This function obeys the way property values are set when an object is instantiated,
// which is the following (the latter wins):
// 1. Default value from builtin class
@@ -60,6 +60,9 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const
if (r_is_class_default) {
*r_is_class_default = false;
}
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
Ref<Script> topmost_script;
@@ -71,6 +74,9 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const
bool found = false;
Variant value_in_ancestor = ia.state->get_property_value(ia.node, p_property, found);
if (found) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
return value_in_ancestor;
}
// Save script for later
@@ -97,15 +103,45 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const
}
Variant default_value;
if (topmost_script->get_property_default_value(p_property, default_value)) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
return default_value;
}
}
// Fall back to the default from the native class
- if (r_is_class_default) {
- *r_is_class_default = true;
+ {
+ if (r_is_class_default) {
+ *r_is_class_default = true;
+ }
+ bool valid = false;
+ Variant value = ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property, &valid);
+ if (valid) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return value;
+ } else {
+ // Heuristically check if this is a synthetic property (whatever/0, whatever/1, etc.)
+ // because they are not in the class DB yet must have a default (null).
+ String prop_str = String(p_property);
+ int p = prop_str.rfind("/");
+ if (p != -1 && p < prop_str.length() - 1) {
+ bool all_digits = true;
+ for (int i = p + 1; i < prop_str.length(); i++) {
+ if (!is_digit(prop_str[i])) {
+ all_digits = false;
+ break;
+ }
+ }
+ if (r_is_valid) {
+ *r_is_valid = all_digits;
+ }
+ }
+ return Variant();
+ }
}
- return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property);
}
// Like SceneState::PackState, but using a raw pointer to avoid the cost of
@@ -164,7 +200,7 @@ Vector<SceneState::PackState> PropertyUtils::get_node_states_stack(const Node *p
}
}
break;
- } else if (n->get_scene_file_path() != String()) {
+ } else if (!n->get_scene_file_path().is_empty()) {
const Ref<SceneState> &state = n->get_scene_instance_state();
_collect_inheritance_chain(state, n->get_path_to(p_node), states_stack);
}
diff --git a/scene/property_utils.h b/scene/property_utils.h
index fde9163548..5ada35fdd7 100644
--- a/scene/property_utils.h
+++ b/scene/property_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,7 +38,7 @@ class PropertyUtils {
public:
static bool is_property_value_different(const Variant &p_a, const Variant &p_b);
// Gets the most pure default value, the one that would be set when the node has just been instantiated
- static Variant get_property_default_value(const Object *p_object, const StringName &p_property, const Vector<SceneState::PackState> *p_states_stack_cache = nullptr, bool p_update_exports = false, const Node *p_owner = nullptr, bool *r_is_class_default = nullptr);
+ static Variant get_property_default_value(const Object *p_object, const StringName &p_property, bool *r_is_valid = nullptr, const Vector<SceneState::PackState> *p_states_stack_cache = nullptr, bool p_update_exports = false, const Node *p_owner = nullptr, bool *r_is_class_default = nullptr);
// Gets the instance/inheritance states of this node, in order of precedence,
// that is, from the topmost (the most able to override values) to the lowermost
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 056ace5e4e..6bce957d80 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -91,6 +91,7 @@
#include "scene/gui/control.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/file_dialog.h"
+#include "scene/gui/flow_container.h"
#include "scene/gui/graph_edit.h"
#include "scene/gui/graph_node.h"
#include "scene/gui/grid_container.h"
@@ -123,16 +124,23 @@
#include "scene/gui/texture_progress_bar.h"
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
-#include "scene/gui/video_player.h"
+#include "scene/gui/video_stream_player.h"
#include "scene/main/canvas_item.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/http_request.h"
#include "scene/main/instance_placeholder.h"
+#include "scene/main/missing_node.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/bit_map.h"
#include "scene/resources/box_shape_3d.h"
@@ -219,6 +227,7 @@
#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/joint_3d.h"
+#include "scene/3d/label_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/3d/lightmap_probe.h"
@@ -232,7 +241,6 @@
#include "scene/3d/path_3d.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/3d/position_3d.h"
-#include "scene/3d/proximity_group_3d.h"
#include "scene/3d/ray_cast_3d.h"
#include "scene/3d/reflection_probe.h"
#include "scene/3d/remote_transform_3d.h"
@@ -255,9 +263,9 @@
static Ref<ResourceFormatSaverText> resource_saver_text;
static Ref<ResourceFormatLoaderText> resource_loader_text;
-static Ref<ResourceFormatLoaderStreamTexture2D> resource_loader_stream_texture;
-static Ref<ResourceFormatLoaderStreamTextureLayered> resource_loader_texture_layered;
-static Ref<ResourceFormatLoaderStreamTexture3D> resource_loader_texture_3d;
+static Ref<ResourceFormatLoaderCompressedTexture2D> resource_loader_stream_texture;
+static Ref<ResourceFormatLoaderCompressedTextureLayered> resource_loader_texture_layered;
+static Ref<ResourceFormatLoaderCompressedTexture3D> resource_loader_texture_3d;
static Ref<ResourceFormatSaverShader> resource_saver_shader;
static Ref<ResourceFormatLoaderShader> resource_loader_shader;
@@ -295,12 +303,15 @@ void register_scene_types() {
GDREGISTER_CLASS(Object);
GDREGISTER_CLASS(Node);
- GDREGISTER_VIRTUAL_CLASS(InstancePlaceholder);
+ GDREGISTER_VIRTUAL_CLASS(MissingNode);
+ GDREGISTER_ABSTRACT_CLASS(InstancePlaceholder);
- GDREGISTER_VIRTUAL_CLASS(Viewport);
+ GDREGISTER_ABSTRACT_CLASS(Viewport);
GDREGISTER_CLASS(SubViewport);
GDREGISTER_CLASS(ViewportTexture);
GDREGISTER_CLASS(HTTPRequest);
+ GDREGISTER_CLASS(MultiplayerSpawner);
+ GDREGISTER_CLASS(MultiplayerSynchronizer);
GDREGISTER_CLASS(Timer);
GDREGISTER_CLASS(CanvasLayer);
GDREGISTER_CLASS(CanvasModulate);
@@ -317,11 +328,11 @@ void register_scene_types() {
GDREGISTER_CLASS(Control);
GDREGISTER_CLASS(Button);
GDREGISTER_CLASS(Label);
- GDREGISTER_VIRTUAL_CLASS(ScrollBar);
+ GDREGISTER_ABSTRACT_CLASS(ScrollBar);
GDREGISTER_CLASS(HScrollBar);
GDREGISTER_CLASS(VScrollBar);
GDREGISTER_CLASS(ProgressBar);
- GDREGISTER_VIRTUAL_CLASS(Slider);
+ GDREGISTER_ABSTRACT_CLASS(Slider);
GDREGISTER_CLASS(HSlider);
GDREGISTER_CLASS(VSlider);
GDREGISTER_CLASS(Popup);
@@ -342,18 +353,21 @@ void register_scene_types() {
GDREGISTER_CLASS(AspectRatioContainer);
GDREGISTER_CLASS(TabContainer);
GDREGISTER_CLASS(TabBar);
- GDREGISTER_VIRTUAL_CLASS(Separator);
+ GDREGISTER_ABSTRACT_CLASS(Separator);
GDREGISTER_CLASS(HSeparator);
GDREGISTER_CLASS(VSeparator);
GDREGISTER_CLASS(TextureButton);
GDREGISTER_CLASS(Container);
- GDREGISTER_VIRTUAL_CLASS(BoxContainer);
+ GDREGISTER_ABSTRACT_CLASS(BoxContainer);
GDREGISTER_CLASS(HBoxContainer);
GDREGISTER_CLASS(VBoxContainer);
GDREGISTER_CLASS(GridContainer);
GDREGISTER_CLASS(CenterContainer);
GDREGISTER_CLASS(ScrollContainer);
GDREGISTER_CLASS(PanelContainer);
+ GDREGISTER_ABSTRACT_CLASS(FlowContainer);
+ GDREGISTER_CLASS(HFlowContainer);
+ GDREGISTER_CLASS(VFlowContainer);
OS::get_singleton()->yield(); // may take time to init
@@ -361,7 +375,7 @@ void register_scene_types() {
GDREGISTER_CLASS(ItemList);
GDREGISTER_CLASS(LineEdit);
- GDREGISTER_CLASS(VideoPlayer);
+ GDREGISTER_CLASS(VideoStreamPlayer);
#ifndef ADVANCED_GUI_DISABLED
GDREGISTER_CLASS(FileDialog);
@@ -374,7 +388,7 @@ void register_scene_types() {
GDREGISTER_CLASS(SyntaxHighlighter);
GDREGISTER_CLASS(CodeHighlighter);
- GDREGISTER_VIRTUAL_CLASS(TreeItem);
+ GDREGISTER_ABSTRACT_CLASS(TreeItem);
GDREGISTER_CLASS(OptionButton);
GDREGISTER_CLASS(SpinBox);
GDREGISTER_CLASS(ColorPicker);
@@ -388,9 +402,10 @@ void register_scene_types() {
GDREGISTER_CLASS(MarginContainer);
GDREGISTER_CLASS(SubViewportContainer);
- GDREGISTER_VIRTUAL_CLASS(SplitContainer);
+ GDREGISTER_ABSTRACT_CLASS(SplitContainer);
GDREGISTER_CLASS(HSplitContainer);
GDREGISTER_CLASS(VSplitContainer);
+
GDREGISTER_CLASS(GraphNode);
GDREGISTER_CLASS(GraphEdit);
@@ -407,7 +422,7 @@ void register_scene_types() {
GDREGISTER_CLASS(AnimationPlayer);
GDREGISTER_CLASS(Tween);
- GDREGISTER_VIRTUAL_CLASS(Tweener);
+ GDREGISTER_ABSTRACT_CLASS(Tweener);
GDREGISTER_CLASS(PropertyTweener);
GDREGISTER_CLASS(IntervalTweener);
GDREGISTER_CLASS(CallbackTweener);
@@ -442,9 +457,9 @@ void register_scene_types() {
#ifndef _3D_DISABLED
GDREGISTER_CLASS(Node3D);
- GDREGISTER_VIRTUAL_CLASS(Node3DGizmo);
+ GDREGISTER_ABSTRACT_CLASS(Node3DGizmo);
GDREGISTER_CLASS(Skin);
- GDREGISTER_VIRTUAL_CLASS(SkinReference);
+ GDREGISTER_ABSTRACT_CLASS(SkinReference);
GDREGISTER_CLASS(Skeleton3D);
GDREGISTER_CLASS(ImporterMesh);
GDREGISTER_CLASS(ImporterMeshInstance3D);
@@ -453,17 +468,23 @@ void register_scene_types() {
GDREGISTER_CLASS(Camera3D);
GDREGISTER_CLASS(AudioListener3D);
GDREGISTER_CLASS(XRCamera3D);
- GDREGISTER_VIRTUAL_CLASS(XRNode3D);
+ GDREGISTER_ABSTRACT_CLASS(XRNode3D);
GDREGISTER_CLASS(XRController3D);
GDREGISTER_CLASS(XRAnchor3D);
GDREGISTER_CLASS(XROrigin3D);
GDREGISTER_CLASS(MeshInstance3D);
GDREGISTER_CLASS(OccluderInstance3D);
- GDREGISTER_CLASS(Occluder3D);
- GDREGISTER_VIRTUAL_CLASS(SpriteBase3D);
+ GDREGISTER_ABSTRACT_CLASS(Occluder3D);
+ GDREGISTER_CLASS(ArrayOccluder3D);
+ GDREGISTER_CLASS(QuadOccluder3D);
+ GDREGISTER_CLASS(BoxOccluder3D);
+ GDREGISTER_CLASS(SphereOccluder3D);
+ GDREGISTER_CLASS(PolygonOccluder3D);
+ GDREGISTER_ABSTRACT_CLASS(SpriteBase3D);
GDREGISTER_CLASS(Sprite3D);
GDREGISTER_CLASS(AnimatedSprite3D);
- GDREGISTER_VIRTUAL_CLASS(Light3D);
+ GDREGISTER_CLASS(Label3D);
+ GDREGISTER_ABSTRACT_CLASS(Light3D);
GDREGISTER_CLASS(DirectionalLight3D);
GDREGISTER_CLASS(OmniLight3D);
GDREGISTER_CLASS(SpotLight3D);
@@ -474,17 +495,17 @@ void register_scene_types() {
GDREGISTER_CLASS(LightmapGI);
GDREGISTER_CLASS(LightmapGIData);
GDREGISTER_CLASS(LightmapProbe);
- GDREGISTER_VIRTUAL_CLASS(Lightmapper);
+ GDREGISTER_ABSTRACT_CLASS(Lightmapper);
GDREGISTER_CLASS(GPUParticles3D);
- GDREGISTER_VIRTUAL_CLASS(GPUParticlesCollision3D);
- GDREGISTER_CLASS(GPUParticlesCollisionBox);
- GDREGISTER_CLASS(GPUParticlesCollisionSphere);
- GDREGISTER_CLASS(GPUParticlesCollisionSDF);
- GDREGISTER_CLASS(GPUParticlesCollisionHeightField);
- GDREGISTER_VIRTUAL_CLASS(GPUParticlesAttractor3D);
- GDREGISTER_CLASS(GPUParticlesAttractorBox);
- GDREGISTER_CLASS(GPUParticlesAttractorSphere);
- GDREGISTER_CLASS(GPUParticlesAttractorVectorField);
+ GDREGISTER_ABSTRACT_CLASS(GPUParticlesCollision3D);
+ GDREGISTER_CLASS(GPUParticlesCollisionBox3D);
+ GDREGISTER_CLASS(GPUParticlesCollisionSphere3D);
+ GDREGISTER_CLASS(GPUParticlesCollisionSDF3D);
+ GDREGISTER_CLASS(GPUParticlesCollisionHeightField3D);
+ GDREGISTER_ABSTRACT_CLASS(GPUParticlesAttractor3D);
+ GDREGISTER_CLASS(GPUParticlesAttractorBox3D);
+ GDREGISTER_CLASS(GPUParticlesAttractorSphere3D);
+ GDREGISTER_CLASS(GPUParticlesAttractorVectorField3D);
GDREGISTER_CLASS(CPUParticles3D);
GDREGISTER_CLASS(Position3D);
@@ -493,8 +514,8 @@ void register_scene_types() {
OS::get_singleton()->yield(); // may take time to init
- GDREGISTER_VIRTUAL_CLASS(CollisionObject3D);
- GDREGISTER_VIRTUAL_CLASS(PhysicsBody3D);
+ GDREGISTER_ABSTRACT_CLASS(CollisionObject3D);
+ GDREGISTER_ABSTRACT_CLASS(PhysicsBody3D);
GDREGISTER_CLASS(StaticBody3D);
GDREGISTER_CLASS(AnimatableBody3D);
GDREGISTER_CLASS(RigidDynamicBody3D);
@@ -511,7 +532,6 @@ void register_scene_types() {
GDREGISTER_CLASS(VehicleBody3D);
GDREGISTER_CLASS(VehicleWheel3D);
GDREGISTER_CLASS(Area3D);
- GDREGISTER_CLASS(ProximityGroup3D);
GDREGISTER_CLASS(CollisionShape3D);
GDREGISTER_CLASS(CollisionPolygon3D);
GDREGISTER_CLASS(RayCast3D);
@@ -527,7 +547,7 @@ void register_scene_types() {
GDREGISTER_CLASS(FogMaterial);
GDREGISTER_CLASS(RemoteTransform3D);
- GDREGISTER_VIRTUAL_CLASS(Joint3D);
+ GDREGISTER_ABSTRACT_CLASS(Joint3D);
GDREGISTER_CLASS(PinJoint3D);
GDREGISTER_CLASS(HingeJoint3D);
GDREGISTER_CLASS(SliderJoint3D);
@@ -545,19 +565,22 @@ void register_scene_types() {
GDREGISTER_CLASS(Shader);
GDREGISTER_CLASS(VisualShader);
- GDREGISTER_VIRTUAL_CLASS(VisualShaderNode);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNode);
GDREGISTER_CLASS(VisualShaderNodeCustom);
GDREGISTER_CLASS(VisualShaderNodeInput);
- GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeOutput);
- GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeResizableBase);
- GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeGroupBase);
- GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeConstant);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeOutput);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeResizableBase);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeGroupBase);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeConstant);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeVectorBase);
GDREGISTER_CLASS(VisualShaderNodeComment);
GDREGISTER_CLASS(VisualShaderNodeFloatConstant);
GDREGISTER_CLASS(VisualShaderNodeIntConstant);
GDREGISTER_CLASS(VisualShaderNodeBooleanConstant);
GDREGISTER_CLASS(VisualShaderNodeColorConstant);
+ GDREGISTER_CLASS(VisualShaderNodeVec2Constant);
GDREGISTER_CLASS(VisualShaderNodeVec3Constant);
+ GDREGISTER_CLASS(VisualShaderNodeVec4Constant);
GDREGISTER_CLASS(VisualShaderNodeTransformConstant);
GDREGISTER_CLASS(VisualShaderNodeFloatOp);
GDREGISTER_CLASS(VisualShaderNodeIntOp);
@@ -574,8 +597,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeDotProduct);
GDREGISTER_CLASS(VisualShaderNodeVectorLen);
GDREGISTER_CLASS(VisualShaderNodeDeterminant);
- GDREGISTER_CLASS(VisualShaderNodeScalarDerivativeFunc);
- GDREGISTER_CLASS(VisualShaderNodeVectorDerivativeFunc);
+ GDREGISTER_CLASS(VisualShaderNodeDerivativeFunc);
GDREGISTER_CLASS(VisualShaderNodeClamp);
GDREGISTER_CLASS(VisualShaderNodeFaceForward);
GDREGISTER_CLASS(VisualShaderNodeOuterProduct);
@@ -591,17 +613,19 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeTexture);
GDREGISTER_CLASS(VisualShaderNodeCurveTexture);
GDREGISTER_CLASS(VisualShaderNodeCurveXYZTexture);
- GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeSample3D);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeSample3D);
GDREGISTER_CLASS(VisualShaderNodeTexture2DArray);
GDREGISTER_CLASS(VisualShaderNodeTexture3D);
GDREGISTER_CLASS(VisualShaderNodeCubemap);
- GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeUniform);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeUniform);
GDREGISTER_CLASS(VisualShaderNodeUniformRef);
GDREGISTER_CLASS(VisualShaderNodeFloatUniform);
GDREGISTER_CLASS(VisualShaderNodeIntUniform);
GDREGISTER_CLASS(VisualShaderNodeBooleanUniform);
GDREGISTER_CLASS(VisualShaderNodeColorUniform);
+ GDREGISTER_CLASS(VisualShaderNodeVec2Uniform);
GDREGISTER_CLASS(VisualShaderNodeVec3Uniform);
+ GDREGISTER_CLASS(VisualShaderNodeVec4Uniform);
GDREGISTER_CLASS(VisualShaderNodeTransformUniform);
GDREGISTER_CLASS(VisualShaderNodeTextureUniform);
GDREGISTER_CLASS(VisualShaderNodeTextureUniformTriplanar);
@@ -617,6 +641,9 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeCompare);
GDREGISTER_CLASS(VisualShaderNodeMultiplyAdd);
GDREGISTER_CLASS(VisualShaderNodeBillboard);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeVarying);
+ GDREGISTER_CLASS(VisualShaderNodeVaryingSetter);
+ GDREGISTER_CLASS(VisualShaderNodeVaryingGetter);
GDREGISTER_CLASS(VisualShaderNodeSDFToScreenUV);
GDREGISTER_CLASS(VisualShaderNodeScreenUVToSDF);
@@ -625,7 +652,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeSDFRaymarch);
GDREGISTER_CLASS(VisualShaderNodeParticleOutput);
- GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeParticleEmitter);
+ GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeParticleEmitter);
GDREGISTER_CLASS(VisualShaderNodeParticleSphereEmitter);
GDREGISTER_CLASS(VisualShaderNodeParticleBoxEmitter);
GDREGISTER_CLASS(VisualShaderNodeParticleRingEmitter);
@@ -637,7 +664,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeParticleEmit);
GDREGISTER_CLASS(ShaderMaterial);
- GDREGISTER_VIRTUAL_CLASS(CanvasItem);
+ GDREGISTER_ABSTRACT_CLASS(CanvasItem);
GDREGISTER_CLASS(CanvasTexture);
GDREGISTER_CLASS(CanvasItemMaterial);
SceneTree::add_idle_callback(CanvasItemMaterial::flush_changes);
@@ -656,8 +683,8 @@ void register_scene_types() {
GDREGISTER_CLASS(Line2D);
GDREGISTER_CLASS(MeshInstance2D);
GDREGISTER_CLASS(MultiMeshInstance2D);
- GDREGISTER_VIRTUAL_CLASS(CollisionObject2D);
- GDREGISTER_VIRTUAL_CLASS(PhysicsBody2D);
+ GDREGISTER_ABSTRACT_CLASS(CollisionObject2D);
+ GDREGISTER_ABSTRACT_CLASS(PhysicsBody2D);
GDREGISTER_CLASS(StaticBody2D);
GDREGISTER_CLASS(AnimatableBody2D);
GDREGISTER_CLASS(RigidDynamicBody2D);
@@ -673,7 +700,7 @@ void register_scene_types() {
GDREGISTER_CLASS(Polygon2D);
GDREGISTER_CLASS(Skeleton2D);
GDREGISTER_CLASS(Bone2D);
- GDREGISTER_VIRTUAL_CLASS(Light2D);
+ GDREGISTER_ABSTRACT_CLASS(Light2D);
GDREGISTER_CLASS(PointLight2D);
GDREGISTER_CLASS(DirectionalLight2D);
GDREGISTER_CLASS(LightOccluder2D);
@@ -684,12 +711,12 @@ void register_scene_types() {
GDREGISTER_CLASS(Camera2D);
GDREGISTER_CLASS(AudioListener2D);
- GDREGISTER_VIRTUAL_CLASS(Joint2D);
+ GDREGISTER_ABSTRACT_CLASS(Joint2D);
GDREGISTER_CLASS(PinJoint2D);
GDREGISTER_CLASS(GrooveJoint2D);
GDREGISTER_CLASS(DampedSpringJoint2D);
GDREGISTER_CLASS(TileSet);
- GDREGISTER_VIRTUAL_CLASS(TileSetSource);
+ GDREGISTER_ABSTRACT_CLASS(TileSetSource);
GDREGISTER_CLASS(TileSetAtlasSource);
GDREGISTER_CLASS(TileSetScenesCollectionSource);
GDREGISTER_CLASS(TileMapPattern);
@@ -716,7 +743,7 @@ void register_scene_types() {
/* REGISTER RESOURCES */
- GDREGISTER_VIRTUAL_CLASS(Shader);
+ GDREGISTER_ABSTRACT_CLASS(Shader);
GDREGISTER_CLASS(ParticlesMaterial);
SceneTree::add_idle_callback(ParticlesMaterial::flush_changes);
ParticlesMaterial::init_shaders();
@@ -727,6 +754,7 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(Mesh);
GDREGISTER_CLASS(ArrayMesh);
+ GDREGISTER_CLASS(PlaceholderMesh);
GDREGISTER_CLASS(ImmediateMesh);
GDREGISTER_CLASS(MultiMesh);
GDREGISTER_CLASS(SurfaceTool);
@@ -741,13 +769,15 @@ void register_scene_types() {
GDREGISTER_CLASS(PrismMesh);
GDREGISTER_CLASS(QuadMesh);
GDREGISTER_CLASS(SphereMesh);
+ GDREGISTER_CLASS(TextMesh);
GDREGISTER_CLASS(TubeTrailMesh);
GDREGISTER_CLASS(RibbonTrailMesh);
GDREGISTER_CLASS(PointMesh);
GDREGISTER_VIRTUAL_CLASS(Material);
- GDREGISTER_VIRTUAL_CLASS(BaseMaterial3D);
+ GDREGISTER_ABSTRACT_CLASS(BaseMaterial3D);
GDREGISTER_CLASS(StandardMaterial3D);
GDREGISTER_CLASS(ORMMaterial3D);
+ GDREGISTER_CLASS(PlaceholderMaterial);
SceneTree::add_idle_callback(BaseMaterial3D::flush_changes);
BaseMaterial3D::init_shaders();
@@ -755,7 +785,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); // may take time to init
- GDREGISTER_VIRTUAL_CLASS(Shape3D);
+ GDREGISTER_ABSTRACT_CLASS(Shape3D);
GDREGISTER_CLASS(SeparationRayShape3D);
GDREGISTER_CLASS(SphereShape3D);
GDREGISTER_CLASS(BoxShape3D);
@@ -788,7 +818,8 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(Texture);
GDREGISTER_VIRTUAL_CLASS(Texture2D);
GDREGISTER_CLASS(Sky);
- GDREGISTER_CLASS(StreamTexture2D);
+ GDREGISTER_CLASS(CompressedTexture2D);
+ GDREGISTER_CLASS(PortableCompressedTexture2D);
GDREGISTER_CLASS(ImageTexture);
GDREGISTER_CLASS(AtlasTexture);
GDREGISTER_CLASS(MeshTexture);
@@ -800,23 +831,32 @@ void register_scene_types() {
GDREGISTER_CLASS(AnimatedTexture);
GDREGISTER_CLASS(CameraTexture);
GDREGISTER_VIRTUAL_CLASS(TextureLayered);
- GDREGISTER_VIRTUAL_CLASS(ImageTextureLayered);
+ GDREGISTER_ABSTRACT_CLASS(ImageTextureLayered);
GDREGISTER_VIRTUAL_CLASS(Texture3D);
GDREGISTER_CLASS(ImageTexture3D);
- GDREGISTER_CLASS(StreamTexture3D);
+ GDREGISTER_CLASS(CompressedTexture3D);
GDREGISTER_CLASS(Cubemap);
GDREGISTER_CLASS(CubemapArray);
GDREGISTER_CLASS(Texture2DArray);
- GDREGISTER_VIRTUAL_CLASS(StreamTextureLayered);
- GDREGISTER_CLASS(StreamCubemap);
- GDREGISTER_CLASS(StreamCubemapArray);
- GDREGISTER_CLASS(StreamTexture2DArray);
+ GDREGISTER_ABSTRACT_CLASS(CompressedTextureLayered);
+ GDREGISTER_CLASS(CompressedCubemap);
+ GDREGISTER_CLASS(CompressedCubemapArray);
+ GDREGISTER_CLASS(CompressedTexture2DArray);
+ GDREGISTER_CLASS(PlaceholderTexture2D);
+ GDREGISTER_CLASS(PlaceholderTexture3D);
+ GDREGISTER_ABSTRACT_CLASS(PlaceholderTextureLayered);
+ GDREGISTER_CLASS(PlaceholderTexture2DArray);
+ GDREGISTER_CLASS(PlaceholderCubemap);
+ GDREGISTER_CLASS(PlaceholderCubemapArray);
GDREGISTER_CLASS(Animation);
+ GDREGISTER_CLASS(AnimationLibrary);
GDREGISTER_CLASS(FontData);
GDREGISTER_CLASS(Font);
GDREGISTER_CLASS(Curve);
+ GDREGISTER_CLASS(SceneReplicationConfig);
+
GDREGISTER_CLASS(TextLine);
GDREGISTER_CLASS(TextParagraph);
@@ -838,12 +878,12 @@ void register_scene_types() {
#ifndef _3D_DISABLED
GDREGISTER_CLASS(AudioStreamPlayer3D);
#endif
- GDREGISTER_VIRTUAL_CLASS(VideoStream);
+ GDREGISTER_ABSTRACT_CLASS(VideoStream);
GDREGISTER_CLASS(AudioStreamSample);
OS::get_singleton()->yield(); // may take time to init
- GDREGISTER_VIRTUAL_CLASS(Shape2D);
+ GDREGISTER_ABSTRACT_CLASS(Shape2D);
GDREGISTER_CLASS(WorldBoundaryShape2D);
GDREGISTER_CLASS(SegmentShape2D);
GDREGISTER_CLASS(SeparationRayShape2D);
@@ -864,26 +904,33 @@ void register_scene_types() {
OS::get_singleton()->yield(); // may take time to init
- GDREGISTER_VIRTUAL_CLASS(SceneState);
+ GDREGISTER_ABSTRACT_CLASS(SceneState);
GDREGISTER_CLASS(PackedScene);
GDREGISTER_CLASS(SceneTree);
- GDREGISTER_VIRTUAL_CLASS(SceneTreeTimer); // sorry, you can't create it
+ GDREGISTER_ABSTRACT_CLASS(SceneTreeTimer); // sorry, you can't create it
#ifndef DISABLE_DEPRECATED
// Dropped in 4.0, near approximation.
ClassDB::add_compatibility_class("AnimationTreePlayer", "AnimationTree");
+ ClassDB::add_compatibility_class("BakedLightmap", "LightmapGI");
+ ClassDB::add_compatibility_class("BakedLightmapData", "LightmapGIData");
ClassDB::add_compatibility_class("BitmapFont", "Font");
ClassDB::add_compatibility_class("DynamicFont", "Font");
ClassDB::add_compatibility_class("DynamicFontData", "FontData");
- ClassDB::add_compatibility_class("ToolButton", "Button");
ClassDB::add_compatibility_class("Navigation3D", "Node3D");
ClassDB::add_compatibility_class("Navigation2D", "Node2D");
+ ClassDB::add_compatibility_class("OpenSimplexNoise", "FastNoiseLite");
+ ClassDB::add_compatibility_class("ToolButton", "Button");
ClassDB::add_compatibility_class("YSort", "Node2D");
- ClassDB::add_compatibility_class("GIProbe", "VoxelGI");
- ClassDB::add_compatibility_class("GIProbeData", "VoxelGIData");
- ClassDB::add_compatibility_class("BakedLightmap", "LightmapGI");
- ClassDB::add_compatibility_class("BakedLightmapData", "LightmapGIData");
+ // Portal and room occlusion was replaced by raster occlusion (OccluderInstance3D node).
+ ClassDB::add_compatibility_class("Portal", "Node3D");
+ ClassDB::add_compatibility_class("Room", "Node3D");
+ ClassDB::add_compatibility_class("RoomManager", "Node3D");
+ ClassDB::add_compatibility_class("RoomGroup", "Node3D");
+ ClassDB::add_compatibility_class("Occluder", "Node3D");
+ // The OccluderShapeSphere resource (used in the old Occluder node) is not present anymore.
+ ClassDB::add_compatibility_class("OccluderShapeSphere", "Resource");
// Renamed in 4.0.
// Keep alphabetical ordering to easily locate classes and avoid duplicates.
@@ -896,10 +943,9 @@ void register_scene_types() {
ClassDB::add_compatibility_class("ARVROrigin", "XROrigin3D");
ClassDB::add_compatibility_class("ARVRPositionalTracker", "XRPositionalTracker");
ClassDB::add_compatibility_class("ARVRServer", "XRServer");
+ ClassDB::add_compatibility_class("AStar", "AStar3D");
ClassDB::add_compatibility_class("BoneAttachment", "BoneAttachment3D");
ClassDB::add_compatibility_class("BoxShape", "BoxShape3D");
- ClassDB::add_compatibility_class("BulletPhysicsDirectBodyState", "BulletPhysicsDirectBodyState3D");
- ClassDB::add_compatibility_class("BulletPhysicsServer", "BulletPhysicsServer3D");
ClassDB::add_compatibility_class("Camera", "Camera3D");
ClassDB::add_compatibility_class("CapsuleShape", "CapsuleShape3D");
ClassDB::add_compatibility_class("ClippedCamera", "ClippedCamera3D");
@@ -925,6 +971,8 @@ void register_scene_types() {
ClassDB::add_compatibility_class("EditorSpatialGizmo", "EditorNode3DGizmo");
ClassDB::add_compatibility_class("EditorSpatialGizmoPlugin", "EditorNode3DGizmoPlugin");
ClassDB::add_compatibility_class("Generic6DOFJoint", "Generic6DOFJoint3D");
+ ClassDB::add_compatibility_class("GIProbe", "VoxelGI");
+ ClassDB::add_compatibility_class("GIProbeData", "VoxelGIData");
ClassDB::add_compatibility_class("GradientTexture", "GradientTexture1D");
ClassDB::add_compatibility_class("HeightMapShape", "HeightMapShape3D");
ClassDB::add_compatibility_class("HingeJoint", "HingeJoint3D");
@@ -933,6 +981,7 @@ void register_scene_types() {
ClassDB::add_compatibility_class("KinematicBody2D", "CharacterBody2D");
ClassDB::add_compatibility_class("KinematicCollision", "KinematicCollision3D");
ClassDB::add_compatibility_class("Light", "Light3D");
+ ClassDB::add_compatibility_class("Light2D", "PointLight2D");
ClassDB::add_compatibility_class("LineShape2D", "WorldBoundaryShape2D");
ClassDB::add_compatibility_class("Listener", "AudioListener3D");
ClassDB::add_compatibility_class("MeshInstance", "MeshInstance3D");
@@ -964,7 +1013,6 @@ void register_scene_types() {
ClassDB::add_compatibility_class("PinJoint", "PinJoint3D");
ClassDB::add_compatibility_class("PlaneShape", "WorldBoundaryShape3D");
ClassDB::add_compatibility_class("ProceduralSky", "Sky");
- ClassDB::add_compatibility_class("ProximityGroup", "ProximityGroup3D");
ClassDB::add_compatibility_class("RayCast", "RayCast3D");
ClassDB::add_compatibility_class("RayShape", "SeparationRayShape3D");
ClassDB::add_compatibility_class("RayShape2D", "SeparationRayShape2D");
@@ -986,13 +1034,17 @@ void register_scene_types() {
ClassDB::add_compatibility_class("SpringArm", "SpringArm3D");
ClassDB::add_compatibility_class("Sprite", "Sprite2D");
ClassDB::add_compatibility_class("StaticBody", "StaticBody3D");
+ ClassDB::add_compatibility_class("StreamTexture", "CompressedTexture2D");
ClassDB::add_compatibility_class("TextureProgress", "TextureProgressBar");
ClassDB::add_compatibility_class("VehicleBody", "VehicleBody3D");
ClassDB::add_compatibility_class("VehicleWheel", "VehicleWheel3D");
+ ClassDB::add_compatibility_class("VideoPlayer", "VideoStreamPlayer");
ClassDB::add_compatibility_class("ViewportContainer", "SubViewportContainer");
ClassDB::add_compatibility_class("Viewport", "SubViewport");
ClassDB::add_compatibility_class("VisibilityEnabler", "VisibleOnScreenEnabler3D");
ClassDB::add_compatibility_class("VisibilityNotifier", "VisibleOnScreenNotifier3D");
+ ClassDB::add_compatibility_class("VisibilityNotifier2D", "VisibleOnScreenNotifier2D");
+ ClassDB::add_compatibility_class("VisibilityNotifier3D", "VisibleOnScreenNotifier3D");
ClassDB::add_compatibility_class("VisualServer", "RenderingServer");
ClassDB::add_compatibility_class("VisualShaderNodeScalarConstant", "VisualShaderNodeFloatConstant");
ClassDB::add_compatibility_class("VisualShaderNodeScalarFunc", "VisualShaderNodeFloatFunc");
@@ -1009,12 +1061,17 @@ void register_scene_types() {
ClassDB::add_compatibility_class("VisualShaderNodeVectorScalarStep", "VisualShaderNodeStep");
ClassDB::add_compatibility_class("VisualShaderNodeScalarSwitch", "VisualShaderNodeSwitch");
ClassDB::add_compatibility_class("VisualShaderNodeScalarTransformMult", "VisualShaderNodeTransformOp");
+ ClassDB::add_compatibility_class("VisualShaderNodeScalarDerivativeFunc", "VisualShaderNodeDerivativeFunc");
+ ClassDB::add_compatibility_class("VisualShaderNodeVectorDerivativeFunc", "VisualShaderNodeDerivativeFunc");
ClassDB::add_compatibility_class("World", "World3D");
- ClassDB::add_compatibility_class("StreamTexture", "StreamTexture2D");
- ClassDB::add_compatibility_class("Light2D", "PointLight2D");
- ClassDB::add_compatibility_class("VisibilityNotifier2D", "VisibleOnScreenNotifier2D");
- ClassDB::add_compatibility_class("VisibilityNotifier3D", "VisibleOnScreenNotifier3D");
+ // Renamed during 4.0 alpha, added to ease transition between alphas.
+ ClassDB::add_compatibility_class("StreamCubemap", "CompressedCubemap");
+ ClassDB::add_compatibility_class("StreamCubemapArray", "CompressedCubemapArray");
+ ClassDB::add_compatibility_class("StreamTexture2D", "CompressedTexture2D");
+ ClassDB::add_compatibility_class("StreamTexture2DArray", "CompressedTexture2DArray");
+ ClassDB::add_compatibility_class("StreamTexture3D", "CompressedTexture3D");
+ ClassDB::add_compatibility_class("StreamTextureLayered", "CompressedTextureLayered");
#endif /* DISABLE_DEPRECATED */
OS::get_singleton()->yield(); // may take time to init
@@ -1031,15 +1088,41 @@ void register_scene_types() {
GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i + 1), "");
}
- bool default_theme_hidpi = GLOBAL_DEF("gui/theme/use_hidpi", false);
- ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/use_hidpi", PropertyInfo(Variant::BOOL, "gui/theme/use_hidpi", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+ if (RenderingServer::get_singleton()) {
+ ColorPicker::init_shaders(); // RenderingServer needs to exist for this to succeed.
+ }
+
+ SceneDebugger::initialize();
+ SceneReplicationInterface::make_default();
+ SceneRPCInterface::make_default();
+ SceneCacheInterface::make_default();
+}
+
+void initialize_theme() {
+ // Allow creating the default theme at a different scale to suit higher/lower base resolutions.
+ float default_theme_scale = GLOBAL_DEF("gui/theme/default_theme_scale", 1.0);
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_theme_scale", PropertyInfo(Variant::FLOAT, "gui/theme/default_theme_scale", PROPERTY_HINT_RANGE, "0.5,8,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
String theme_path = GLOBAL_DEF_RST("gui/theme/custom", "");
ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", "");
ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res,*.font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+ bool font_antialiased = (bool)GLOBAL_DEF_RST("gui/theme/default_font_antialiased", true);
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiased", PropertyInfo(Variant::BOOL, "gui/theme/default_font_antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
+ TextServer::Hinting font_hinting = (TextServer::Hinting)(int)GLOBAL_DEF_RST("gui/theme/default_font_hinting", TextServer::HINTING_LIGHT);
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_hinting", PropertyInfo(Variant::INT, "gui/theme/default_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
+ TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)GLOBAL_DEF_RST("gui/theme/default_font_subpixel_positioning", TextServer::SUBPIXEL_POSITIONING_AUTO);
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
+ const bool font_msdf = GLOBAL_DEF_RST("gui/theme/default_font_multichannel_signed_distance_field", false);
+ const bool font_generate_mipmaps = GLOBAL_DEF_RST("gui/theme/default_font_generate_mipmaps", false);
+
Ref<Font> font;
- if (font_path != String()) {
+ if (!font_path.is_empty()) {
font = ResourceLoader::load(font_path);
if (!font.is_valid()) {
ERR_PRINT("Error loading custom font '" + font_path + "'");
@@ -1048,29 +1131,23 @@ void register_scene_types() {
// Always make the default theme to avoid invalid default font/icon/style in the given theme.
if (RenderingServer::get_singleton()) {
- make_default_theme(default_theme_hidpi, font);
- ColorPicker::init_shaders(); // RenderingServer needs to exist for this to succeed.
+ make_default_theme(default_theme_scale, font, font_subpixel_positioning, font_hinting, font_antialiased, font_msdf, font_generate_mipmaps);
}
- if (theme_path != String()) {
+ if (!theme_path.is_empty()) {
Ref<Theme> theme = ResourceLoader::load(theme_path);
if (theme.is_valid()) {
Theme::set_project_default(theme);
if (font.is_valid()) {
- Theme::set_default_font(font);
+ Theme::set_fallback_font(font);
}
} else {
ERR_PRINT("Error loading custom theme '" + theme_path + "'");
}
}
- SceneDebugger::initialize();
-
- NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE);
}
void unregister_scene_types() {
- NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE);
-
SceneDebugger::deinitialize();
clear_default_theme();
diff --git a/scene/register_scene_types.h b/scene/register_scene_types.h
index 1ff542eef8..f0a14387c1 100644
--- a/scene/register_scene_types.h
+++ b/scene/register_scene_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,4 +34,6 @@
void register_scene_types();
void unregister_scene_types();
+void initialize_theme();
+
#endif
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 29aca6b321..a26aa10f42 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -335,7 +335,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
bt->values.write[i].value.in_handle.y = rv[i * 6 + 2];
bt->values.write[i].value.out_handle.x = rv[i * 6 + 3];
bt->values.write[i].value.out_handle.y = rv[i * 6 + 4];
- bt->values.write[i].value.handle_mode = (HandleMode)rv[i * 6 + 5];
+ bt->values.write[i].value.handle_mode = static_cast<HandleMode>((int)rv[i * 6 + 5]);
}
}
@@ -930,7 +930,7 @@ void Animation::remove_track(int p_track) {
}
memdelete(t);
- tracks.remove(p_track);
+ tracks.remove_at(p_track);
emit_changed();
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
@@ -1320,7 +1320,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
ERR_FAIL_COND(tt->compressed_track >= 0);
ERR_FAIL_INDEX(p_idx, tt->positions.size());
- tt->positions.remove(p_idx);
+ tt->positions.remove_at(p_idx);
} break;
case TYPE_ROTATION_3D: {
@@ -1329,7 +1329,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
ERR_FAIL_COND(rt->compressed_track >= 0);
ERR_FAIL_INDEX(p_idx, rt->rotations.size());
- rt->rotations.remove(p_idx);
+ rt->rotations.remove_at(p_idx);
} break;
case TYPE_SCALE_3D: {
@@ -1338,7 +1338,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
ERR_FAIL_COND(st->compressed_track >= 0);
ERR_FAIL_INDEX(p_idx, st->scales.size());
- st->scales.remove(p_idx);
+ st->scales.remove_at(p_idx);
} break;
case TYPE_BLEND_SHAPE: {
@@ -1347,37 +1347,37 @@ void Animation::track_remove_key(int p_track, int p_idx) {
ERR_FAIL_COND(bst->compressed_track >= 0);
ERR_FAIL_INDEX(p_idx, bst->blend_shapes.size());
- bst->blend_shapes.remove(p_idx);
+ bst->blend_shapes.remove_at(p_idx);
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
ERR_FAIL_INDEX(p_idx, vt->values.size());
- vt->values.remove(p_idx);
+ vt->values.remove_at(p_idx);
} break;
case TYPE_METHOD: {
MethodTrack *mt = static_cast<MethodTrack *>(t);
ERR_FAIL_INDEX(p_idx, mt->methods.size());
- mt->methods.remove(p_idx);
+ mt->methods.remove_at(p_idx);
} break;
case TYPE_BEZIER: {
BezierTrack *bz = static_cast<BezierTrack *>(t);
ERR_FAIL_INDEX(p_idx, bz->values.size());
- bz->values.remove(p_idx);
+ bz->values.remove_at(p_idx);
} break;
case TYPE_AUDIO: {
AudioTrack *ad = static_cast<AudioTrack *>(t);
ERR_FAIL_INDEX(p_idx, ad->values.size());
- ad->values.remove(p_idx);
+ ad->values.remove_at(p_idx);
} break;
case TYPE_ANIMATION: {
AnimationTrack *an = static_cast<AnimationTrack *>(t);
ERR_FAIL_INDEX(p_idx, an->values.size());
- an->values.remove(p_idx);
+ an->values.remove_at(p_idx);
} break;
}
@@ -1634,7 +1634,7 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke
k.value.in_handle.y = arr[2];
k.value.out_handle.x = arr[3];
k.value.out_handle.y = arr[4];
- k.value.handle_mode = (HandleMode) int(arr[5]);
+ k.value.handle_mode = static_cast<HandleMode>((int)arr[5]);
_insert(p_time, bt->values, k);
} break;
@@ -1905,7 +1905,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
TKey<Vector3> key = tt->positions[p_key_idx];
key.time = p_time;
- tt->positions.remove(p_key_idx);
+ tt->positions.remove_at(p_key_idx);
_insert(p_time, tt->positions, key);
return;
}
@@ -1915,7 +1915,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, tt->rotations.size());
TKey<Quaternion> key = tt->rotations[p_key_idx];
key.time = p_time;
- tt->rotations.remove(p_key_idx);
+ tt->rotations.remove_at(p_key_idx);
_insert(p_time, tt->rotations, key);
return;
}
@@ -1925,7 +1925,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, tt->scales.size());
TKey<Vector3> key = tt->scales[p_key_idx];
key.time = p_time;
- tt->scales.remove(p_key_idx);
+ tt->scales.remove_at(p_key_idx);
_insert(p_time, tt->scales, key);
return;
}
@@ -1935,7 +1935,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, tt->blend_shapes.size());
TKey<float> key = tt->blend_shapes[p_key_idx];
key.time = p_time;
- tt->blend_shapes.remove(p_key_idx);
+ tt->blend_shapes.remove_at(p_key_idx);
_insert(p_time, tt->blend_shapes, key);
return;
}
@@ -1944,7 +1944,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, vt->values.size());
TKey<Variant> key = vt->values[p_key_idx];
key.time = p_time;
- vt->values.remove(p_key_idx);
+ vt->values.remove_at(p_key_idx);
_insert(p_time, vt->values, key);
return;
}
@@ -1953,7 +1953,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, mt->methods.size());
MethodKey key = mt->methods[p_key_idx];
key.time = p_time;
- mt->methods.remove(p_key_idx);
+ mt->methods.remove_at(p_key_idx);
_insert(p_time, mt->methods, key);
return;
}
@@ -1962,7 +1962,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, bt->values.size());
TKey<BezierKey> key = bt->values[p_key_idx];
key.time = p_time;
- bt->values.remove(p_key_idx);
+ bt->values.remove_at(p_key_idx);
_insert(p_time, bt->values, key);
return;
}
@@ -1971,7 +1971,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, at->values.size());
TKey<AudioKey> key = at->values[p_key_idx];
key.time = p_time;
- at->values.remove(p_key_idx);
+ at->values.remove_at(p_key_idx);
_insert(p_time, at->values, key);
return;
}
@@ -1980,7 +1980,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_key_idx, at->values.size());
TKey<StringName> key = at->values[p_key_idx];
key.time = p_time;
- at->values.remove(p_key_idx);
+ at->values.remove_at(p_key_idx);
_insert(p_time, at->values, key);
return;
}
@@ -2155,7 +2155,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
bt->values.write[p_key_idx].value.in_handle.y = arr[2];
bt->values.write[p_key_idx].value.out_handle.x = arr[3];
bt->values.write[p_key_idx].value.out_handle.y = arr[4];
- bt->values.write[p_key_idx].value.handle_mode = (HandleMode) int(arr[5]);
+ bt->values.write[p_key_idx].value.handle_mode = static_cast<HandleMode>((int)arr[5]);
} break;
case TYPE_AUDIO: {
@@ -2320,22 +2320,12 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a
if (vformat == ((1 << Variant::INT) | (1 << Variant::FLOAT)) || vformat == (1 << Variant::FLOAT)) {
//mix of real and int
+ real_t a = p_a;
+ real_t b = p_b;
+ real_t pa = p_pre_a;
+ real_t pb = p_post_b;
- real_t p0 = p_pre_a;
- real_t p1 = p_a;
- real_t p2 = p_b;
- real_t p3 = p_post_b;
-
- real_t t = p_c;
- real_t t2 = t * t;
- real_t t3 = t2 * t;
-
- return 0.5f *
- ((p1 * 2.0f) +
- (-p0 + p2) * t +
- (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
- (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
-
+ return Math::cubic_interpolate(a, b, pa, pb, p_c);
} else if ((vformat & (vformat - 1))) {
return p_a; //can't interpolate, mix of types
}
@@ -2423,7 +2413,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
real_t c = 0.0;
// prepare for all cases of interpolation
- if ((loop_mode == LOOP_LINEAR || loop_mode == LOOP_PINGPONG) && p_loop_wrap) {
+ if (loop_mode == LOOP_LINEAR && p_loop_wrap) {
// loop
if (!p_backward) {
// no backward
@@ -2474,34 +2464,38 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
real_t from = (length - p_time) - (length - p_keys[idx].time);
- if (Math::is_zero_approx(delta))
+ if (Math::is_zero_approx(delta)) {
c = 0;
- else
+ } else {
c = from / delta;
+ }
} else {
next = len - 1;
real_t delta = p_keys[idx].time + (length - p_keys[next].time);
real_t from = (length - p_time) - (length - p_keys[idx].time);
- if (Math::is_zero_approx(delta))
+ if (Math::is_zero_approx(delta)) {
c = 0;
- else
+ } else {
c = from / delta;
+ }
}
} else {
// on loop, in front of last key
idx = 0;
next = len - 1;
real_t endtime = p_keys[idx].time;
- if (endtime > length) // may be keys past the end
+ if (endtime > length) { // may be keys past the end
endtime = length;
+ }
real_t delta = p_keys[next].time - endtime;
real_t from = p_time - endtime;
- if (Math::is_zero_approx(delta))
+ if (Math::is_zero_approx(delta)) {
c = 0;
- else
+ } else {
c = from / delta;
+ }
}
}
} else { // no loop
@@ -2573,11 +2567,19 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
case INTERPOLATION_CUBIC: {
int pre = idx - 1;
if (pre < 0) {
- pre = 0;
+ if (loop_mode == LOOP_LINEAR && p_loop_wrap) {
+ pre = len - 1;
+ } else {
+ pre = 0;
+ }
}
int post = next + 1;
if (post >= len) {
- post = next;
+ if (loop_mode == LOOP_LINEAR && p_loop_wrap) {
+ post = 0;
+ } else {
+ post = next;
+ }
}
return _cubic_interpolate(p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c);
@@ -2609,7 +2611,7 @@ Variant Animation::value_track_interpolate(int p_track, double p_time) const {
void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
- to_time = length * 1.001; //include a little more if at the end
+ to_time = length + CMP_EPSILON; //include a little more if at the end
}
int to = _find(vt->values, to_time);
@@ -2715,6 +2717,7 @@ void Animation::value_track_set_update_mode(int p_track, UpdateMode p_mode) {
ValueTrack *vt = static_cast<ValueTrack *>(t);
vt->update_mode = p_mode;
+ emit_changed();
}
Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const {
@@ -2729,7 +2732,7 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const
template <class T>
void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
- to_time = length * 1.01; //include a little more if at the end
+ to_time = length + CMP_EPSILON; //include a little more if at the end
}
int to = _find(p_array, to_time);
@@ -3080,7 +3083,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
- to_time = length * 1.01; //include a little more if at the end
+ to_time = length + CMP_EPSILON; //include a little more if at the end
}
int to = _find(mt->methods, to_time);
@@ -3425,7 +3428,6 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
real_t duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes
real_t low = 0.0; // 0% of the current animation segment
real_t high = 1.0; // 100% of the current animation segment
- real_t middle;
Vector2 start(0, bt->values[idx].value.value);
Vector2 start_out = start + bt->values[idx].value.out_handle;
@@ -3434,7 +3436,7 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
//narrow high and low as much as possible
for (int i = 0; i < iterations; i++) {
- middle = (low + high) / 2;
+ real_t middle = (low + high) / 2;
Vector2 interp = _bezier_interp(middle, start, start_out, end_in, end);
@@ -3453,7 +3455,7 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
return low_pos.lerp(high_pos, c).y;
}
-int Animation::audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset, real_t p_end_offset) {
+int Animation::audio_track_insert_key(int p_track, double p_time, const Ref<Resource> &p_stream, real_t p_start_offset, real_t p_end_offset) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, -1);
@@ -3479,7 +3481,7 @@ int Animation::audio_track_insert_key(int p_track, double p_time, const RES &p_s
return key;
}
-void Animation::audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream) {
+void Animation::audio_track_set_key_stream(int p_track, int p_key, const Ref<Resource> &p_stream) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_AUDIO);
@@ -3529,14 +3531,14 @@ void Animation::audio_track_set_key_end_offset(int p_track, int p_key, real_t p_
emit_changed();
}
-RES Animation::audio_track_get_key_stream(int p_track, int p_key) const {
- ERR_FAIL_INDEX_V(p_track, tracks.size(), RES());
+Ref<Resource> Animation::audio_track_get_key_stream(int p_track, int p_key) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), Ref<Resource>());
const Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_AUDIO, RES());
+ ERR_FAIL_COND_V(t->type != TYPE_AUDIO, Ref<Resource>());
const AudioTrack *at = static_cast<const AudioTrack *>(t);
- ERR_FAIL_INDEX_V(p_key, at->values.size(), RES());
+ ERR_FAIL_INDEX_V(p_key, at->values.size(), Ref<Resource>());
return at->values[p_key].value.stream;
}
@@ -3679,7 +3681,7 @@ void Animation::track_move_to(int p_track, int p_to_index) {
}
Track *track = tracks.get(p_track);
- tracks.remove(p_track);
+ tracks.remove_at(p_track);
// Take into account that the position of the tracks that come after the one removed will change.
tracks.insert(p_to_index > p_track ? p_to_index - 1 : p_to_index, track);
@@ -3826,7 +3828,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode"), "set_loop_mode", "get_loop_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step");
ADD_SIGNAL(MethodInfo("tracks_changed"));
@@ -3998,13 +4000,12 @@ bool Animation::_blend_shape_track_optimize_key(const TKey<float> &t0, const TKe
float v1 = t1.value;
float v2 = t2.value;
- if (Math::is_equal_approx(v1, v2, p_allowed_unit_error)) {
+ if (Math::is_equal_approx(v1, v2, (float)p_allowed_unit_error)) {
//0 and 2 are close, let's see if 1 is close
- if (!Math::is_equal_approx(v0, v1, p_allowed_unit_error)) {
+ if (!Math::is_equal_approx(v0, v1, (float)p_allowed_unit_error)) {
//not close, not optimizable
return false;
}
-
} else {
/*
TODO eventually discuss a way to optimize these better.
@@ -4058,7 +4059,7 @@ void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err,
prev_erased = true;
}
- tt->positions.remove(i);
+ tt->positions.remove_at(i);
i--;
} else {
@@ -4093,7 +4094,7 @@ void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_angular_err
prev_erased = true;
}
- tt->rotations.remove(i);
+ tt->rotations.remove_at(i);
i--;
} else {
@@ -4127,7 +4128,7 @@ void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) {
prev_erased = true;
}
- tt->scales.remove(i);
+ tt->scales.remove_at(i);
i--;
} else {
@@ -4162,7 +4163,7 @@ void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_linear_e
prev_erased = true;
}
- tt->blend_shapes.remove(i);
+ tt->blend_shapes.remove_at(i);
i--;
} else {
@@ -4198,7 +4199,7 @@ struct AnimationCompressionDataState {
};
uint32_t components = 3;
- LocalVector<uint8_t> data; //commited packets
+ LocalVector<uint8_t> data; // Committed packets.
struct PacketData {
int32_t data[3] = { 0, 0, 0 };
uint32_t frame = 0;
@@ -4345,7 +4346,7 @@ struct AnimationCompressionDataState {
if (temp_packets.size() == 0) {
return; //nohing to do
}
-#define DEBUG_PACKET_PUSH
+//#define DEBUG_PACKET_PUSH
#ifdef DEBUG_PACKET_PUSH
#ifndef _MSC_VER
#warning Debugging packet push, disable this code in production to gain a bit more import performance.
@@ -4376,7 +4377,7 @@ struct AnimationCompressionDataState {
header_bytes += 2;
}
- while (header_bytes % 4 != 0) {
+ while (header_bytes < 8 && header_bytes % 4 != 0) { // First cond needed to silence wrong GCC warning.
header[header_bytes++] = 0;
}
@@ -4577,7 +4578,7 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
}
}
for (int j = 0; j < 3; j++) {
- //cant have zero
+ // Can't have zero.
if (aabb.size[j] < CMP_EPSILON) {
aabb.size[j] = CMP_EPSILON;
}
@@ -4597,7 +4598,7 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
}
}
for (int j = 0; j < 3; j++) {
- //cant have zero
+ // Can't have zero.
if (aabb.size[j] < CMP_EPSILON) {
aabb.size[j] = CMP_EPSILON;
}
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 8e4287e4fb..b4528ccd3a 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -180,7 +180,7 @@ private:
/* AUDIO TRACK */
struct AudioKey {
- RES stream;
+ Ref<Resource> stream;
real_t start_offset = 0.0; //offset from start
real_t end_offset = 0.0; //offset from end, if 0 then full length or infinite
AudioKey() {
@@ -436,11 +436,11 @@ public:
real_t bezier_track_interpolate(int p_track, double p_time) const;
- int audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset = 0, real_t p_end_offset = 0);
- void audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream);
+ int audio_track_insert_key(int p_track, double p_time, const Ref<Resource> &p_stream, real_t p_start_offset = 0, real_t p_end_offset = 0);
+ void audio_track_set_key_stream(int p_track, int p_key, const Ref<Resource> &p_stream);
void audio_track_set_key_start_offset(int p_track, int p_key, real_t p_offset);
void audio_track_set_key_end_offset(int p_track, int p_key, real_t p_offset);
- RES audio_track_get_key_stream(int p_track, int p_key) const;
+ Ref<Resource> audio_track_get_key_stream(int p_track, int p_key) const;
real_t audio_track_get_key_start_offset(int p_track, int p_key) const;
real_t audio_track_get_key_end_offset(int p_track, int p_key) const;
diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp
new file mode 100644
index 0000000000..2a581fb126
--- /dev/null
+++ b/scene/resources/animation_library.cpp
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* animation_library.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 "animation_library.h"
+
+bool AnimationLibrary::is_valid_animation_name(const String &p_name) {
+ return !(p_name.is_empty() || p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("["));
+}
+
+bool AnimationLibrary::is_valid_library_name(const String &p_name) {
+ return !(p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("["));
+}
+
+String AnimationLibrary::validate_library_name(const String &p_name) {
+ String name = p_name;
+ const char *characters = "/:,[";
+ for (const char *p = characters; *p; p++) {
+ name = name.replace(String::chr(*p), "_");
+ }
+ return name;
+}
+
+Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) {
+ ERR_FAIL_COND_V_MSG(!is_valid_animation_name(p_name), ERR_INVALID_PARAMETER, "Invalid animation name: '" + String(p_name) + "'.");
+ ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER);
+
+ if (animations.has(p_name)) {
+ animations.erase(p_name);
+ emit_signal(SNAME("animation_removed"), p_name);
+ }
+
+ animations.insert(p_name, p_animation);
+ emit_signal(SNAME("animation_added"), p_name);
+ notify_property_list_changed();
+ return OK;
+}
+
+void AnimationLibrary::remove_animation(const StringName &p_name) {
+ ERR_FAIL_COND(!animations.has(p_name));
+
+ animations.erase(p_name);
+ emit_signal(SNAME("animation_removed"), p_name);
+ notify_property_list_changed();
+}
+
+void AnimationLibrary::rename_animation(const StringName &p_name, const StringName &p_new_name) {
+ ERR_FAIL_COND(!animations.has(p_name));
+ ERR_FAIL_COND_MSG(!is_valid_animation_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'.");
+ ERR_FAIL_COND(animations.has(p_new_name));
+
+ animations.insert(p_new_name, animations[p_name]);
+ animations.erase(p_name);
+ emit_signal(SNAME("animation_renamed"), p_name, p_new_name);
+}
+
+bool AnimationLibrary::has_animation(const StringName &p_name) const {
+ return animations.has(p_name);
+}
+
+Ref<Animation> AnimationLibrary::get_animation(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!animations.has(p_name), Ref<Animation>());
+
+ return animations[p_name];
+}
+
+TypedArray<StringName> AnimationLibrary::_get_animation_list() const {
+ TypedArray<StringName> ret;
+ List<StringName> names;
+ get_animation_list(&names);
+ for (const StringName &K : names) {
+ ret.push_back(K);
+ }
+ return ret;
+}
+
+void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const {
+ List<StringName> anims;
+
+ for (const KeyValue<StringName, Ref<Animation>> &E : animations) {
+ anims.push_back(E.key);
+ }
+
+ anims.sort_custom<StringName::AlphCompare>();
+
+ for (const StringName &E : anims) {
+ p_animations->push_back(E);
+ }
+}
+
+void AnimationLibrary::_set_data(const Dictionary &p_data) {
+ animations.clear();
+ List<Variant> keys;
+ p_data.get_key_list(&keys);
+ for (const Variant &K : keys) {
+ add_animation(K, p_data[K]);
+ }
+}
+
+Dictionary AnimationLibrary::_get_data() const {
+ Dictionary ret;
+ for (const KeyValue<StringName, Ref<Animation>> &K : animations) {
+ ret[K.key] = K.value;
+ }
+ return ret;
+}
+
+void AnimationLibrary::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_animation", "name", "animation"), &AnimationLibrary::add_animation);
+ ClassDB::bind_method(D_METHOD("remove_animation", "name"), &AnimationLibrary::remove_animation);
+ ClassDB::bind_method(D_METHOD("rename_animation", "name", "newname"), &AnimationLibrary::rename_animation);
+ ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationLibrary::has_animation);
+ ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationLibrary::get_animation);
+ ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationLibrary::_get_animation_list);
+
+ ClassDB::bind_method(D_METHOD("_set_data", "data"), &AnimationLibrary::_set_data);
+ ClassDB::bind_method(D_METHOD("_get_data"), &AnimationLibrary::_get_data);
+
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
+ ADD_SIGNAL(MethodInfo("animation_added", PropertyInfo(Variant::OBJECT, "name", PROPERTY_HINT_RESOURCE_TYPE, "Animation")));
+ ADD_SIGNAL(MethodInfo("animation_removed", PropertyInfo(Variant::OBJECT, "name", PROPERTY_HINT_RESOURCE_TYPE, "Animation")));
+ ADD_SIGNAL(MethodInfo("animation_renamed", PropertyInfo(Variant::OBJECT, "name", PROPERTY_HINT_RESOURCE_TYPE, "Animation"), PropertyInfo(Variant::OBJECT, "to_name", PROPERTY_HINT_RESOURCE_TYPE, "Animation")));
+}
+AnimationLibrary::AnimationLibrary() {
+}
diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h
new file mode 100644
index 0000000000..7a69cd140a
--- /dev/null
+++ b/scene/resources/animation_library.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* animation_library.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 ANIMATION_LIBRARY_H
+#define ANIMATION_LIBRARY_H
+
+#include "core/variant/typed_array.h"
+#include "scene/resources/animation.h"
+
+class AnimationLibrary : public Resource {
+ GDCLASS(AnimationLibrary, Resource)
+
+ void _set_data(const Dictionary &p_data);
+ Dictionary _get_data() const;
+
+ TypedArray<StringName> _get_animation_list() const;
+
+ friend class AnimationPlayer; //for faster access
+ HashMap<StringName, Ref<Animation>> animations;
+
+protected:
+ static void _bind_methods();
+
+public:
+ static bool is_valid_animation_name(const String &p_name);
+ static bool is_valid_library_name(const String &p_name);
+ static String validate_library_name(const String &p_name);
+
+ Error add_animation(const StringName &p_name, const Ref<Animation> &p_animation);
+ void remove_animation(const StringName &p_name);
+ void rename_animation(const StringName &p_name, const StringName &p_new_name);
+ bool has_animation(const StringName &p_name) const;
+ Ref<Animation> get_animation(const StringName &p_name) const;
+ void get_animation_list(List<StringName> *p_animations) const;
+
+ AnimationLibrary();
+};
+
+#endif // ANIMATIONLIBRARY_H
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index d3fab802c5..30c222bdff 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -556,9 +556,9 @@ Error AudioStreamSample::save_to_wav(const String &p_path) {
file_path += ".wav";
}
- FileAccessRef file = FileAccess::open(file_path, FileAccess::WRITE); //Overrides existing file if present
+ Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE); //Overrides existing file if present
- ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE);
+ ERR_FAIL_COND_V(file.is_null(), ERR_FILE_CANT_WRITE);
// Create WAV Header
file->store_string("RIFF"); //ChunkID
@@ -596,8 +596,6 @@ Error AudioStreamSample::save_to_wav(const String &p_path) {
break;
}
- file->close();
-
return OK;
}
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 0eb34be9bf..357febc27a 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -89,6 +89,7 @@ public:
FORMAT_IMA_ADPCM
};
+ // Keep the ResourceImporterWAV `edit/loop_mode` enum hint in sync with these options.
enum LoopMode {
LOOP_DISABLED,
LOOP_FORWARD,
diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp
index 16f1507c34..634fb3ef2f 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -170,8 +170,8 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start)
int curx = startx;
int cury = starty;
unsigned int count = 0;
- Set<Point2i> case9s;
- Set<Point2i> case6s;
+ RBSet<Point2i> case9s;
+ RBSet<Point2i> case6s;
Vector<Vector2> _points;
do {
int sv = 0;
@@ -667,11 +667,13 @@ void BitMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_true_bit_count"), &BitMap::get_true_bit_count);
ClassDB::bind_method(D_METHOD("get_size"), &BitMap::get_size);
+ ClassDB::bind_method(D_METHOD("resize", "new_size"), &BitMap::resize);
ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data);
ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask);
+ ClassDB::bind_method(D_METHOD("convert_to_image"), &BitMap::convert_to_image);
ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0));
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h
index 68fd0b999a..0d0d779c32 100644
--- a/scene/resources/bit_map.h
+++ b/scene/resources/bit_map.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp
index 008914c5ee..1abbf366fd 100644
--- a/scene/resources/box_shape_3d.cpp
+++ b/scene/resources/box_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -77,6 +77,7 @@ bool BoxShape3D::_get(const StringName &p_name, Variant &r_property) const {
#endif // DISABLE_DEPRECATED
void BoxShape3D::set_size(const Vector3 &p_size) {
+ ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0 || p_size.z < 0, "BoxShape3D size cannot be negative.");
size = p_size;
_update_shape();
notify_change_to_owners();
@@ -95,5 +96,5 @@ void BoxShape3D::_bind_methods() {
BoxShape3D::BoxShape3D() :
Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_BOX)) {
- set_size(Vector3(2, 2, 2));
+ set_size(Vector3(1, 1, 1));
}
diff --git a/scene/resources/box_shape_3d.h b/scene/resources/box_shape_3d.h
index 91978a0e6a..4e6db893af 100644
--- a/scene/resources/box_shape_3d.h
+++ b/scene/resources/box_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp
index 0df372ea1b..ebe2aa4dba 100644
--- a/scene/resources/camera_effects.cpp
+++ b/scene/resources/camera_effects.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/camera_effects.h b/scene/resources/camera_effects.h
index b9338f4806..85ae64cdf5 100644
--- a/scene/resources/camera_effects.h
+++ b/scene/resources/camera_effects.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
index b291724c4c..aa6cc4aded 100644
--- a/scene/resources/canvas_item_material.cpp
+++ b/scene/resources/canvas_item_material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,7 @@
Mutex CanvasItemMaterial::material_mutex;
SelfList<CanvasItemMaterial>::List *CanvasItemMaterial::dirty_materials = nullptr;
-Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
+HashMap<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData, CanvasItemMaterial::MaterialKey> CanvasItemMaterial::shader_map;
CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = nullptr;
void CanvasItemMaterial::init_shaders() {
diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h
index 37cd4de136..7c44c125a8 100644
--- a/scene/resources/canvas_item_material.h
+++ b/scene/resources/canvas_item_material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -63,8 +63,11 @@ private:
uint32_t key = 0;
- bool operator<(const MaterialKey &p_key) const {
- return key < p_key.key;
+ static uint32_t hash(const MaterialKey &p_key) {
+ return hash_djb2_one_32(p_key.key);
+ }
+ bool operator==(const MaterialKey &p_key) const {
+ return key == p_key.key;
}
};
@@ -81,7 +84,7 @@ private:
int users = 0;
};
- static Map<MaterialKey, ShaderData> shader_map;
+ static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map;
MaterialKey current_key;
@@ -107,10 +110,10 @@ private:
LightMode light_mode = LIGHT_MODE_NORMAL;
bool particles_animation = false;
- // Initialized in the constructor.
- int particles_anim_h_frames;
- int particles_anim_v_frames;
- bool particles_anim_loop;
+ // Proper values set in constructor.
+ int particles_anim_h_frames = 0;
+ int particles_anim_v_frames = 0;
+ bool particles_anim_loop = false;
protected:
static void _bind_methods();
diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp
index 0818e4fd99..c7bd4cb698 100644
--- a/scene/resources/capsule_shape_2d.cpp
+++ b/scene/resources/capsule_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -59,6 +59,7 @@ void CapsuleShape2D::_update_shape() {
}
void CapsuleShape2D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0, "CapsuleShape2D radius cannot be negative.");
radius = p_radius;
if (radius > height * 0.5) {
height = radius * 2.0;
@@ -71,6 +72,7 @@ real_t CapsuleShape2D::get_radius() const {
}
void CapsuleShape2D::set_height(real_t p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0, "CapsuleShape2D height cannot be negative.");
height = p_height;
if (radius > height * 0.5) {
radius = height * 0.5;
@@ -84,13 +86,11 @@ real_t CapsuleShape2D::get_height() const {
void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Vector2> points = _get_points();
- Vector<Color> col;
- col.push_back(p_color);
+ Vector<Color> col = { p_color };
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
if (is_collision_outline_enabled()) {
+ points.push_back(points[0]);
RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
- // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
}
}
@@ -109,8 +109,8 @@ void CapsuleShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleShape2D::set_height);
ClassDB::bind_method(D_METHOD("get_height"), &CapsuleShape2D::get_height);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_height", "get_height");
ADD_LINKED_PROPERTY("radius", "height");
ADD_LINKED_PROPERTY("height", "radius");
}
diff --git a/scene/resources/capsule_shape_2d.h b/scene/resources/capsule_shape_2d.h
index 37b6c52c0e..ec8b540947 100644
--- a/scene/resources/capsule_shape_2d.h
+++ b/scene/resources/capsule_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp
index afec7b1877..d708706ff2 100644
--- a/scene/resources/capsule_shape_3d.cpp
+++ b/scene/resources/capsule_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "capsule_shape_3d.h"
+
#include "servers/physics_server_3d.h"
Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const {
@@ -79,6 +80,7 @@ void CapsuleShape3D::_update_shape() {
}
void CapsuleShape3D::set_radius(float p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0, "CapsuleShape3D radius cannot be negative.");
radius = p_radius;
if (radius > height * 0.5) {
height = radius * 2.0;
@@ -92,6 +94,7 @@ float CapsuleShape3D::get_radius() const {
}
void CapsuleShape3D::set_height(float p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0, "CapsuleShape3D height cannot be negative.");
height = p_height;
if (radius > height * 0.5) {
radius = height * 0.5;
@@ -110,8 +113,8 @@ void CapsuleShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleShape3D::set_height);
ClassDB::bind_method(D_METHOD("get_height"), &CapsuleShape3D::get_height);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_height", "get_height");
ADD_LINKED_PROPERTY("radius", "height");
ADD_LINKED_PROPERTY("height", "radius");
}
diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h
index f09b4fb77d..4c039ab326 100644
--- a/scene/resources/capsule_shape_3d.h
+++ b/scene/resources/capsule_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,8 +35,8 @@
class CapsuleShape3D : public Shape3D {
GDCLASS(CapsuleShape3D, Shape3D);
- float radius = 1.0;
- float height = 3.0;
+ float radius = 0.5;
+ float height = 2.0;
protected:
static void _bind_methods();
diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp
index f06bc4248d..c287de9ede 100644
--- a/scene/resources/circle_shape_2d.cpp
+++ b/scene/resources/circle_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,6 +43,7 @@ void CircleShape2D::_update_shape() {
}
void CircleShape2D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0, "CircleShape2D radius cannot be negative.");
radius = p_radius;
_update_shape();
}
@@ -55,7 +56,7 @@ void CircleShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CircleShape2D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &CircleShape2D::get_radius);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,16384,0.5"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius");
}
Rect2 CircleShape2D::get_rect() const {
@@ -71,18 +72,19 @@ real_t CircleShape2D::get_enclosing_radius() const {
void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Vector2> points;
+ points.resize(24);
+
const real_t turn_step = Math_TAU / 24.0;
for (int i = 0; i < 24; i++) {
- points.push_back(Vector2(Math::cos(i * turn_step), Math::sin(i * turn_step)) * get_radius());
+ points.write[i] = Vector2(Math::cos(i * turn_step), Math::sin(i * turn_step)) * get_radius();
}
- Vector<Color> col;
- col.push_back(p_color);
+ Vector<Color> col = { p_color };
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
+
if (is_collision_outline_enabled()) {
+ points.push_back(points[0]);
RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
- // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
}
}
diff --git a/scene/resources/circle_shape_2d.h b/scene/resources/circle_shape_2d.h
index 333f299236..62a907387f 100644
--- a/scene/resources/circle_shape_2d.h
+++ b/scene/resources/circle_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp
index 0c767c8a52..b188a6164d 100644
--- a/scene/resources/concave_polygon_shape_2d.cpp
+++ b/scene/resources/concave_polygon_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h
index 98ae341e97..0f49a0d80f 100644
--- a/scene/resources/concave_polygon_shape_2d.h
+++ b/scene/resources/concave_polygon_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp
index 03854683bd..ab2c1da327 100644
--- a/scene/resources/concave_polygon_shape_3d.cpp
+++ b/scene/resources/concave_polygon_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#include "servers/physics_server_3d.h"
Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const {
- Set<DrawEdge> edges;
+ RBSet<DrawEdge> edges;
int index_count = faces.size();
ERR_FAIL_COND_V((index_count % 3) != 0, Vector<Vector3>());
@@ -50,7 +50,7 @@ Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const {
Vector<Vector3> points;
points.resize(edges.size() * 2);
int idx = 0;
- for (Set<DrawEdge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<DrawEdge>::Element *E = edges.front(); E; E = E->next()) {
points.write[idx + 0] = E->get().a;
points.write[idx + 1] = E->get().b;
idx += 2;
diff --git a/scene/resources/concave_polygon_shape_3d.h b/scene/resources/concave_polygon_shape_3d.h
index bb28359dcc..5337deb5fb 100644
--- a/scene/resources/concave_polygon_shape_3d.h
+++ b/scene/resources/concave_polygon_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp
index ac31315858..c416a03f38 100644
--- a/scene/resources/convex_polygon_shape_2d.cpp
+++ b/scene/resources/convex_polygon_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -82,7 +82,7 @@ void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) {
if (is_collision_outline_enabled()) {
RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
// Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
+ RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color, 1.0, true);
}
}
diff --git a/scene/resources/convex_polygon_shape_2d.h b/scene/resources/convex_polygon_shape_2d.h
index 1813b608bd..4896d3ec66 100644
--- a/scene/resources/convex_polygon_shape_2d.h
+++ b/scene/resources/convex_polygon_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp
index 6b895da606..e7960f1ba4 100644
--- a/scene/resources/convex_polygon_shape_3d.cpp
+++ b/scene/resources/convex_polygon_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/convex_polygon_shape_3d.h b/scene/resources/convex_polygon_shape_3d.h
index edd127c8f4..930edb015d 100644
--- a/scene/resources/convex_polygon_shape_3d.h
+++ b/scene/resources/convex_polygon_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index b530a72033..9ea7bb464a 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,15 +33,15 @@
#include "core/core_string_names.h"
template <class T>
-static _FORCE_INLINE_ T _bezier_interp(real_t t, T start, T control_1, T control_2, T end) {
+static _FORCE_INLINE_ T _bezier_interp(real_t p_t, T p_start, T p_control_1, T p_control_2, T p_end) {
/* Formula from Wikipedia article on Bezier curves. */
- real_t omt = (1.0 - t);
+ real_t omt = (1.0 - p_t);
real_t omt2 = omt * omt;
real_t omt3 = omt2 * omt;
- real_t t2 = t * t;
- real_t t3 = t2 * t;
+ real_t t2 = p_t * p_t;
+ real_t t3 = t2 * p_t;
- return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
+ return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
}
const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed";
@@ -49,46 +49,46 @@ const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed";
Curve::Curve() {
}
-int Curve::add_point(Vector2 p_pos, real_t left_tangent, real_t right_tangent, TangentMode left_mode, TangentMode right_mode) {
+int Curve::add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) {
// Add a point and preserve order
// Curve bounds is in 0..1
- if (p_pos.x > MAX_X) {
- p_pos.x = MAX_X;
- } else if (p_pos.x < MIN_X) {
- p_pos.x = MIN_X;
+ if (p_position.x > MAX_X) {
+ p_position.x = MAX_X;
+ } else if (p_position.x < MIN_X) {
+ p_position.x = MIN_X;
}
int ret = -1;
if (_points.size() == 0) {
- _points.push_back(Point(p_pos, left_tangent, right_tangent, left_mode, right_mode));
+ _points.push_back(Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
ret = 0;
} else if (_points.size() == 1) {
// TODO Is the `else` able to handle this block already?
- real_t diff = p_pos.x - _points[0].pos.x;
+ real_t diff = p_position.x - _points[0].position.x;
if (diff > 0) {
- _points.push_back(Point(p_pos, left_tangent, right_tangent, left_mode, right_mode));
+ _points.push_back(Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
ret = 1;
} else {
- _points.insert(0, Point(p_pos, left_tangent, right_tangent, left_mode, right_mode));
+ _points.insert(0, Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
ret = 0;
}
} else {
- int i = get_index(p_pos.x);
+ int i = get_index(p_position.x);
- if (i == 0 && p_pos.x < _points[0].pos.x) {
+ if (i == 0 && p_position.x < _points[0].position.x) {
// Insert before anything else
- _points.insert(0, Point(p_pos, left_tangent, right_tangent, left_mode, right_mode));
+ _points.insert(0, Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
ret = 0;
} else {
// Insert between i and i+1
++i;
- _points.insert(i, Point(p_pos, left_tangent, right_tangent, left_mode, right_mode));
+ _points.insert(i, Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
ret = i;
}
}
@@ -100,7 +100,7 @@ int Curve::add_point(Vector2 p_pos, real_t left_tangent, real_t right_tangent, T
return ret;
}
-int Curve::get_index(real_t offset) const {
+int Curve::get_index(real_t p_offset) const {
// Lower-bound float binary search
int imin = 0;
@@ -109,13 +109,13 @@ int Curve::get_index(real_t offset) const {
while (imax - imin > 1) {
int m = (imin + imax) / 2;
- real_t a = _points[m].pos.x;
- real_t b = _points[m + 1].pos.x;
+ real_t a = _points[m].position.x;
+ real_t b = _points[m + 1].position.x;
- if (a < offset && b < offset) {
+ if (a < p_offset && b < p_offset) {
imin = m;
- } else if (a > offset) {
+ } else if (a > p_offset) {
imax = m;
} else {
@@ -124,7 +124,7 @@ int Curve::get_index(real_t offset) const {
}
// Will happen if the offset is out of bounds
- if (offset > _points[imax].pos.x) {
+ if (p_offset > _points[imax].position.x) {
return imax;
}
return imin;
@@ -134,9 +134,9 @@ void Curve::clean_dupes() {
bool dirty = false;
for (int i = 1; i < _points.size(); ++i) {
- real_t diff = _points[i - 1].pos.x - _points[i].pos.x;
+ real_t diff = _points[i - 1].position.x - _points[i].position.x;
if (diff <= CMP_EPSILON) {
- _points.remove(i);
+ _points.remove_at(i);
--i;
dirty = true;
}
@@ -147,67 +147,67 @@ void Curve::clean_dupes() {
}
}
-void Curve::set_point_left_tangent(int i, real_t tangent) {
- ERR_FAIL_INDEX(i, _points.size());
- _points.write[i].left_tangent = tangent;
- _points.write[i].left_mode = TANGENT_FREE;
+void Curve::set_point_left_tangent(int p_index, real_t p_tangent) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points.write[p_index].left_tangent = p_tangent;
+ _points.write[p_index].left_mode = TANGENT_FREE;
mark_dirty();
}
-void Curve::set_point_right_tangent(int i, real_t tangent) {
- ERR_FAIL_INDEX(i, _points.size());
- _points.write[i].right_tangent = tangent;
- _points.write[i].right_mode = TANGENT_FREE;
+void Curve::set_point_right_tangent(int p_index, real_t p_tangent) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points.write[p_index].right_tangent = p_tangent;
+ _points.write[p_index].right_mode = TANGENT_FREE;
mark_dirty();
}
-void Curve::set_point_left_mode(int i, TangentMode p_mode) {
- ERR_FAIL_INDEX(i, _points.size());
- _points.write[i].left_mode = p_mode;
- if (i > 0) {
+void Curve::set_point_left_mode(int p_index, TangentMode p_mode) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points.write[p_index].left_mode = p_mode;
+ if (p_index > 0) {
if (p_mode == TANGENT_LINEAR) {
- Vector2 v = (_points[i - 1].pos - _points[i].pos).normalized();
- _points.write[i].left_tangent = v.y / v.x;
+ Vector2 v = (_points[p_index - 1].position - _points[p_index].position).normalized();
+ _points.write[p_index].left_tangent = v.y / v.x;
}
}
mark_dirty();
}
-void Curve::set_point_right_mode(int i, TangentMode p_mode) {
- ERR_FAIL_INDEX(i, _points.size());
- _points.write[i].right_mode = p_mode;
- if (i + 1 < _points.size()) {
+void Curve::set_point_right_mode(int p_index, TangentMode p_mode) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points.write[p_index].right_mode = p_mode;
+ if (p_index + 1 < _points.size()) {
if (p_mode == TANGENT_LINEAR) {
- Vector2 v = (_points[i + 1].pos - _points[i].pos).normalized();
- _points.write[i].right_tangent = v.y / v.x;
+ Vector2 v = (_points[p_index + 1].position - _points[p_index].position).normalized();
+ _points.write[p_index].right_tangent = v.y / v.x;
}
}
mark_dirty();
}
-real_t Curve::get_point_left_tangent(int i) const {
- ERR_FAIL_INDEX_V(i, _points.size(), 0);
- return _points[i].left_tangent;
+real_t Curve::get_point_left_tangent(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), 0);
+ return _points[p_index].left_tangent;
}
-real_t Curve::get_point_right_tangent(int i) const {
- ERR_FAIL_INDEX_V(i, _points.size(), 0);
- return _points[i].right_tangent;
+real_t Curve::get_point_right_tangent(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), 0);
+ return _points[p_index].right_tangent;
}
-Curve::TangentMode Curve::get_point_left_mode(int i) const {
- ERR_FAIL_INDEX_V(i, _points.size(), TANGENT_FREE);
- return _points[i].left_mode;
+Curve::TangentMode Curve::get_point_left_mode(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), TANGENT_FREE);
+ return _points[p_index].left_mode;
}
-Curve::TangentMode Curve::get_point_right_mode(int i) const {
- ERR_FAIL_INDEX_V(i, _points.size(), TANGENT_FREE);
- return _points[i].right_mode;
+Curve::TangentMode Curve::get_point_right_mode(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), TANGENT_FREE);
+ return _points[p_index].right_mode;
}
void Curve::remove_point(int p_index) {
ERR_FAIL_INDEX(p_index, _points.size());
- _points.remove(p_index);
+ _points.remove_at(p_index);
mark_dirty();
}
@@ -216,18 +216,18 @@ void Curve::clear_points() {
mark_dirty();
}
-void Curve::set_point_value(int p_index, real_t pos) {
+void Curve::set_point_value(int p_index, real_t p_position) {
ERR_FAIL_INDEX(p_index, _points.size());
- _points.write[p_index].pos.y = pos;
+ _points.write[p_index].position.y = p_position;
update_auto_tangents(p_index);
mark_dirty();
}
-int Curve::set_point_offset(int p_index, float offset) {
+int Curve::set_point_offset(int p_index, real_t p_offset) {
ERR_FAIL_INDEX_V(p_index, _points.size(), -1);
Point p = _points[p_index];
remove_point(p_index);
- int i = add_point(Vector2(offset, p.pos.y));
+ int i = add_point(Vector2(p_offset, p.position.y));
_points.write[i].left_tangent = p.left_tangent;
_points.write[i].right_tangent = p.right_tangent;
_points.write[i].left_mode = p.left_mode;
@@ -241,7 +241,7 @@ int Curve::set_point_offset(int p_index, float offset) {
Vector2 Curve::get_point_position(int p_index) const {
ERR_FAIL_INDEX_V(p_index, _points.size(), Vector2(0, 0));
- return _points[p_index].pos;
+ return _points[p_index].position;
}
Curve::Point Curve::get_point(int p_index) const {
@@ -249,35 +249,35 @@ Curve::Point Curve::get_point(int p_index) const {
return _points[p_index];
}
-void Curve::update_auto_tangents(int i) {
- Point &p = _points.write[i];
+void Curve::update_auto_tangents(int p_index) {
+ Point &p = _points.write[p_index];
- if (i > 0) {
+ if (p_index > 0) {
if (p.left_mode == TANGENT_LINEAR) {
- Vector2 v = (_points[i - 1].pos - p.pos).normalized();
+ Vector2 v = (_points[p_index - 1].position - p.position).normalized();
p.left_tangent = v.y / v.x;
}
- if (_points[i - 1].right_mode == TANGENT_LINEAR) {
- Vector2 v = (_points[i - 1].pos - p.pos).normalized();
- _points.write[i - 1].right_tangent = v.y / v.x;
+ if (_points[p_index - 1].right_mode == TANGENT_LINEAR) {
+ Vector2 v = (_points[p_index - 1].position - p.position).normalized();
+ _points.write[p_index - 1].right_tangent = v.y / v.x;
}
}
- if (i + 1 < _points.size()) {
+ if (p_index + 1 < _points.size()) {
if (p.right_mode == TANGENT_LINEAR) {
- Vector2 v = (_points[i + 1].pos - p.pos).normalized();
+ Vector2 v = (_points[p_index + 1].position - p.position).normalized();
p.right_tangent = v.y / v.x;
}
- if (_points[i + 1].left_mode == TANGENT_LINEAR) {
- Vector2 v = (_points[i + 1].pos - p.pos).normalized();
- _points.write[i + 1].left_tangent = v.y / v.x;
+ if (_points[p_index + 1].left_mode == TANGENT_LINEAR) {
+ Vector2 v = (_points[p_index + 1].position - p.position).normalized();
+ _points.write[p_index + 1].left_tangent = v.y / v.x;
}
}
}
#define MIN_Y_RANGE 0.01
-void Curve::set_min_value(float p_min) {
+void Curve::set_min_value(real_t p_min) {
if (_minmax_set_once & 0b11 && p_min > _max_value - MIN_Y_RANGE) {
_min_value = _max_value - MIN_Y_RANGE;
} else {
@@ -289,7 +289,7 @@ void Curve::set_min_value(float p_min) {
emit_signal(SNAME(SIGNAL_RANGE_CHANGED));
}
-void Curve::set_max_value(float p_max) {
+void Curve::set_max_value(real_t p_max) {
if (_minmax_set_once & 0b11 && p_max < _min_value + MIN_Y_RANGE) {
_max_value = _min_value + MIN_Y_RANGE;
} else {
@@ -299,32 +299,32 @@ void Curve::set_max_value(float p_max) {
emit_signal(SNAME(SIGNAL_RANGE_CHANGED));
}
-real_t Curve::interpolate(real_t offset) const {
+real_t Curve::interpolate(real_t p_offset) const {
if (_points.size() == 0) {
return 0;
}
if (_points.size() == 1) {
- return _points[0].pos.y;
+ return _points[0].position.y;
}
- int i = get_index(offset);
+ int i = get_index(p_offset);
if (i == _points.size() - 1) {
- return _points[i].pos.y;
+ return _points[i].position.y;
}
- real_t local = offset - _points[i].pos.x;
+ real_t local = p_offset - _points[i].position.x;
if (i == 0 && local <= 0) {
- return _points[0].pos.y;
+ return _points[0].position.y;
}
return interpolate_local_nocheck(i, local);
}
-real_t Curve::interpolate_local_nocheck(int index, real_t local_offset) const {
- const Point a = _points[index];
- const Point b = _points[index + 1];
+real_t Curve::interpolate_local_nocheck(int p_index, real_t p_local_offset) const {
+ const Point a = _points[p_index];
+ const Point b = _points[p_index + 1];
/* Cubic bezier
*
@@ -341,16 +341,16 @@ real_t Curve::interpolate_local_nocheck(int index, real_t local_offset) const {
*/
// Control points are chosen at equal distances
- real_t d = b.pos.x - a.pos.x;
- if (Math::abs(d) <= CMP_EPSILON) {
- return b.pos.y;
+ real_t d = b.position.x - a.position.x;
+ if (Math::is_zero_approx(d)) {
+ return b.position.y;
}
- local_offset /= d;
+ p_local_offset /= d;
d /= 3.0;
- real_t yac = a.pos.y + d * a.right_tangent;
- real_t ybc = b.pos.y - d * b.left_tangent;
+ real_t yac = a.position.y + d * a.right_tangent;
+ real_t ybc = b.position.y - d * b.left_tangent;
- real_t y = _bezier_interp(local_offset, a.pos.y, yac, ybc, b.pos.y);
+ real_t y = _bezier_interp(p_local_offset, a.position.y, yac, ybc, b.position.y);
return y;
}
@@ -369,7 +369,7 @@ Array Curve::get_data() const {
const Point p = _points[j];
int i = j * ELEMS;
- output[i] = p.pos;
+ output[i] = p.position;
output[i + 1] = p.left_tangent;
output[i + 2] = p.right_tangent;
output[i + 3] = p.left_mode;
@@ -379,39 +379,39 @@ Array Curve::get_data() const {
return output;
}
-void Curve::set_data(Array input) {
+void Curve::set_data(const Array p_input) {
const unsigned int ELEMS = 5;
- ERR_FAIL_COND(input.size() % ELEMS != 0);
+ ERR_FAIL_COND(p_input.size() % ELEMS != 0);
_points.clear();
// Validate input
- for (int i = 0; i < input.size(); i += ELEMS) {
- ERR_FAIL_COND(input[i].get_type() != Variant::VECTOR2);
- ERR_FAIL_COND(!input[i + 1].is_num());
- ERR_FAIL_COND(input[i + 2].get_type() != Variant::FLOAT);
+ for (int i = 0; i < p_input.size(); i += ELEMS) {
+ ERR_FAIL_COND(p_input[i].get_type() != Variant::VECTOR2);
+ ERR_FAIL_COND(!p_input[i + 1].is_num());
+ ERR_FAIL_COND(p_input[i + 2].get_type() != Variant::FLOAT);
- ERR_FAIL_COND(input[i + 3].get_type() != Variant::INT);
- int left_mode = input[i + 3];
+ ERR_FAIL_COND(p_input[i + 3].get_type() != Variant::INT);
+ int left_mode = p_input[i + 3];
ERR_FAIL_COND(left_mode < 0 || left_mode >= TANGENT_MODE_COUNT);
- ERR_FAIL_COND(input[i + 4].get_type() != Variant::INT);
- int right_mode = input[i + 4];
+ ERR_FAIL_COND(p_input[i + 4].get_type() != Variant::INT);
+ int right_mode = p_input[i + 4];
ERR_FAIL_COND(right_mode < 0 || right_mode >= TANGENT_MODE_COUNT);
}
- _points.resize(input.size() / ELEMS);
+ _points.resize(p_input.size() / ELEMS);
for (int j = 0; j < _points.size(); ++j) {
Point &p = _points.write[j];
int i = j * ELEMS;
- p.pos = input[i];
- p.left_tangent = input[i + 1];
- p.right_tangent = input[i + 2];
+ p.position = p_input[i];
+ p.left_tangent = p_input[i + 1];
+ p.right_tangent = p_input[i + 2];
// TODO For some reason the compiler won't convert from Variant to enum
- int left_mode = input[i + 3];
- int right_mode = input[i + 4];
+ int left_mode = p_input[i + 3];
+ int right_mode = p_input[i + 4];
p.left_mode = (TangentMode)left_mode;
p.right_mode = (TangentMode)right_mode;
}
@@ -431,8 +431,8 @@ void Curve::bake() {
}
if (_points.size() != 0) {
- _baked_cache.write[0] = _points[0].pos.y;
- _baked_cache.write[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y;
+ _baked_cache.write[0] = _points[0].position.y;
+ _baked_cache.write[_baked_cache.size() - 1] = _points[_points.size() - 1].position.y;
}
_baked_cache_dirty = false;
@@ -445,7 +445,7 @@ void Curve::set_bake_resolution(int p_resolution) {
_baked_cache_dirty = true;
}
-real_t Curve::interpolate_baked(real_t offset) const {
+real_t Curve::interpolate_baked(real_t p_offset) const {
if (_baked_cache_dirty) {
// Last-second bake if not done already
const_cast<Curve *>(this)->bake();
@@ -456,13 +456,13 @@ real_t Curve::interpolate_baked(real_t offset) const {
if (_points.size() == 0) {
return 0;
}
- return _points[0].pos.y;
+ return _points[0].position.y;
} else if (_baked_cache.size() == 1) {
return _baked_cache[0];
}
// Get interpolation index
- real_t fi = offset * _baked_cache.size();
+ real_t fi = p_offset * _baked_cache.size();
int i = Math::floor(fi);
if (i < 0) {
i = 0;
@@ -481,7 +481,7 @@ real_t Curve::interpolate_baked(real_t offset) const {
}
}
-void Curve::ensure_default_setup(float p_min, float p_max) {
+void Curve::ensure_default_setup(real_t p_min, real_t p_max) {
if (_points.size() == 0 && _min_value == 0 && _max_value == 1) {
add_point(Vector2(0, 1));
add_point(Vector2(1, 1));
@@ -535,9 +535,9 @@ int Curve2D::get_point_count() const {
return points.size();
}
-void Curve2D::add_point(const Vector2 &p_pos, const Vector2 &p_in, const Vector2 &p_out, int p_atpos) {
+void Curve2D::add_point(const Vector2 &p_position, const Vector2 &p_in, const Vector2 &p_out, int p_atpos) {
Point n;
- n.pos = p_pos;
+ n.position = p_position;
n.in = p_in;
n.out = p_out;
if (p_atpos >= 0 && p_atpos < points.size()) {
@@ -550,17 +550,17 @@ void Curve2D::add_point(const Vector2 &p_pos, const Vector2 &p_in, const Vector2
emit_signal(CoreStringNames::get_singleton()->changed);
}
-void Curve2D::set_point_position(int p_index, const Vector2 &p_pos) {
+void Curve2D::set_point_position(int p_index, const Vector2 &p_position) {
ERR_FAIL_INDEX(p_index, points.size());
- points.write[p_index].pos = p_pos;
+ points.write[p_index].position = p_position;
baked_cache_dirty = true;
emit_signal(CoreStringNames::get_singleton()->changed);
}
Vector2 Curve2D::get_point_position(int p_index) const {
ERR_FAIL_INDEX_V(p_index, points.size(), Vector2());
- return points[p_index].pos;
+ return points[p_index].position;
}
void Curve2D::set_point_in(int p_index, const Vector2 &p_in) {
@@ -591,7 +591,7 @@ Vector2 Curve2D::get_point_out(int p_index) const {
void Curve2D::remove_point(int p_index) {
ERR_FAIL_INDEX(p_index, points.size());
- points.remove(p_index);
+ points.remove_at(p_index);
baked_cache_dirty = true;
emit_signal(CoreStringNames::get_singleton()->changed);
}
@@ -604,19 +604,19 @@ void Curve2D::clear_points() {
}
}
-Vector2 Curve2D::interpolate(int p_index, float p_offset) const {
+Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const {
int pc = points.size();
ERR_FAIL_COND_V(pc == 0, Vector2());
if (p_index >= pc - 1) {
- return points[pc - 1].pos;
+ return points[pc - 1].position;
} else if (p_index < 0) {
- return points[0].pos;
+ return points[0].position;
}
- Vector2 p0 = points[p_index].pos;
+ Vector2 p0 = points[p_index].position;
Vector2 p1 = p0 + points[p_index].out;
- Vector2 p3 = points[p_index + 1].pos;
+ Vector2 p3 = points[p_index + 1].position;
Vector2 p2 = p3 + points[p_index + 1].in;
return _bezier_interp(p_offset, p0, p1, p2, p3);
@@ -632,15 +632,15 @@ Vector2 Curve2D::interpolatef(real_t p_findex) const {
return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0));
}
-void Curve2D::_bake_segment2d(Map<float, Vector2> &r_bake, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, float p_tol) const {
- float mp = p_begin + (p_end - p_begin) * 0.5;
+void Curve2D::_bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const {
+ real_t mp = p_begin + (p_end - p_begin) * 0.5;
Vector2 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
Vector2 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b);
Vector2 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b);
Vector2 na = (mid - beg).normalized();
Vector2 nb = (end - mid).normalized();
- float dp = na.dot(nb);
+ real_t dp = na.dot(nb);
if (dp < Math::cos(Math::deg2rad(p_tol))) {
r_bake[mp] = mid;
@@ -661,54 +661,55 @@ void Curve2D::_bake() const {
baked_cache_dirty = false;
if (points.size() == 0) {
- baked_point_cache.resize(0);
- baked_dist_cache.resize(0);
+ baked_point_cache.clear();
+ baked_dist_cache.clear();
return;
}
if (points.size() == 1) {
baked_point_cache.resize(1);
- baked_point_cache.set(0, points[0].pos);
+ baked_point_cache.set(0, points[0].position);
baked_dist_cache.resize(1);
baked_dist_cache.set(0, 0.0);
return;
}
- Vector2 pos = points[0].pos;
- float dist = 0.0;
+ Vector2 position = points[0].position;
+ real_t dist = 0.0;
List<Vector2> pointlist;
- List<float> distlist;
+ List<real_t> distlist;
- pointlist.push_back(pos); //start always from origin
+ // Start always from origin.
+ pointlist.push_back(position);
distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
- float step = 0.1; // at least 10 substeps ought to be enough?
- float p = 0.0;
+ real_t step = 0.1; // at least 10 substeps ought to be enough?
+ real_t p = 0.0;
while (p < 1.0) {
- float np = p + step;
+ real_t np = p + step;
if (np > 1.0) {
np = 1.0;
}
- Vector2 npp = _bezier_interp(np, points[i].pos, points[i].pos + points[i].out, points[i + 1].pos + points[i + 1].in, points[i + 1].pos);
- float d = pos.distance_to(npp);
+ Vector2 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
+ real_t d = position.distance_to(npp);
if (d > bake_interval) {
// OK! between P and NP there _has_ to be Something, let's go searching!
int iterations = 10; //lots of detail!
- float low = p;
- float hi = np;
- float mid = low + (hi - low) * 0.5;
+ real_t low = p;
+ real_t hi = np;
+ real_t mid = low + (hi - low) * 0.5;
for (int j = 0; j < iterations; j++) {
- npp = _bezier_interp(mid, points[i].pos, points[i].pos + points[i].out, points[i + 1].pos + points[i + 1].in, points[i + 1].pos);
- d = pos.distance_to(npp);
+ npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
+ d = position.distance_to(npp);
if (bake_interval < d) {
hi = mid;
@@ -718,31 +719,34 @@ void Curve2D::_bake() const {
mid = low + (hi - low) * 0.5;
}
- pos = npp;
+ position = npp;
p = mid;
dist += d;
- pointlist.push_back(pos);
+ pointlist.push_back(position);
distlist.push_back(dist);
} else {
p = np;
}
}
- }
- Vector2 lastpos = points[points.size() - 1].pos;
+ Vector2 npp = points[i + 1].position;
+ real_t d = position.distance_to(npp);
+
+ position = npp;
+ dist += d;
+
+ pointlist.push_back(position);
+ distlist.push_back(dist);
+ }
- float rem = pos.distance_to(lastpos);
- dist += rem;
baked_max_ofs = dist;
- pointlist.push_back(lastpos);
- distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
baked_dist_cache.resize(distlist.size());
Vector2 *w = baked_point_cache.ptrw();
- float *wd = baked_dist_cache.ptrw();
+ real_t *wd = baked_dist_cache.ptrw();
for (int i = 0; i < pointlist.size(); i++) {
w[i] = pointlist[i];
@@ -750,7 +754,7 @@ void Curve2D::_bake() const {
}
}
-float Curve2D::get_baked_length() const {
+real_t Curve2D::get_baked_length() const {
if (baked_cache_dirty) {
_bake();
}
@@ -758,12 +762,12 @@ float Curve2D::get_baked_length() const {
return baked_max_ofs;
}
-Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const {
+Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const {
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D.");
@@ -771,20 +775,21 @@ Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const {
return baked_point_cache.get(0);
}
- int bpc = baked_point_cache.size();
const Vector2 *r = baked_point_cache.ptr();
if (p_offset < 0) {
return r[0];
}
if (p_offset >= baked_max_ofs) {
- return r[bpc - 1];
+ return r[pc - 1];
}
- int start = 0, end = bpc, idx = (end + start) / 2;
- // binary search to find baked points
+ int start = 0;
+ int end = pc;
+ int idx = (end + start) / 2;
+ // Binary search to find baked points.
while (start < idx) {
- float offset = baked_dist_cache[idx];
+ real_t offset = baked_dist_cache[idx];
if (p_offset <= offset) {
end = idx;
} else {
@@ -793,17 +798,17 @@ Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const {
idx = (end + start) / 2;
}
- float offset_begin = baked_dist_cache[idx];
- float offset_end = baked_dist_cache[idx + 1];
+ real_t offset_begin = baked_dist_cache[idx];
+ real_t offset_end = baked_dist_cache[idx + 1];
- float idx_interval = offset_end - offset_begin;
+ real_t idx_interval = offset_end - offset_begin;
ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "failed to find baked segment");
- float frac = (p_offset - offset_begin) / idx_interval;
+ real_t frac = (p_offset - offset_begin) / idx_interval;
if (p_cubic) {
Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
- Vector2 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
+ Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
} else {
return r[idx].lerp(r[idx + 1], frac);
@@ -818,24 +823,24 @@ PackedVector2Array Curve2D::get_baked_points() const {
return baked_point_cache;
}
-void Curve2D::set_bake_interval(float p_tolerance) {
+void Curve2D::set_bake_interval(real_t p_tolerance) {
bake_interval = p_tolerance;
baked_cache_dirty = true;
emit_signal(CoreStringNames::get_singleton()->changed);
}
-float Curve2D::get_bake_interval() const {
+real_t Curve2D::get_bake_interval() const {
return bake_interval;
}
Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
- // Brute force method
+ // Brute force method.
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D.");
@@ -846,16 +851,16 @@ Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
const Vector2 *r = baked_point_cache.ptr();
Vector2 nearest;
- float nearest_dist = -1.0f;
+ real_t nearest_dist = -1.0f;
for (int i = 0; i < pc - 1; i++) {
Vector2 origin = r[i];
Vector2 direction = (r[i + 1] - origin) / bake_interval;
- float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
Vector2 proj = origin + direction * d;
- float dist = proj.distance_squared_to(p_to_point);
+ real_t dist = proj.distance_squared_to(p_to_point);
if (nearest_dist < 0.0f || dist < nearest_dist) {
nearest = proj;
@@ -866,14 +871,14 @@ Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
return nearest;
}
-float Curve2D::get_closest_offset(const Vector2 &p_to_point) const {
- // Brute force method
+real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const {
+ // Brute force method.
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve2D.");
@@ -883,18 +888,18 @@ float Curve2D::get_closest_offset(const Vector2 &p_to_point) const {
const Vector2 *r = baked_point_cache.ptr();
- float nearest = 0.0f;
- float nearest_dist = -1.0f;
- float offset = 0.0f;
+ real_t nearest = 0.0f;
+ real_t nearest_dist = -1.0f;
+ real_t offset = 0.0f;
for (int i = 0; i < pc - 1; i++) {
Vector2 origin = r[i];
Vector2 direction = (r[i + 1] - origin) / bake_interval;
- float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
Vector2 proj = origin + direction * d;
- float dist = proj.distance_squared_to(p_to_point);
+ real_t dist = proj.distance_squared_to(p_to_point);
if (nearest_dist < 0.0f || dist < nearest_dist) {
nearest = offset + d;
@@ -917,7 +922,7 @@ Dictionary Curve2D::_get_data() const {
for (int i = 0; i < points.size(); i++) {
w[i * 3 + 0] = points[i].in;
w[i * 3 + 1] = points[i].out;
- w[i * 3 + 2] = points[i].pos;
+ w[i * 3 + 2] = points[i].position;
}
dc["points"] = d;
@@ -937,42 +942,44 @@ void Curve2D::_set_data(const Dictionary &p_data) {
for (int i = 0; i < points.size(); i++) {
points.write[i].in = r[i * 3 + 0];
points.write[i].out = r[i * 3 + 1];
- points.write[i].pos = r[i * 3 + 2];
+ points.write[i].position = r[i * 3 + 2];
}
baked_cache_dirty = true;
}
-PackedVector2Array Curve2D::tessellate(int p_max_stages, float p_tolerance) const {
+PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) const {
PackedVector2Array tess;
if (points.size() == 0) {
return tess;
}
- Vector<Map<float, Vector2>> midpoints;
+
+ // The current implementation requires a sorted map.
+ Vector<RBMap<real_t, Vector2>> midpoints;
midpoints.resize(points.size() - 1);
int pc = 1;
for (int i = 0; i < points.size() - 1; i++) {
- _bake_segment2d(midpoints.write[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance);
+ _bake_segment2d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_tolerance);
pc++;
pc += midpoints[i].size();
}
tess.resize(pc);
Vector2 *bpw = tess.ptrw();
- bpw[0] = points[0].pos;
+ bpw[0] = points[0].position;
int pidx = 0;
for (int i = 0; i < points.size() - 1; i++) {
- for (const KeyValue<float, Vector2> &E : midpoints[i]) {
+ for (const KeyValue<real_t, Vector2> &E : midpoints[i]) {
pidx++;
bpw[pidx] = E.value;
}
pidx++;
- bpw[pidx] = points[i + 1].pos;
+ bpw[pidx] = points[i + 1].position;
}
return tess;
@@ -1009,11 +1016,7 @@ void Curve2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
-Curve2D::Curve2D() {
- /* add_point(Vector2(-1,0,0));
- add_point(Vector2(0,2,0));
- add_point(Vector2(0,3,5));*/
-}
+Curve2D::Curve2D() {}
/***********************************************************************************/
/***********************************************************************************/
@@ -1026,9 +1029,9 @@ int Curve3D::get_point_count() const {
return points.size();
}
-void Curve3D::add_point(const Vector3 &p_pos, const Vector3 &p_in, const Vector3 &p_out, int p_atpos) {
+void Curve3D::add_point(const Vector3 &p_position, const Vector3 &p_in, const Vector3 &p_out, int p_atpos) {
Point n;
- n.pos = p_pos;
+ n.position = p_position;
n.in = p_in;
n.out = p_out;
if (p_atpos >= 0 && p_atpos < points.size()) {
@@ -1041,20 +1044,20 @@ void Curve3D::add_point(const Vector3 &p_pos, const Vector3 &p_in, const Vector3
emit_signal(CoreStringNames::get_singleton()->changed);
}
-void Curve3D::set_point_position(int p_index, const Vector3 &p_pos) {
+void Curve3D::set_point_position(int p_index, const Vector3 &p_position) {
ERR_FAIL_INDEX(p_index, points.size());
- points.write[p_index].pos = p_pos;
+ points.write[p_index].position = p_position;
baked_cache_dirty = true;
emit_signal(CoreStringNames::get_singleton()->changed);
}
Vector3 Curve3D::get_point_position(int p_index) const {
ERR_FAIL_INDEX_V(p_index, points.size(), Vector3());
- return points[p_index].pos;
+ return points[p_index].position;
}
-void Curve3D::set_point_tilt(int p_index, float p_tilt) {
+void Curve3D::set_point_tilt(int p_index, real_t p_tilt) {
ERR_FAIL_INDEX(p_index, points.size());
points.write[p_index].tilt = p_tilt;
@@ -1062,7 +1065,7 @@ void Curve3D::set_point_tilt(int p_index, float p_tilt) {
emit_signal(CoreStringNames::get_singleton()->changed);
}
-float Curve3D::get_point_tilt(int p_index) const {
+real_t Curve3D::get_point_tilt(int p_index) const {
ERR_FAIL_INDEX_V(p_index, points.size(), 0);
return points[p_index].tilt;
}
@@ -1095,7 +1098,7 @@ Vector3 Curve3D::get_point_out(int p_index) const {
void Curve3D::remove_point(int p_index) {
ERR_FAIL_INDEX(p_index, points.size());
- points.remove(p_index);
+ points.remove_at(p_index);
baked_cache_dirty = true;
emit_signal(CoreStringNames::get_singleton()->changed);
}
@@ -1108,19 +1111,19 @@ void Curve3D::clear_points() {
}
}
-Vector3 Curve3D::interpolate(int p_index, float p_offset) const {
+Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const {
int pc = points.size();
ERR_FAIL_COND_V(pc == 0, Vector3());
if (p_index >= pc - 1) {
- return points[pc - 1].pos;
+ return points[pc - 1].position;
} else if (p_index < 0) {
- return points[0].pos;
+ return points[0].position;
}
- Vector3 p0 = points[p_index].pos;
+ Vector3 p0 = points[p_index].position;
Vector3 p1 = p0 + points[p_index].out;
- Vector3 p3 = points[p_index + 1].pos;
+ Vector3 p3 = points[p_index + 1].position;
Vector3 p2 = p3 + points[p_index + 1].in;
return _bezier_interp(p_offset, p0, p1, p2, p3);
@@ -1136,15 +1139,15 @@ Vector3 Curve3D::interpolatef(real_t p_findex) const {
return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0));
}
-void Curve3D::_bake_segment3d(Map<float, Vector3> &r_bake, float p_begin, float p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, float p_tol) const {
- float mp = p_begin + (p_end - p_begin) * 0.5;
+void Curve3D::_bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const {
+ real_t mp = p_begin + (p_end - p_begin) * 0.5;
Vector3 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
Vector3 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b);
Vector3 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b);
Vector3 na = (mid - beg).normalized();
Vector3 nb = (end - mid).normalized();
- float dp = na.dot(nb);
+ real_t dp = na.dot(nb);
if (dp < Math::cos(Math::deg2rad(p_tol))) {
r_bake[mp] = mid;
@@ -1164,16 +1167,16 @@ void Curve3D::_bake() const {
baked_cache_dirty = false;
if (points.size() == 0) {
- baked_point_cache.resize(0);
- baked_tilt_cache.resize(0);
- baked_up_vector_cache.resize(0);
- baked_dist_cache.resize(0);
+ baked_point_cache.clear();
+ baked_tilt_cache.clear();
+ baked_up_vector_cache.clear();
+ baked_dist_cache.clear();
return;
}
if (points.size() == 1) {
baked_point_cache.resize(1);
- baked_point_cache.set(0, points[0].pos);
+ baked_point_cache.set(0, points[0].position);
baked_tilt_cache.resize(1);
baked_tilt_cache.set(0, points[0].tilt);
baked_dist_cache.resize(1);
@@ -1183,45 +1186,46 @@ void Curve3D::_bake() const {
baked_up_vector_cache.resize(1);
baked_up_vector_cache.set(0, Vector3(0, 1, 0));
} else {
- baked_up_vector_cache.resize(0);
+ baked_up_vector_cache.clear();
}
return;
}
- Vector3 pos = points[0].pos;
- float dist = 0.0;
+ Vector3 position = points[0].position;
+ real_t dist = 0.0;
List<Plane> pointlist;
- List<float> distlist;
+ List<real_t> distlist;
- pointlist.push_back(Plane(pos, points[0].tilt));
+ // Start always from origin.
+ pointlist.push_back(Plane(position, points[0].tilt));
distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
- float step = 0.1; // at least 10 substeps ought to be enough?
- float p = 0.0;
+ real_t step = 0.1; // at least 10 substeps ought to be enough?
+ real_t p = 0.0;
while (p < 1.0) {
- float np = p + step;
+ real_t np = p + step;
if (np > 1.0) {
np = 1.0;
}
- Vector3 npp = _bezier_interp(np, points[i].pos, points[i].pos + points[i].out, points[i + 1].pos + points[i + 1].in, points[i + 1].pos);
- float d = pos.distance_to(npp);
+ Vector3 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
+ real_t d = position.distance_to(npp);
if (d > bake_interval) {
// OK! between P and NP there _has_ to be Something, let's go searching!
int iterations = 10; //lots of detail!
- float low = p;
- float hi = np;
- float mid = low + (hi - low) * 0.5;
+ real_t low = p;
+ real_t hi = np;
+ real_t mid = low + (hi - low) * 0.5;
for (int j = 0; j < iterations; j++) {
- npp = _bezier_interp(mid, points[i].pos, points[i].pos + points[i].out, points[i + 1].pos + points[i + 1].in, points[i + 1].pos);
- d = pos.distance_to(npp);
+ npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
+ d = position.distance_to(npp);
if (bake_interval < d) {
hi = mid;
@@ -1231,10 +1235,10 @@ void Curve3D::_bake() const {
mid = low + (hi - low) * 0.5;
}
- pos = npp;
+ position = npp;
p = mid;
Plane post;
- post.normal = pos;
+ post.normal = position;
post.d = Math::lerp(points[i].tilt, points[i + 1].tilt, mid);
dist += d;
@@ -1244,16 +1248,22 @@ void Curve3D::_bake() const {
p = np;
}
}
- }
- Vector3 lastpos = points[points.size() - 1].pos;
- float lastilt = points[points.size() - 1].tilt;
+ Vector3 npp = points[i + 1].position;
+ real_t d = position.distance_to(npp);
+
+ position = npp;
+ Plane post;
+ post.normal = position;
+ post.d = points[i + 1].tilt;
+
+ dist += d;
+
+ pointlist.push_back(post);
+ distlist.push_back(dist);
+ }
- float rem = pos.distance_to(lastpos);
- dist += rem;
baked_max_ofs = dist;
- pointlist.push_back(Plane(lastpos, lastilt));
- distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
Vector3 *w = baked_point_cache.ptrw();
@@ -1266,7 +1276,7 @@ void Curve3D::_bake() const {
Vector3 *up_write = baked_up_vector_cache.ptrw();
baked_dist_cache.resize(pointlist.size());
- float *wd = baked_dist_cache.ptrw();
+ real_t *wd = baked_dist_cache.ptrw();
Vector3 sideways;
Vector3 up;
@@ -1288,7 +1298,7 @@ void Curve3D::_bake() const {
forward = idx > 0 ? (w[idx] - w[idx - 1]).normalized() : prev_forward;
- float y_dot = prev_up.dot(forward);
+ real_t y_dot = prev_up.dot(forward);
if (y_dot > (1.0f - CMP_EPSILON)) {
sideways = prev_sideways;
@@ -1315,7 +1325,7 @@ void Curve3D::_bake() const {
}
}
-float Curve3D::get_baked_length() const {
+real_t Curve3D::get_baked_length() const {
if (baked_cache_dirty) {
_bake();
}
@@ -1323,12 +1333,12 @@ float Curve3D::get_baked_length() const {
return baked_max_ofs;
}
-Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const {
+Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const {
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D.");
@@ -1336,20 +1346,21 @@ Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const {
return baked_point_cache.get(0);
}
- int bpc = baked_point_cache.size();
const Vector3 *r = baked_point_cache.ptr();
if (p_offset < 0) {
return r[0];
}
if (p_offset >= baked_max_ofs) {
- return r[bpc - 1];
+ return r[pc - 1];
}
- int start = 0, end = bpc, idx = (end + start) / 2;
- // binary search to find baked points
+ int start = 0;
+ int end = pc;
+ int idx = (end + start) / 2;
+ // Binary search to find baked points.
while (start < idx) {
- float offset = baked_dist_cache[idx];
+ real_t offset = baked_dist_cache[idx];
if (p_offset <= offset) {
end = idx;
} else {
@@ -1358,29 +1369,29 @@ Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const {
idx = (end + start) / 2;
}
- float offset_begin = baked_dist_cache[idx];
- float offset_end = baked_dist_cache[idx + 1];
+ real_t offset_begin = baked_dist_cache[idx];
+ real_t offset_end = baked_dist_cache[idx + 1];
- float idx_interval = offset_end - offset_begin;
+ real_t idx_interval = offset_end - offset_begin;
ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "failed to find baked segment");
- float frac = (p_offset - offset_begin) / idx_interval;
+ real_t frac = (p_offset - offset_begin) / idx_interval;
if (p_cubic) {
Vector3 pre = idx > 0 ? r[idx - 1] : r[idx];
- Vector3 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
+ Vector3 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
} else {
return r[idx].lerp(r[idx + 1], frac);
}
}
-float Curve3D::interpolate_baked_tilt(float p_offset) const {
+real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const {
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked tilts.
int pc = baked_tilt_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D.");
@@ -1388,39 +1399,46 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const {
return baked_tilt_cache.get(0);
}
- int bpc = baked_tilt_cache.size();
const real_t *r = baked_tilt_cache.ptr();
if (p_offset < 0) {
return r[0];
}
if (p_offset >= baked_max_ofs) {
- return r[bpc - 1];
+ return r[pc - 1];
}
- int idx = Math::floor((double)p_offset / (double)bake_interval);
- float frac = Math::fmod(p_offset, bake_interval);
-
- if (idx >= bpc - 1) {
- return r[bpc - 1];
- } else if (idx == bpc - 2) {
- if (frac > 0) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ int start = 0;
+ int end = pc;
+ int idx = (end + start) / 2;
+ // Binary search to find baked points.
+ while (start < idx) {
+ real_t offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
}
- } else {
- frac /= bake_interval;
+ idx = (end + start) / 2;
}
+ real_t offset_begin = baked_dist_cache[idx];
+ real_t offset_end = baked_dist_cache[idx + 1];
+
+ real_t idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "failed to find baked segment");
+
+ real_t frac = (p_offset - offset_begin) / idx_interval;
+
return Math::lerp(r[idx], r[idx + 1], (real_t)frac);
}
-Vector3 Curve3D::interpolate_baked_up_vector(float p_offset, bool p_apply_tilt) const {
+Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) const {
if (baked_cache_dirty) {
_bake();
}
- //validate//
- // curve may not have baked up vectors
+ // Validate: Curve may not have baked up vectors.
int count = baked_up_vector_cache.size();
ERR_FAIL_COND_V_MSG(count == 0, Vector3(0, 1, 0), "No up vectors in Curve3D.");
@@ -1432,10 +1450,27 @@ Vector3 Curve3D::interpolate_baked_up_vector(float p_offset, bool p_apply_tilt)
const Vector3 *rp = baked_point_cache.ptr();
const real_t *rt = baked_tilt_cache.ptr();
- float offset = CLAMP(p_offset, 0.0f, baked_max_ofs);
+ int start = 0;
+ int end = count;
+ int idx = (end + start) / 2;
+ // Binary search to find baked points.
+ while (start < idx) {
+ real_t offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
+ }
+ idx = (end + start) / 2;
+ }
+
+ real_t offset_begin = baked_dist_cache[idx];
+ real_t offset_end = baked_dist_cache[idx + 1];
- int idx = Math::floor((double)offset / (double)bake_interval);
- float frac = Math::fmod(offset, bake_interval) / bake_interval;
+ real_t idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "failed to find baked segment");
+
+ real_t frac = (p_offset - offset_begin) / idx_interval;
if (idx == count - 1) {
return p_apply_tilt ? r[idx].rotated((rp[idx] - rp[idx - 1]).normalized(), rt[idx]) : r[idx];
@@ -1486,13 +1521,13 @@ PackedVector3Array Curve3D::get_baked_up_vectors() const {
}
Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
- // Brute force method
+ // Brute force method.
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D.");
@@ -1503,16 +1538,16 @@ Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
const Vector3 *r = baked_point_cache.ptr();
Vector3 nearest;
- float nearest_dist = -1.0f;
+ real_t nearest_dist = -1.0f;
for (int i = 0; i < pc - 1; i++) {
Vector3 origin = r[i];
Vector3 direction = (r[i + 1] - origin) / bake_interval;
- float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
Vector3 proj = origin + direction * d;
- float dist = proj.distance_squared_to(p_to_point);
+ real_t dist = proj.distance_squared_to(p_to_point);
if (nearest_dist < 0.0f || dist < nearest_dist) {
nearest = proj;
@@ -1523,14 +1558,14 @@ Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
return nearest;
}
-float Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
- // Brute force method
+real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
+ // Brute force method.
if (baked_cache_dirty) {
_bake();
}
- //validate//
+ // Validate: Curve may not have baked points.
int pc = baked_point_cache.size();
ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve3D.");
@@ -1540,18 +1575,18 @@ float Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
const Vector3 *r = baked_point_cache.ptr();
- float nearest = 0.0f;
- float nearest_dist = -1.0f;
- float offset = 0.0f;
+ real_t nearest = 0.0f;
+ real_t nearest_dist = -1.0f;
+ real_t offset = 0.0f;
for (int i = 0; i < pc - 1; i++) {
Vector3 origin = r[i];
Vector3 direction = (r[i + 1] - origin) / bake_interval;
- float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
Vector3 proj = origin + direction * d;
- float dist = proj.distance_squared_to(p_to_point);
+ real_t dist = proj.distance_squared_to(p_to_point);
if (nearest_dist < 0.0f || dist < nearest_dist) {
nearest = offset + d;
@@ -1564,13 +1599,13 @@ float Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
return nearest;
}
-void Curve3D::set_bake_interval(float p_tolerance) {
+void Curve3D::set_bake_interval(real_t p_tolerance) {
bake_interval = p_tolerance;
baked_cache_dirty = true;
emit_signal(CoreStringNames::get_singleton()->changed);
}
-float Curve3D::get_bake_interval() const {
+real_t Curve3D::get_bake_interval() const {
return bake_interval;
}
@@ -1597,7 +1632,7 @@ Dictionary Curve3D::_get_data() const {
for (int i = 0; i < points.size(); i++) {
w[i * 3 + 0] = points[i].in;
w[i * 3 + 1] = points[i].out;
- w[i * 3 + 2] = points[i].pos;
+ w[i * 3 + 2] = points[i].position;
wt[i] = points[i].tilt;
}
@@ -1622,43 +1657,43 @@ void Curve3D::_set_data(const Dictionary &p_data) {
for (int i = 0; i < points.size(); i++) {
points.write[i].in = r[i * 3 + 0];
points.write[i].out = r[i * 3 + 1];
- points.write[i].pos = r[i * 3 + 2];
+ points.write[i].position = r[i * 3 + 2];
points.write[i].tilt = rt[i];
}
baked_cache_dirty = true;
}
-PackedVector3Array Curve3D::tessellate(int p_max_stages, float p_tolerance) const {
+PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) const {
PackedVector3Array tess;
if (points.size() == 0) {
return tess;
}
- Vector<Map<float, Vector3>> midpoints;
+ Vector<RBMap<real_t, Vector3>> midpoints;
midpoints.resize(points.size() - 1);
int pc = 1;
for (int i = 0; i < points.size() - 1; i++) {
- _bake_segment3d(midpoints.write[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance);
+ _bake_segment3d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_tolerance);
pc++;
pc += midpoints[i].size();
}
tess.resize(pc);
Vector3 *bpw = tess.ptrw();
- bpw[0] = points[0].pos;
+ bpw[0] = points[0].position;
int pidx = 0;
for (int i = 0; i < points.size() - 1; i++) {
- for (const KeyValue<float, Vector3> &E : midpoints[i]) {
+ for (const KeyValue<real_t, Vector3> &E : midpoints[i]) {
pidx++;
bpw[pidx] = E.value;
}
pidx++;
- bpw[pidx] = points[i + 1].pos;
+ bpw[pidx] = points[i + 1].position;
}
return tess;
@@ -1705,8 +1740,4 @@ void Curve3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled");
}
-Curve3D::Curve3D() {
- /* add_point(Vector3(-1,0,0));
- add_point(Vector3(0,2,0));
- add_point(Vector3(0,3,5));*/
-}
+Curve3D::Curve3D() {}
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 5808fd6508..398f64cabe 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -50,7 +50,7 @@ public:
};
struct Point {
- Vector2 pos;
+ Vector2 position;
real_t left_tangent = 0.0;
real_t right_tangent = 0.0;
TangentMode left_mode = TANGENT_FREE;
@@ -59,12 +59,12 @@ public:
Point() {
}
- Point(Vector2 p_pos,
+ Point(const Vector2 &p_position,
real_t p_left = 0.0,
real_t p_right = 0.0,
TangentMode p_left_mode = TANGENT_FREE,
TangentMode p_right_mode = TANGENT_FREE) {
- pos = p_pos;
+ position = p_position;
left_tangent = p_left;
right_tangent = p_right;
left_mode = p_left_mode;
@@ -76,7 +76,7 @@ public:
int get_point_count() const { return _points.size(); }
- int add_point(Vector2 p_pos,
+ int add_point(Vector2 p_position,
real_t left_tangent = 0,
real_t right_tangent = 0,
TangentMode left_mode = TANGENT_FREE,
@@ -85,34 +85,34 @@ public:
void remove_point(int p_index);
void clear_points();
- int get_index(real_t offset) const;
+ int get_index(real_t p_offset) const;
- void set_point_value(int p_index, real_t pos);
- int set_point_offset(int p_index, float offset);
+ void set_point_value(int p_index, real_t p_position);
+ int set_point_offset(int p_index, real_t p_offset);
Vector2 get_point_position(int p_index) const;
Point get_point(int p_index) const;
- float get_min_value() const { return _min_value; }
- void set_min_value(float p_min);
+ real_t get_min_value() const { return _min_value; }
+ void set_min_value(real_t p_min);
- float get_max_value() const { return _max_value; }
- void set_max_value(float p_max);
+ real_t get_max_value() const { return _max_value; }
+ void set_max_value(real_t p_max);
- real_t interpolate(real_t offset) const;
- real_t interpolate_local_nocheck(int index, real_t local_offset) const;
+ real_t interpolate(real_t p_offset) const;
+ real_t interpolate_local_nocheck(int p_index, real_t p_local_offset) const;
void clean_dupes();
- void set_point_left_tangent(int i, real_t tangent);
- void set_point_right_tangent(int i, real_t tangent);
- void set_point_left_mode(int i, TangentMode p_mode);
- void set_point_right_mode(int i, TangentMode p_mode);
+ void set_point_left_tangent(int p_index, real_t p_tangent);
+ void set_point_right_tangent(int p_index, real_t p_tangent);
+ void set_point_left_mode(int p_index, TangentMode p_mode);
+ void set_point_right_mode(int p_index, TangentMode p_mode);
- real_t get_point_left_tangent(int i) const;
- real_t get_point_right_tangent(int i) const;
- TangentMode get_point_left_mode(int i) const;
- TangentMode get_point_right_mode(int i) const;
+ real_t get_point_left_tangent(int p_index) const;
+ real_t get_point_right_tangent(int p_index) const;
+ TangentMode get_point_left_mode(int p_index) const;
+ TangentMode get_point_right_mode(int p_index) const;
void update_auto_tangents(int i);
@@ -122,9 +122,9 @@ public:
void bake();
int get_bake_resolution() const { return _bake_resolution; }
void set_bake_resolution(int p_resolution);
- real_t interpolate_baked(real_t offset) const;
+ real_t interpolate_baked(real_t p_offset) const;
- void ensure_default_setup(float p_min, float p_max);
+ void ensure_default_setup(real_t p_min, real_t p_max);
protected:
static void _bind_methods();
@@ -136,8 +136,8 @@ private:
bool _baked_cache_dirty = false;
Vector<real_t> _baked_cache;
int _bake_resolution = 100;
- float _min_value = 0.0;
- float _max_value = 1.0;
+ real_t _min_value = 0.0;
+ real_t _max_value = 1.0;
int _minmax_set_once = 0b00; // Encodes whether min and max have been set a first time, first bit for min and second for max.
};
@@ -149,26 +149,26 @@ class Curve2D : public Resource {
struct Point {
Vector2 in;
Vector2 out;
- Vector2 pos;
+ Vector2 position;
};
Vector<Point> points;
struct BakedPoint {
- float ofs = 0.0;
+ real_t ofs = 0.0;
Vector2 point;
};
mutable bool baked_cache_dirty = false;
mutable PackedVector2Array baked_point_cache;
- mutable PackedFloat32Array baked_dist_cache;
- mutable float baked_max_ofs = 0.0;
+ mutable Vector<real_t> baked_dist_cache;
+ mutable real_t baked_max_ofs = 0.0;
void _bake() const;
- float bake_interval = 5.0;
+ real_t bake_interval = 5.0;
- void _bake_segment2d(Map<float, Vector2> &r_bake, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, float p_tol) const;
+ void _bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const;
Dictionary _get_data() const;
void _set_data(const Dictionary &p_data);
@@ -177,8 +177,8 @@ protected:
public:
int get_point_count() const;
- void add_point(const Vector2 &p_pos, const Vector2 &p_in = Vector2(), const Vector2 &p_out = Vector2(), int p_atpos = -1);
- void set_point_position(int p_index, const Vector2 &p_pos);
+ void add_point(const Vector2 &p_position, const Vector2 &p_in = Vector2(), const Vector2 &p_out = Vector2(), int p_atpos = -1);
+ void set_point_position(int p_index, const Vector2 &p_position);
Vector2 get_point_position(int p_index) const;
void set_point_in(int p_index, const Vector2 &p_in);
Vector2 get_point_in(int p_index) const;
@@ -187,19 +187,19 @@ public:
void remove_point(int p_index);
void clear_points();
- Vector2 interpolate(int p_index, float p_offset) const;
+ Vector2 interpolate(int p_index, real_t p_offset) const;
Vector2 interpolatef(real_t p_findex) const;
- void set_bake_interval(float p_tolerance);
- float get_bake_interval() const;
+ void set_bake_interval(real_t p_tolerance);
+ real_t get_bake_interval() const;
- float get_baked_length() const;
- Vector2 interpolate_baked(float p_offset, bool p_cubic = false) const;
+ real_t get_baked_length() const;
+ Vector2 interpolate_baked(real_t p_offset, bool p_cubic = false) const;
PackedVector2Array get_baked_points() const; //useful for going through
Vector2 get_closest_point(const Vector2 &p_to_point) const;
- float get_closest_offset(const Vector2 &p_to_point) const;
+ real_t get_closest_offset(const Vector2 &p_to_point) const;
- PackedVector2Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display
+ PackedVector2Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; //useful for display
Curve2D();
};
@@ -210,14 +210,14 @@ class Curve3D : public Resource {
struct Point {
Vector3 in;
Vector3 out;
- Vector3 pos;
- float tilt = 0.0;
+ Vector3 position;
+ real_t tilt = 0.0;
};
Vector<Point> points;
struct BakedPoint {
- float ofs = 0.0;
+ real_t ofs = 0.0;
Vector3 point;
};
@@ -225,15 +225,15 @@ class Curve3D : public Resource {
mutable PackedVector3Array baked_point_cache;
mutable Vector<real_t> baked_tilt_cache;
mutable PackedVector3Array baked_up_vector_cache;
- mutable PackedFloat32Array baked_dist_cache;
- mutable float baked_max_ofs = 0.0;
+ mutable Vector<real_t> baked_dist_cache;
+ mutable real_t baked_max_ofs = 0.0;
void _bake() const;
- float bake_interval = 0.2;
+ real_t bake_interval = 0.2;
bool up_vector_enabled = true;
- void _bake_segment3d(Map<float, Vector3> &r_bake, float p_begin, float p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, float p_tol) const;
+ void _bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const;
Dictionary _get_data() const;
void _set_data(const Dictionary &p_data);
@@ -242,11 +242,11 @@ protected:
public:
int get_point_count() const;
- void add_point(const Vector3 &p_pos, const Vector3 &p_in = Vector3(), const Vector3 &p_out = Vector3(), int p_atpos = -1);
- void set_point_position(int p_index, const Vector3 &p_pos);
+ void add_point(const Vector3 &p_position, const Vector3 &p_in = Vector3(), const Vector3 &p_out = Vector3(), int p_atpos = -1);
+ void set_point_position(int p_index, const Vector3 &p_position);
Vector3 get_point_position(int p_index) const;
- void set_point_tilt(int p_index, float p_tilt);
- float get_point_tilt(int p_index) const;
+ void set_point_tilt(int p_index, real_t p_tilt);
+ real_t get_point_tilt(int p_index) const;
void set_point_in(int p_index, const Vector3 &p_in);
Vector3 get_point_in(int p_index) const;
void set_point_out(int p_index, const Vector3 &p_out);
@@ -254,25 +254,25 @@ public:
void remove_point(int p_index);
void clear_points();
- Vector3 interpolate(int p_index, float p_offset) const;
+ Vector3 interpolate(int p_index, real_t p_offset) const;
Vector3 interpolatef(real_t p_findex) const;
- void set_bake_interval(float p_tolerance);
- float get_bake_interval() const;
+ void set_bake_interval(real_t p_tolerance);
+ real_t get_bake_interval() const;
void set_up_vector_enabled(bool p_enable);
bool is_up_vector_enabled() const;
- float get_baked_length() const;
- Vector3 interpolate_baked(float p_offset, bool p_cubic = false) const;
- float interpolate_baked_tilt(float p_offset) const;
- Vector3 interpolate_baked_up_vector(float p_offset, bool p_apply_tilt = false) const;
+ real_t get_baked_length() const;
+ Vector3 interpolate_baked(real_t p_offset, bool p_cubic = false) const;
+ real_t interpolate_baked_tilt(real_t p_offset) const;
+ Vector3 interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt = false) const;
PackedVector3Array get_baked_points() const; //useful for going through
Vector<real_t> get_baked_tilts() const; //useful for going through
PackedVector3Array get_baked_up_vectors() const;
Vector3 get_closest_point(const Vector3 &p_to_point) const;
- float get_closest_offset(const Vector3 &p_to_point) const;
+ real_t get_closest_offset(const Vector3 &p_to_point) const;
- PackedVector3Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display
+ PackedVector3Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; //useful for display
Curve3D();
};
diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp
index 63bdc8d26d..a1fe5c46fb 100644
--- a/scene/resources/cylinder_shape_3d.cpp
+++ b/scene/resources/cylinder_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "cylinder_shape_3d.h"
+
#include "servers/physics_server_3d.h"
Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() const {
@@ -72,6 +73,7 @@ void CylinderShape3D::_update_shape() {
}
void CylinderShape3D::set_radius(float p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0, "CylinderShape3D radius cannot be negative.");
radius = p_radius;
_update_shape();
notify_change_to_owners();
@@ -82,6 +84,7 @@ float CylinderShape3D::get_radius() const {
}
void CylinderShape3D::set_height(float p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0, "CylinderShape3D height cannot be negative.");
height = p_height;
_update_shape();
notify_change_to_owners();
@@ -97,8 +100,8 @@ void CylinderShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderShape3D::set_height);
ClassDB::bind_method(D_METHOD("get_height"), &CylinderShape3D::get_height);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_radius", "get_radius");
}
CylinderShape3D::CylinderShape3D() :
diff --git a/scene/resources/cylinder_shape_3d.h b/scene/resources/cylinder_shape_3d.h
index d1b8364672..65427423c8 100644
--- a/scene/resources/cylinder_shape_3d.h
+++ b/scene/resources/cylinder_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,7 @@
class CylinderShape3D : public Shape3D {
GDCLASS(CylinderShape3D, Shape3D);
- float radius = 1.0;
+ float radius = 0.5;
float height = 2.0;
protected:
diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub
index 3667ab7c14..5bef7e5a6c 100644
--- a/scene/resources/default_theme/SCsub
+++ b/scene/resources/default_theme/SCsub
@@ -4,12 +4,29 @@ Import("env")
from platform_methods import run_in_subprocess
import default_theme_builders
+import default_theme_icons_builders
env.add_source_files(env.scene_sources, "*.cpp")
-env.Depends("#scene/resources/default_theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.ttf")
+env.Depends("#scene/resources/default_theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.woff2")
env.CommandNoCache(
"#scene/resources/default_theme/default_font.gen.h",
- "#thirdparty/fonts/OpenSans_SemiBold.ttf",
+ "#thirdparty/fonts/OpenSans_SemiBold.woff2",
run_in_subprocess(default_theme_builders.make_fonts_header),
)
+
+env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder(
+ action=env.Run(
+ default_theme_icons_builders.make_default_theme_icons_action, "Generating default project theme icons header."
+ ),
+ suffix=".h",
+ src_suffix=".svg",
+)
+
+# Default theme icons
+icon_sources = Glob("*.svg")
+
+env.Alias(
+ "default_theme_icons",
+ [env.MakeDefaultThemeIconsBuilder("#scene/resources/default_theme/default_theme_icons.gen.h", icon_sources)],
+)
diff --git a/scene/resources/default_theme/add.svg b/scene/resources/default_theme/add.svg
new file mode 100644
index 0000000000..6884a07aa0
--- /dev/null
+++ b/scene/resources/default_theme/add.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/arrow_down.png b/scene/resources/default_theme/arrow_down.png
deleted file mode 100644
index bfb87a4761..0000000000
--- a/scene/resources/default_theme/arrow_down.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/arrow_down.svg b/scene/resources/default_theme/arrow_down.svg
new file mode 100644
index 0000000000..16cc00fa8f
--- /dev/null
+++ b/scene/resources/default_theme/arrow_down.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m5 7 3 3 3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/arrow_left.png b/scene/resources/default_theme/arrow_left.png
deleted file mode 100644
index 4163059dd3..0000000000
--- a/scene/resources/default_theme/arrow_left.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/arrow_left.svg b/scene/resources/default_theme/arrow_left.svg
new file mode 100644
index 0000000000..7dbf01e186
--- /dev/null
+++ b/scene/resources/default_theme/arrow_left.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m9 11-3-3 3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/arrow_right.png b/scene/resources/default_theme/arrow_right.png
deleted file mode 100644
index 1e4c8e5529..0000000000
--- a/scene/resources/default_theme/arrow_right.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/arrow_right.svg b/scene/resources/default_theme/arrow_right.svg
new file mode 100644
index 0000000000..b031f12ec0
--- /dev/null
+++ b/scene/resources/default_theme/arrow_right.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m6 11 3-3-3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/bar_arrow.png b/scene/resources/default_theme/bar_arrow.png
deleted file mode 100644
index 7cf146b8da..0000000000
--- a/scene/resources/default_theme/bar_arrow.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/bookmark.png b/scene/resources/default_theme/bookmark.png
deleted file mode 100644
index 9718cf53b6..0000000000
--- a/scene/resources/default_theme/bookmark.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/bookmark.svg b/scene/resources/default_theme/bookmark.svg
new file mode 100644
index 0000000000..0b50c2b034
--- /dev/null
+++ b/scene/resources/default_theme/bookmark.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#fefffe" fill-opacity=".85"><path d="m14.505 1.476h-12.995v13.049l6.491-5.185 6.504 5.185z"/><path d="m15.018 1.476c0-.25-.23-.452-.514-.452h-12.994c-.284 0-.513.202-.513.452v13.049c0 .178.119.34.306.413.185.071.402.041.552-.079l6.146-4.909 6.158 4.909c.151.12.367.15.553.079.187-.073.306-.235.306-.413zm-.514 0h-12.994v13.049l6.491-5.185 6.503 5.185z"/></g></svg>
diff --git a/scene/resources/default_theme/breakpoint.svg b/scene/resources/default_theme/breakpoint.svg
new file mode 100644
index 0000000000..9b18b276d8
--- /dev/null
+++ b/scene/resources/default_theme/breakpoint.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8c0 3.84-3.16 7-7 7s-7-3.16-7-7 3.16-7 7-7 7 3.16 7 7" fill="#ff5d5d" fill-opacity=".5" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/button_disabled.png b/scene/resources/default_theme/button_disabled.png
deleted file mode 100644
index 708748dfe9..0000000000
--- a/scene/resources/default_theme/button_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/button_focus.png b/scene/resources/default_theme/button_focus.png
deleted file mode 100644
index 70e16b953b..0000000000
--- a/scene/resources/default_theme/button_focus.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/button_hover.png b/scene/resources/default_theme/button_hover.png
deleted file mode 100644
index ff2258281e..0000000000
--- a/scene/resources/default_theme/button_hover.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/button_normal.png b/scene/resources/default_theme/button_normal.png
deleted file mode 100644
index c189b61b89..0000000000
--- a/scene/resources/default_theme/button_normal.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/button_pressed.png b/scene/resources/default_theme/button_pressed.png
deleted file mode 100644
index 19a7e237aa..0000000000
--- a/scene/resources/default_theme/button_pressed.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/checked.png b/scene/resources/default_theme/checked.png
deleted file mode 100644
index bde031b6a2..0000000000
--- a/scene/resources/default_theme/checked.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/checked.svg b/scene/resources/default_theme/checked.svg
new file mode 100644
index 0000000000..6c3bc8f210
--- /dev/null
+++ b/scene/resources/default_theme/checked.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#fff" fill-opacity=".75" stroke-width="1.16667"/><path d="m11.500773 3.7343508-5.6117507 5.6117502-1.7045017-1.6814543-1.4992276 1.4992276 3.2037293 3.1806817 7.1109777-7.1109775z" fill="#1a1a1a" stroke-width="1.06023"/></svg>
diff --git a/scene/resources/default_theme/checked_disabled.png b/scene/resources/default_theme/checked_disabled.png
deleted file mode 100644
index 70549e2edc..0000000000
--- a/scene/resources/default_theme/checked_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/checked_disabled.svg b/scene/resources/default_theme/checked_disabled.svg
new file mode 100644
index 0000000000..109c4dc129
--- /dev/null
+++ b/scene/resources/default_theme/checked_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#fff" fill-opacity=".37" stroke-width="1.16667"/><path d="m11.500773 3.7343508-5.6117507 5.6117502-1.7045017-1.6814543-1.4992276 1.4992276 3.2037293 3.1806817 7.1109777-7.1109775z" fill="#1a1a1a" fill-opacity=".5" stroke-width="1.06023"/></svg>
diff --git a/scene/resources/default_theme/checker_bg.png b/scene/resources/default_theme/checker_bg.png
deleted file mode 100644
index 3eff2f0e08..0000000000
--- a/scene/resources/default_theme/checker_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/close.png b/scene/resources/default_theme/close.png
deleted file mode 100644
index 4d4ac4a551..0000000000
--- a/scene/resources/default_theme/close.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/close.svg b/scene/resources/default_theme/close.svg
new file mode 100644
index 0000000000..c26cca06c3
--- /dev/null
+++ b/scene/resources/default_theme/close.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 3 2-2 5 5 5-5 2 2-5 5 5 5-2 2-5.3020979-5-4.6979021 5-2-2 5-5z" fill="#fff" fill-opacity=".75"/></svg>
diff --git a/scene/resources/default_theme/close_hl.png b/scene/resources/default_theme/close_hl.png
deleted file mode 100644
index 4d4ac4a551..0000000000
--- a/scene/resources/default_theme/close_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/close_hl.svg b/scene/resources/default_theme/close_hl.svg
new file mode 100644
index 0000000000..ec44ba6d24
--- /dev/null
+++ b/scene/resources/default_theme/close_hl.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 3 2-2 5 5 5-5 2 2-5 5 5 5-2 2-5.3020979-5-4.6979021 5-2-2 5-5z" fill="#fff"/></svg>
diff --git a/scene/resources/default_theme/color_picker_bar_arrow.svg b/scene/resources/default_theme/color_picker_bar_arrow.svg
new file mode 100644
index 0000000000..5043da0761
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_bar_arrow.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 20" xmlns="http://www.w3.org/2000/svg"><path d="m3.564 15.218h8.872l-4.436-4.436z" fill="#b2b2b2" fill-rule="nonzero" stroke="#b2b2b2"/></svg>
diff --git a/scene/resources/default_theme/color_picker_cursor.svg b/scene/resources/default_theme/color_picker_cursor.svg
new file mode 100644
index 0000000000..88ee3f55ce
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_cursor.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2a6 6 0 0 0 -6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0 -6-6zm0 1a5 5 0 0 1 5 5 5 5 0 0 1 -5 5 5 5 0 0 1 -5-5 5 5 0 0 1 5-5z" fill="#fff"/><path d="m8 3a5 5 0 0 0 -5 5 5 5 0 0 0 5 5 5 5 0 0 0 5-5 5 5 0 0 0 -5-5zm-.0605469 1a4 4 0 0 1 .0605469 0 4 4 0 0 1 4 4 4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 3.9394531-4z"/></svg>
diff --git a/scene/resources/default_theme/color_picker_hue.png b/scene/resources/default_theme/color_picker_hue.png
deleted file mode 100644
index 7b46f03cb4..0000000000
--- a/scene/resources/default_theme/color_picker_hue.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/color_picker_hue.svg b/scene/resources/default_theme/color_picker_hue.svg
new file mode 100644
index 0000000000..ff75d5eb9e
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_hue.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 1 256" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(0 256 -256 0 0 0)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#f00"/><stop offset=".04" stop-color="#ff4000"/><stop offset=".08" stop-color="#ff8000"/><stop offset=".17" stop-color="#ff0"/><stop offset=".25" stop-color="#80ff00"/><stop offset=".33" stop-color="#0f0"/><stop offset=".42" stop-color="#00ff80"/><stop offset=".5" stop-color="#0ff"/><stop offset=".58" stop-color="#0080ff"/><stop offset=".63" stop-color="#0040ff"/><stop offset=".67" stop-color="#00f"/><stop offset=".75" stop-color="#8000ff"/><stop offset=".83" stop-color="#f0f"/><stop offset=".92" stop-color="#ff0080"/><stop offset="1" stop-color="#f00"/></linearGradient><path d="m0 0h1v256h-1z" fill="url(#a)"/></svg>
diff --git a/scene/resources/default_theme/color_picker_overbright.svg b/scene/resources/default_theme/color_picker_overbright.svg
new file mode 100644
index 0000000000..f618980d51
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_overbright.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m.5.5v10l10-10z" fill="#fff" stroke="#000"/><path d="m0 12 12-12h-1.714286l-10.285714 10.285714z" fill="#000003" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/color_picker_pipette.svg b/scene/resources/default_theme/color_picker_pipette.svg
new file mode 100644
index 0000000000..5213a6e1bc
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_pipette.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-1.108 0-2 .892-2 2v2h-1v2h1v5c.001.712.383 1.372 1 1.729v1.271h2v-1.27c.618-.356.999-1.017 1-1.73v-5h1v-2h-1v-2c0-1.108-.892-2-2-2zm-1 6h2v5c0 .549-.451 1-1 1s-1-.451-1-1z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/color_picker_sample.png b/scene/resources/default_theme/color_picker_sample.png
deleted file mode 100644
index e6ec28d307..0000000000
--- a/scene/resources/default_theme/color_picker_sample.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index a1d76ef352..4fb44446d9 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,49 +32,22 @@
#include "core/os/os.h"
#include "default_font.gen.h"
+#include "default_theme_icons.gen.h"
#include "scene/resources/font.h"
#include "scene/resources/theme.h"
#include "servers/text_server.h"
-#include "theme_data.h"
-typedef Map<const void *, Ref<ImageTexture>> TexCacheMap;
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
-static TexCacheMap *tex_cache;
static float scale = 1.0;
-template <class T>
-static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, float p_right, float p_bottom, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1, bool p_draw_center = true) {
- Ref<ImageTexture> texture;
+static const int default_margin = 4;
+static const int default_corner_radius = 3;
- if (tex_cache->has(p_src)) {
- texture = (*tex_cache)[p_src];
- } else {
- texture = Ref<ImageTexture>(memnew(ImageTexture));
- Ref<Image> img = memnew(Image(p_src));
- const Size2 orig_size = img->get_size();
- img->convert(Image::FORMAT_RGBA8);
- img->resize(orig_size.x * scale, orig_size.y * scale);
-
- texture->create_from_image(img);
- (*tex_cache)[p_src] = texture;
- }
-
- Ref<StyleBoxTexture> style(memnew(StyleBoxTexture));
- style->set_texture(texture);
- style->set_margin_size(SIDE_LEFT, p_left * scale);
- style->set_margin_size(SIDE_RIGHT, p_right * scale);
- style->set_margin_size(SIDE_BOTTOM, p_bottom * scale);
- style->set_margin_size(SIDE_TOP, p_top * scale);
- style->set_default_margin(SIDE_LEFT, p_margin_left * scale);
- style->set_default_margin(SIDE_RIGHT, p_margin_right * scale);
- style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale);
- style->set_default_margin(SIDE_TOP, p_margin_top * scale);
- style->set_draw_center(p_draw_center);
-
- return style;
-}
-
-static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
+static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = default_margin, float p_margin_top = default_margin, float p_margin_right = default_margin, float p_margin_bottom = default_margin, int p_corner_radius = default_corner_radius, bool p_draw_center = true, int p_border_width = 0) {
Ref<StyleBoxFlat> style(memnew(StyleBoxFlat));
style->set_bg_color(p_color);
style->set_default_margin(SIDE_LEFT, p_margin_left * scale);
@@ -82,10 +55,18 @@ static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left =
style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale);
style->set_default_margin(SIDE_TOP, p_margin_top * scale);
+ style->set_corner_radius_all(p_corner_radius);
+ style->set_anti_aliased(true);
+ // Adjust level of detail based on the corners' effective sizes.
+ style->set_corner_detail(MIN(Math::ceil(1.5 * p_corner_radius), 6) * scale);
+
+ style->set_draw_center(p_draw_center);
+ style->set_border_width_all(p_border_width);
+
return style;
}
-static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left, float p_top, float p_right, float p_bottom) {
+static Ref<StyleBoxFlat> sb_expand(Ref<StyleBoxFlat> p_sbox, float p_left, float p_top, float p_right, float p_bottom) {
p_sbox->set_expand_margin_size(SIDE_LEFT, p_left * scale);
p_sbox->set_expand_margin_size(SIDE_TOP, p_top * scale);
p_sbox->set_expand_margin_size(SIDE_RIGHT, p_right * scale);
@@ -93,36 +74,22 @@ static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left,
return p_sbox;
}
-template <class T>
-static Ref<Texture2D> make_icon(T p_src) {
- Ref<ImageTexture> texture(memnew(ImageTexture));
- Ref<Image> img = memnew(Image(p_src));
- const Size2 orig_size = img->get_size();
- img->convert(Image::FORMAT_RGBA8);
- img->resize(orig_size.x * scale, orig_size.y * scale);
- texture->create_from_image(img);
-
- return texture;
-}
-
-static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) {
- if (!p_flip_y && !p_flip_x) {
- return p_texture;
- }
-
- Ref<ImageTexture> texture(memnew(ImageTexture));
- Ref<Image> img = p_texture->get_image();
- img = img->duplicate();
-
- if (p_flip_y) {
- img->flip_y();
- }
- if (p_flip_x) {
- img->flip_x();
- }
-
- texture->create_from_image(img);
- return texture;
+// See also `editor_generate_icon()` in `editor/editor_themes.cpp`.
+static Ref<ImageTexture> generate_icon(int p_index) {
+ Ref<ImageTexture> icon = memnew(ImageTexture);
+ Ref<Image> img = memnew(Image);
+
+#ifdef MODULE_SVG_ENABLED
+ // Upsample icon generation only if the scale isn't an integer multiplier.
+ // Generating upsampled icons is slower, and the benefit is hardly visible
+ // with integer scales.
+ const bool upsample = !Math::is_equal_approx(Math::round(scale), scale);
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, false);
+#endif
+ icon->create_from_image(img);
+
+ return icon;
}
static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
@@ -136,48 +103,60 @@ static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margi
return style;
}
-void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &large_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale) {
+void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale) {
scale = p_scale;
- tex_cache = memnew(TexCacheMap);
-
- // Font Colors
-
- Color control_font_color = Color(0.88, 0.88, 0.88);
- Color control_font_lower_color = Color(0.63, 0.63, 0.63);
- Color control_font_low_color = Color(0.69, 0.69, 0.69);
- Color control_font_hover_color = Color(0.94, 0.94, 0.94);
- Color control_font_focus_color = Color(0.94, 0.94, 0.94);
- Color control_font_disabled_color = Color(0.9, 0.9, 0.9, 0.2);
- Color control_font_pressed_color = Color(1, 1, 1);
-
- Color control_selection_color = Color(0.49, 0.49, 0.49);
+ // Font colors
+ const Color control_font_color = Color(0.875, 0.875, 0.875);
+ const Color control_font_low_color = Color(0.7, 0.7, 0.7);
+ const Color control_font_lower_color = Color(0.65, 0.65, 0.65);
+ const Color control_font_hover_color = Color(0.95, 0.95, 0.95);
+ const Color control_font_focus_color = Color(0.95, 0.95, 0.95);
+ const Color control_font_disabled_color = control_font_color * Color(1, 1, 1, 0.5);
+ const Color control_font_placeholder_color = Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.6f);
+ const Color control_font_pressed_color = Color(1, 1, 1);
+ const Color control_selection_color = Color(0.5, 0.5, 0.5);
+
+ // StyleBox colors
+ const Color style_normal_color = Color(0.1, 0.1, 0.1, 0.6);
+ const Color style_hover_color = Color(0.225, 0.225, 0.225, 0.6);
+ const Color style_pressed_color = Color(0, 0, 0, 0.6);
+ const Color style_disabled_color = Color(0.1, 0.1, 0.1, 0.3);
+ const Color style_focus_color = Color(1, 1, 1, 0.75);
+ const Color style_popup_color = Color(0.25, 0.25, 0.25, 1);
+ const Color style_popup_border_color = Color(0.175, 0.175, 0.175, 1);
+ const Color style_popup_hover_color = Color(0.4, 0.4, 0.4, 1);
+ const Color style_selected_color = Color(1, 1, 1, 0.3);
+ // Don't use a color too bright to keep the percentage readable.
+ const Color style_progress_color = Color(1, 1, 1, 0.4);
+ const Color style_separator_color = Color(0.5, 0.5, 0.5);
+
+ // Convert the generated icon sources to a dictionary for easier access.
+ // Unlike the editor icons, there is no central repository of icons in the Theme resource itself to keep it tidy.
+ Dictionary icons;
+ for (int i = 0; i < default_theme_icons_count; i++) {
+ icons[default_theme_icons_names[i]] = generate_icon(i);
+ }
// Panel
-
- theme->set_stylebox("panel", "Panel", make_stylebox(panel_bg_png, 0, 0, 0, 0));
- theme->set_stylebox("panel_fg", "Panel", make_stylebox(panel_bg_png, 0, 0, 0, 0));
-
- // Focus
-
- Ref<StyleBoxTexture> focus = make_stylebox(focus_png, 5, 5, 5, 5);
- for (int i = 0; i < 4; i++) {
- focus->set_expand_margin_size(Side(i), 1 * scale);
- }
+ theme->set_stylebox("panel", "Panel", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+ theme->set_stylebox("panel_fg", "Panel", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
// Button
- Ref<StyleBox> sb_button_normal = sb_expand(make_stylebox(button_normal_png, 4, 4, 4, 4, 6, 3, 6, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_button_pressed = sb_expand(make_stylebox(button_pressed_png, 4, 4, 4, 4, 6, 3, 6, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_button_hover = sb_expand(make_stylebox(button_hover_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_button_disabled = sb_expand(make_stylebox(button_disabled_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_button_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
+ const Ref<StyleBoxFlat> button_normal = make_flat_stylebox(style_normal_color);
+ const Ref<StyleBoxFlat> button_hover = make_flat_stylebox(style_hover_color);
+ const Ref<StyleBoxFlat> button_pressed = make_flat_stylebox(style_pressed_color);
+ const Ref<StyleBoxFlat> button_disabled = make_flat_stylebox(style_disabled_color);
+ Ref<StyleBoxFlat> focus = make_flat_stylebox(style_focus_color, default_margin, default_margin, default_margin, default_margin, default_corner_radius, false, 2);
+ // Make the focus outline appear to be flush with the buttons it's focusing.
+ focus->set_expand_margin_size_all(2 * scale);
- theme->set_stylebox("normal", "Button", sb_button_normal);
- theme->set_stylebox("pressed", "Button", sb_button_pressed);
- theme->set_stylebox("hover", "Button", sb_button_hover);
- theme->set_stylebox("disabled", "Button", sb_button_disabled);
- theme->set_stylebox("focus", "Button", sb_button_focus);
+ theme->set_stylebox("normal", "Button", button_normal);
+ theme->set_stylebox("hover", "Button", button_hover);
+ theme->set_stylebox("pressed", "Button", button_pressed);
+ theme->set_stylebox("disabled", "Button", button_disabled);
+ theme->set_stylebox("focus", "Button", focus);
theme->set_font("font", "Button", Ref<Font>());
theme->set_font_size("font_size", "Button", -1);
@@ -196,9 +175,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("icon_hover_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_hover_pressed_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1));
- theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 1));
+ theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4));
- theme->set_constant("hseparation", "Button", 2 * scale);
+ theme->set_constant("h_separation", "Button", 2 * scale);
// LinkButton
@@ -217,31 +196,29 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("underline_spacing", "LinkButton", 2 * scale);
// OptionButton
+ theme->set_stylebox("focus", "OptionButton", focus);
- Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
- theme->set_stylebox("focus", "OptionButton", sb_optbutton_focus);
-
- Ref<StyleBox> sb_optbutton_normal = sb_expand(make_stylebox(option_button_normal_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_pressed = sb_expand(make_stylebox(option_button_pressed_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_hover = sb_expand(make_stylebox(option_button_hover_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_disabled = sb_expand(make_stylebox(option_button_disabled_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_normal = make_flat_stylebox(style_normal_color, 2 * default_margin, default_margin, 21, default_margin);
+ Ref<StyleBox> sb_optbutton_hover = make_flat_stylebox(style_hover_color, 2 * default_margin, default_margin, 21, default_margin);
+ Ref<StyleBox> sb_optbutton_pressed = make_flat_stylebox(style_pressed_color, 2 * default_margin, default_margin, 21, default_margin);
+ Ref<StyleBox> sb_optbutton_disabled = make_flat_stylebox(style_disabled_color, 2 * default_margin, default_margin, 21, default_margin);
theme->set_stylebox("normal", "OptionButton", sb_optbutton_normal);
- theme->set_stylebox("pressed", "OptionButton", sb_optbutton_pressed);
theme->set_stylebox("hover", "OptionButton", sb_optbutton_hover);
+ theme->set_stylebox("pressed", "OptionButton", sb_optbutton_pressed);
theme->set_stylebox("disabled", "OptionButton", sb_optbutton_disabled);
- Ref<StyleBox> sb_optbutton_normal_mirrored = sb_expand(make_stylebox(option_button_normal_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_pressed_mirrored = sb_expand(make_stylebox(option_button_pressed_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_hover_mirrored = sb_expand(make_stylebox(option_button_hover_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_disabled_mirrored = sb_expand(make_stylebox(option_button_disabled_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_normal_mirrored = make_flat_stylebox(style_normal_color, 21, default_margin, 2 * default_margin, default_margin);
+ Ref<StyleBox> sb_optbutton_hover_mirrored = make_flat_stylebox(style_hover_color, 21, default_margin, 2 * default_margin, default_margin);
+ Ref<StyleBox> sb_optbutton_pressed_mirrored = make_flat_stylebox(style_pressed_color, 21, default_margin, 2 * default_margin, default_margin);
+ Ref<StyleBox> sb_optbutton_disabled_mirrored = make_flat_stylebox(style_disabled_color, 21, default_margin, 2 * default_margin, default_margin);
theme->set_stylebox("normal_mirrored", "OptionButton", sb_optbutton_normal_mirrored);
- theme->set_stylebox("pressed_mirrored", "OptionButton", sb_optbutton_pressed_mirrored);
theme->set_stylebox("hover_mirrored", "OptionButton", sb_optbutton_hover_mirrored);
+ theme->set_stylebox("pressed_mirrored", "OptionButton", sb_optbutton_pressed_mirrored);
theme->set_stylebox("disabled_mirrored", "OptionButton", sb_optbutton_disabled_mirrored);
- theme->set_icon("arrow", "OptionButton", make_icon(option_arrow_png));
+ theme->set_icon("arrow", "OptionButton", icons["option_button_arrow"]);
theme->set_font("font", "OptionButton", Ref<Font>());
theme->set_font_size("font_size", "OptionButton", -1);
@@ -253,17 +230,17 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color);
theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1));
- theme->set_constant("hseparation", "OptionButton", 2 * scale);
- theme->set_constant("arrow_margin", "OptionButton", 2 * scale);
+ theme->set_constant("h_separation", "OptionButton", 2 * scale);
+ theme->set_constant("arrow_margin", "OptionButton", 4 * scale);
theme->set_constant("outline_size", "OptionButton", 0);
// MenuButton
- theme->set_stylebox("normal", "MenuButton", sb_button_normal);
- theme->set_stylebox("pressed", "MenuButton", sb_button_pressed);
- theme->set_stylebox("hover", "MenuButton", sb_button_hover);
- theme->set_stylebox("disabled", "MenuButton", sb_button_disabled);
- theme->set_stylebox("focus", "MenuButton", sb_button_focus);
+ theme->set_stylebox("normal", "MenuButton", button_normal);
+ theme->set_stylebox("pressed", "MenuButton", button_pressed);
+ theme->set_stylebox("hover", "MenuButton", button_hover);
+ theme->set_stylebox("disabled", "MenuButton", button_disabled);
+ theme->set_stylebox("focus", "MenuButton", focus);
theme->set_font("font", "MenuButton", Ref<Font>());
theme->set_font_size("font_size", "MenuButton", -1);
@@ -275,7 +252,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3));
theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1));
- theme->set_constant("hseparation", "MenuButton", 3 * scale);
+ theme->set_constant("h_separation", "MenuButton", 3 * scale);
theme->set_constant("outline_size", "MenuButton", 0);
// CheckBox
@@ -298,14 +275,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("hover_pressed", "CheckBox", cbx_empty);
theme->set_stylebox("focus", "CheckBox", cbx_focus);
- theme->set_icon("checked", "CheckBox", make_icon(checked_png));
- theme->set_icon("checked_disabled", "CheckBox", make_icon(checked_disabled_png));
- theme->set_icon("unchecked", "CheckBox", make_icon(unchecked_png));
- theme->set_icon("unchecked_disabled", "CheckBox", make_icon(unchecked_disabled_png));
- theme->set_icon("radio_checked", "CheckBox", make_icon(radio_checked_png));
- theme->set_icon("radio_checked_disabled", "CheckBox", make_icon(radio_checked_disabled_png));
- theme->set_icon("radio_unchecked", "CheckBox", make_icon(radio_unchecked_png));
- theme->set_icon("radio_unchecked_disabled", "CheckBox", make_icon(radio_unchecked_disabled_png));
+ theme->set_icon("checked", "CheckBox", icons["checked"]);
+ theme->set_icon("checked_disabled", "CheckBox", icons["checked"]);
+ theme->set_icon("unchecked", "CheckBox", icons["unchecked"]);
+ theme->set_icon("unchecked_disabled", "CheckBox", icons["unchecked"]);
+ theme->set_icon("radio_checked", "CheckBox", icons["radio_checked"]);
+ theme->set_icon("radio_checked_disabled", "CheckBox", icons["radio_checked"]);
+ theme->set_icon("radio_unchecked", "CheckBox", icons["radio_unchecked"]);
+ theme->set_icon("radio_unchecked_disabled", "CheckBox", icons["radio_unchecked"]);
theme->set_font("font", "CheckBox", Ref<Font>());
theme->set_font_size("font_size", "CheckBox", -1);
@@ -318,8 +295,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "CheckBox", control_font_disabled_color);
theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1));
- theme->set_constant("hseparation", "CheckBox", 4 * scale);
- theme->set_constant("check_vadjust", "CheckBox", 0 * scale);
+ theme->set_constant("h_separation", "CheckBox", 4 * scale);
+ theme->set_constant("check_v_adjust", "CheckBox", 0 * scale);
theme->set_constant("outline_size", "CheckBox", 0);
// CheckButton
@@ -337,15 +314,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("hover_pressed", "CheckButton", cb_empty);
theme->set_stylebox("focus", "CheckButton", focus);
- theme->set_icon("on", "CheckButton", make_icon(toggle_on_png));
- theme->set_icon("on_disabled", "CheckButton", make_icon(toggle_on_disabled_png));
- theme->set_icon("off", "CheckButton", make_icon(toggle_off_png));
- theme->set_icon("off_disabled", "CheckButton", make_icon(toggle_off_disabled_png));
+ theme->set_icon("on", "CheckButton", icons["toggle_on"]);
+ theme->set_icon("on_disabled", "CheckButton", icons["toggle_on_disabled"]);
+ theme->set_icon("off", "CheckButton", icons["toggle_off"]);
+ theme->set_icon("off_disabled", "CheckButton", icons["toggle_off_disabled"]);
- theme->set_icon("on_mirrored", "CheckButton", make_icon(toggle_on_mirrored_png));
- theme->set_icon("on_disabled_mirrored", "CheckButton", make_icon(toggle_on_disabled_mirrored_png));
- theme->set_icon("off_mirrored", "CheckButton", make_icon(toggle_off_mirrored_png));
- theme->set_icon("off_disabled_mirrored", "CheckButton", make_icon(toggle_off_disabled_mirrored_png));
+ theme->set_icon("on_mirrored", "CheckButton", icons["toggle_on_mirrored"]);
+ theme->set_icon("on_disabled_mirrored", "CheckButton", icons["toggle_on_disabled_mirrored"]);
+ theme->set_icon("off_mirrored", "CheckButton", icons["toggle_off_mirrored"]);
+ theme->set_icon("off_disabled_mirrored", "CheckButton", icons["toggle_off_disabled_mirrored"]);
theme->set_font("font", "CheckButton", Ref<Font>());
theme->set_font_size("font_size", "CheckButton", -1);
@@ -358,8 +335,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "CheckButton", control_font_disabled_color);
theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1));
- theme->set_constant("hseparation", "CheckButton", 4 * scale);
- theme->set_constant("check_vadjust", "CheckButton", 0 * scale);
+ theme->set_constant("h_separation", "CheckButton", 4 * scale);
+ theme->set_constant("check_v_adjust", "CheckButton", 0 * scale);
theme->set_constant("outline_size", "CheckButton", 0);
// Label
@@ -389,16 +366,27 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// LineEdit
- theme->set_stylebox("normal", "LineEdit", make_stylebox(line_edit_png, 5, 5, 5, 5));
+ Ref<StyleBoxFlat> style_line_edit = make_flat_stylebox(style_normal_color);
+ // Add a line at the bottom to make LineEdits distinguishable from Buttons.
+ style_line_edit->set_border_width(SIDE_BOTTOM, 2);
+ style_line_edit->set_border_color(style_pressed_color);
+ theme->set_stylebox("normal", "LineEdit", style_line_edit);
+
theme->set_stylebox("focus", "LineEdit", focus);
- theme->set_stylebox("read_only", "LineEdit", make_stylebox(line_edit_disabled_png, 6, 6, 6, 6));
+
+ Ref<StyleBoxFlat> style_line_edit_read_only = make_flat_stylebox(style_disabled_color);
+ // Add a line at the bottom to make LineEdits distinguishable from Buttons.
+ style_line_edit_read_only->set_border_width(SIDE_BOTTOM, 2);
+ style_line_edit_read_only->set_border_color(style_pressed_color * Color(1, 1, 1, 0.5));
+ theme->set_stylebox("read_only", "LineEdit", style_line_edit_read_only);
theme->set_font("font", "LineEdit", Ref<Font>());
theme->set_font_size("font_size", "LineEdit", -1);
theme->set_color("font_color", "LineEdit", control_font_color);
- theme->set_color("font_selected_color", "LineEdit", Color(0, 0, 0));
- theme->set_color("font_uneditable_color", "LineEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_selected_color", "LineEdit", control_font_pressed_color);
+ theme->set_color("font_uneditable_color", "LineEdit", control_font_disabled_color);
+ theme->set_color("font_placeholder_color", "LineEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "LineEdit", Color(1, 1, 1));
theme->set_color("caret_color", "LineEdit", control_font_hover_color);
theme->set_color("selection_color", "LineEdit", control_selection_color);
@@ -407,13 +395,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("minimum_character_width", "LineEdit", 4);
theme->set_constant("outline_size", "LineEdit", 0);
+ theme->set_constant("caret_width", "LineEdit", 1);
- theme->set_icon("clear", "LineEdit", make_icon(line_edit_clear_png));
+ theme->set_icon("clear", "LineEdit", icons["line_edit_clear"]);
// ProgressBar
- theme->set_stylebox("bg", "ProgressBar", make_stylebox(progress_bar_png, 4, 4, 4, 4, 0, 0, 0, 0));
- theme->set_stylebox("fg", "ProgressBar", make_stylebox(progress_fill_png, 6, 6, 6, 6, 2, 1, 2, 1));
+ theme->set_stylebox("bg", "ProgressBar", make_flat_stylebox(style_disabled_color, 2, 2, 2, 2, 6));
+ theme->set_stylebox("fg", "ProgressBar", make_flat_stylebox(style_progress_color, 2, 2, 2, 2, 6));
theme->set_font("font", "ProgressBar", Ref<Font>());
theme->set_font_size("font_size", "ProgressBar", -1);
@@ -426,47 +415,49 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// TextEdit
- theme->set_stylebox("normal", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
+ theme->set_stylebox("normal", "TextEdit", style_line_edit);
theme->set_stylebox("focus", "TextEdit", focus);
- theme->set_stylebox("read_only", "TextEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0));
+ theme->set_stylebox("read_only", "TextEdit", style_line_edit_read_only);
- theme->set_icon("tab", "TextEdit", make_icon(tab_png));
- theme->set_icon("space", "TextEdit", make_icon(space_png));
+ theme->set_icon("tab", "TextEdit", icons["text_edit_tab"]);
+ theme->set_icon("space", "TextEdit", icons["text_edit_space"]);
theme->set_font("font", "TextEdit", Ref<Font>());
theme->set_font_size("font_size", "TextEdit", -1);
theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0));
theme->set_color("font_color", "TextEdit", control_font_color);
- theme->set_color("font_selected_color", "TextEdit", Color(0, 0, 0));
- theme->set_color("font_readonly_color", "TextEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_selected_color", "TextEdit", control_font_pressed_color);
+ theme->set_color("font_readonly_color", "TextEdit", control_font_disabled_color);
+ theme->set_color("font_placeholder_color", "TextEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "TextEdit", Color(1, 1, 1));
theme->set_color("selection_color", "TextEdit", control_selection_color);
theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8));
theme->set_color("caret_color", "TextEdit", control_font_color);
theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0));
- theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15));
+ theme->set_color("word_highlighted_color", "TextEdit", Color(0.5, 0.5, 0.5, 0.25));
theme->set_color("search_result_color", "TextEdit", Color(0.3, 0.3, 0.3));
theme->set_color("search_result_border_color", "TextEdit", Color(0.3, 0.3, 0.3, 0.4));
theme->set_constant("line_spacing", "TextEdit", 4 * scale);
theme->set_constant("outline_size", "TextEdit", 0);
+ theme->set_constant("caret_width", "TextEdit", 1);
// CodeEdit
- theme->set_stylebox("normal", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
+ theme->set_stylebox("normal", "CodeEdit", style_line_edit);
theme->set_stylebox("focus", "CodeEdit", focus);
- theme->set_stylebox("read_only", "CodeEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0));
- theme->set_stylebox("completion", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
-
- theme->set_icon("tab", "CodeEdit", make_icon(tab_png));
- theme->set_icon("space", "CodeEdit", make_icon(space_png));
- theme->set_icon("breakpoint", "CodeEdit", make_icon(graph_port_png));
- theme->set_icon("bookmark", "CodeEdit", make_icon(bookmark_png));
- theme->set_icon("executing_line", "CodeEdit", make_icon(arrow_right_png));
- theme->set_icon("can_fold", "CodeEdit", make_icon(arrow_down_png));
- theme->set_icon("folded", "CodeEdit", make_icon(arrow_right_png));
- theme->set_icon("folded_eol_icon", "CodeEdit", make_icon(ellipsis_png));
+ theme->set_stylebox("read_only", "CodeEdit", style_line_edit_read_only);
+ theme->set_stylebox("completion", "CodeEdit", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+
+ theme->set_icon("tab", "CodeEdit", icons["text_edit_tab"]);
+ theme->set_icon("space", "CodeEdit", icons["text_edit_space"]);
+ theme->set_icon("breakpoint", "CodeEdit", icons["breakpoint"]);
+ theme->set_icon("bookmark", "CodeEdit", icons["bookmark"]);
+ theme->set_icon("executing_line", "CodeEdit", icons["arrow_right"]);
+ theme->set_icon("can_fold", "CodeEdit", icons["arrow_down"]);
+ theme->set_icon("folded", "CodeEdit", icons["arrow_right"]);
+ theme->set_icon("folded_eol_icon", "CodeEdit", icons["text_edit_ellipsis"]);
theme->set_font("font", "CodeEdit", Ref<Font>());
theme->set_font_size("font_size", "CodeEdit", -1);
@@ -475,11 +466,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("completion_background_color", "CodeEdit", Color(0.17, 0.16, 0.2));
theme->set_color("completion_selected_color", "CodeEdit", Color(0.26, 0.26, 0.27));
theme->set_color("completion_existing_color", "CodeEdit", Color(0.87, 0.87, 0.87, 0.13));
- theme->set_color("completion_scroll_color", "CodeEdit", control_font_pressed_color);
+ theme->set_color("completion_scroll_color", "CodeEdit", control_font_pressed_color * Color(1, 1, 1, 0.29));
theme->set_color("completion_font_color", "CodeEdit", Color(0.67, 0.67, 0.67));
theme->set_color("font_color", "CodeEdit", control_font_color);
theme->set_color("font_selected_color", "CodeEdit", Color(0, 0, 0));
theme->set_color("font_readonly_color", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_placeholder_color", "CodeEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "CodeEdit", Color(1, 1, 1));
theme->set_color("selection_color", "CodeEdit", control_selection_color);
theme->set_color("bookmark_color", "CodeEdit", Color(0.5, 0.64, 1, 0.8));
@@ -498,19 +490,24 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("completion_lines", "CodeEdit", 7);
theme->set_constant("completion_max_width", "CodeEdit", 50);
- theme->set_constant("completion_scroll_width", "CodeEdit", 3);
+ theme->set_constant("completion_scroll_width", "CodeEdit", 6);
theme->set_constant("line_spacing", "CodeEdit", 4 * scale);
theme->set_constant("outline_size", "CodeEdit", 0);
Ref<Texture2D> empty_icon = memnew(ImageTexture);
+ const Ref<StyleBoxFlat> style_scrollbar = make_flat_stylebox(style_normal_color, 4, 4, 4, 4, 10);
+ Ref<StyleBoxFlat> style_scrollbar_grabber = make_flat_stylebox(style_progress_color, 4, 4, 4, 4, 10);
+ Ref<StyleBoxFlat> style_scrollbar_grabber_highlight = make_flat_stylebox(style_focus_color, 4, 4, 4, 4, 10);
+ Ref<StyleBoxFlat> style_scrollbar_grabber_pressed = make_flat_stylebox(style_focus_color * Color(0.75, 0.75, 0.75), 4, 4, 4, 4, 10);
+
// HScrollBar
- theme->set_stylebox("scroll", "HScrollBar", make_stylebox(scroll_bg_png, 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(scroll_bg_png, 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("grabber", "HScrollBar", make_stylebox(scroll_grabber_png, 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(scroll_grabber_hl_png, 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(scroll_grabber_pressed_png, 5, 5, 5, 5, 2, 2, 2, 2));
+ theme->set_stylebox("scroll", "HScrollBar", style_scrollbar);
+ theme->set_stylebox("scroll_focus", "HScrollBar", focus);
+ theme->set_stylebox("grabber", "HScrollBar", style_scrollbar_grabber);
+ theme->set_stylebox("grabber_highlight", "HScrollBar", style_scrollbar_grabber_highlight);
+ theme->set_stylebox("grabber_pressed", "HScrollBar", style_scrollbar_grabber_pressed);
theme->set_icon("increment", "HScrollBar", empty_icon);
theme->set_icon("increment_highlight", "HScrollBar", empty_icon);
@@ -521,11 +518,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// VScrollBar
- theme->set_stylebox("scroll", "VScrollBar", make_stylebox(scroll_bg_png, 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(scroll_bg_png, 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("grabber", "VScrollBar", make_stylebox(scroll_grabber_png, 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(scroll_grabber_hl_png, 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(scroll_grabber_pressed_png, 5, 5, 5, 5, 2, 2, 2, 2));
+ theme->set_stylebox("scroll", "VScrollBar", style_scrollbar);
+ theme->set_stylebox("scroll_focus", "VScrollBar", focus);
+ theme->set_stylebox("grabber", "VScrollBar", style_scrollbar_grabber);
+ theme->set_stylebox("grabber_highlight", "VScrollBar", style_scrollbar_grabber_highlight);
+ theme->set_stylebox("grabber_pressed", "VScrollBar", style_scrollbar_grabber_pressed);
theme->set_icon("increment", "VScrollBar", empty_icon);
theme->set_icon("increment_highlight", "VScrollBar", empty_icon);
@@ -534,31 +531,35 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("decrement_highlight", "VScrollBar", empty_icon);
theme->set_icon("decrement_pressed", "VScrollBar", empty_icon);
+ const Ref<StyleBoxFlat> style_slider = make_flat_stylebox(style_normal_color, 4, 4, 4, 4, 4);
+ const Ref<StyleBoxFlat> style_slider_grabber = make_flat_stylebox(style_progress_color, 4, 4, 4, 4, 4);
+ const Ref<StyleBoxFlat> style_slider_grabber_highlight = make_flat_stylebox(style_focus_color, 4, 4, 4, 4, 4);
+
// HSlider
- theme->set_stylebox("slider", "HSlider", make_stylebox(hslider_bg_png, 4, 4, 4, 4));
- theme->set_stylebox("grabber_area", "HSlider", make_stylebox(hslider_bg_png, 4, 4, 4, 4));
- theme->set_stylebox("grabber_area_highlight", "HSlider", make_stylebox(hslider_bg_png, 4, 4, 4, 4));
+ theme->set_stylebox("slider", "HSlider", style_slider);
+ theme->set_stylebox("grabber_area", "HSlider", style_slider_grabber);
+ theme->set_stylebox("grabber_area_highlight", "HSlider", style_slider_grabber_highlight);
- theme->set_icon("grabber", "HSlider", make_icon(hslider_grabber_png));
- theme->set_icon("grabber_highlight", "HSlider", make_icon(hslider_grabber_hl_png));
- theme->set_icon("grabber_disabled", "HSlider", make_icon(hslider_grabber_disabled_png));
- theme->set_icon("tick", "HSlider", make_icon(hslider_tick_png));
+ theme->set_icon("grabber", "HSlider", icons["slider_grabber"]);
+ theme->set_icon("grabber_highlight", "HSlider", icons["slider_grabber_hl"]);
+ theme->set_icon("grabber_disabled", "HSlider", icons["slider_grabber_disabled"]);
+ theme->set_icon("tick", "HSlider", icons["hslider_tick"]);
// VSlider
- theme->set_stylebox("slider", "VSlider", make_stylebox(vslider_bg_png, 4, 4, 4, 4));
- theme->set_stylebox("grabber_area", "VSlider", make_stylebox(vslider_bg_png, 4, 4, 4, 4));
- theme->set_stylebox("grabber_area_highlight", "VSlider", make_stylebox(vslider_bg_png, 4, 4, 4, 4));
+ theme->set_stylebox("slider", "VSlider", style_slider);
+ theme->set_stylebox("grabber_area", "VSlider", style_slider_grabber);
+ theme->set_stylebox("grabber_area_highlight", "VSlider", style_slider_grabber_highlight);
- theme->set_icon("grabber", "VSlider", make_icon(vslider_grabber_png));
- theme->set_icon("grabber_highlight", "VSlider", make_icon(vslider_grabber_hl_png));
- theme->set_icon("grabber_disabled", "VSlider", make_icon(vslider_grabber_disabled_png));
- theme->set_icon("tick", "VSlider", make_icon(vslider_tick_png));
+ theme->set_icon("grabber", "VSlider", icons["slider_grabber"]);
+ theme->set_icon("grabber_highlight", "VSlider", icons["slider_grabber_hl"]);
+ theme->set_icon("grabber_disabled", "VSlider", icons["slider_grabber_disabled"]);
+ theme->set_icon("tick", "VSlider", icons["vslider_tick"]);
// SpinBox
- theme->set_icon("updown", "SpinBox", make_icon(spinbox_updown_png));
+ theme->set_icon("updown", "SpinBox", icons["updown"]);
// ScrollContainer
@@ -568,70 +569,93 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Window
- theme->set_stylebox("embedded_border", "Window", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));
+ theme->set_stylebox("embedded_border", "Window", sb_expand(make_flat_stylebox(style_popup_color, 10, 28, 10, 8), 8, 32, 8, 6));
theme->set_constant("scaleborder_size", "Window", 4 * scale);
- theme->set_font("title_font", "Window", large_font);
+ theme->set_font("title_font", "Window", Ref<Font>());
theme->set_font_size("title_font_size", "Window", -1);
-
- theme->set_color("title_color", "Window", Color(0, 0, 0));
+ theme->set_color("title_color", "Window", control_font_color);
theme->set_color("title_outline_modulate", "Window", Color(1, 1, 1));
-
theme->set_constant("title_outline_size", "Window", 0);
- theme->set_constant("title_height", "Window", 20 * scale);
+ theme->set_constant("title_height", "Window", 36 * scale);
theme->set_constant("resize_margin", "Window", 4 * scale);
- theme->set_icon("close", "Window", make_icon(close_png));
- theme->set_icon("close_pressed", "Window", make_icon(close_hl_png));
- theme->set_constant("close_h_ofs", "Window", 18 * scale);
- theme->set_constant("close_v_ofs", "Window", 18 * scale);
+ theme->set_icon("close", "Window", icons["close"]);
+ theme->set_icon("close_pressed", "Window", icons["close_hl"]);
+ theme->set_constant("close_h_offset", "Window", 18 * scale);
+ theme->set_constant("close_v_offset", "Window", 24 * scale);
+
+ // Dialogs
+
+ theme->set_constant("margin", "Dialogs", 8 * scale);
+ theme->set_constant("button_margin", "Dialogs", 32 * scale);
// AcceptDialog
- theme->set_stylebox("panel", "AcceptDialog", make_stylebox(dialog_bg_png, 0, 0, 0, 0));
+ theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, 0, 0, 0, 0));
// File Dialog
- theme->set_icon("parent_folder", "FileDialog", make_icon(icon_parent_folder_png));
- theme->set_icon("back_folder", "FileDialog", make_icon(arrow_left_png));
- theme->set_icon("forward_folder", "FileDialog", make_icon(arrow_right_png));
- theme->set_icon("reload", "FileDialog", make_icon(icon_reload_png));
- theme->set_icon("toggle_hidden", "FileDialog", make_icon(icon_visibility_png));
+ theme->set_icon("parent_folder", "FileDialog", icons["folder_up"]);
+ theme->set_icon("back_folder", "FileDialog", icons["arrow_left"]);
+ theme->set_icon("forward_folder", "FileDialog", icons["arrow_right"]);
+ theme->set_icon("reload", "FileDialog", icons["reload"]);
+ theme->set_icon("toggle_hidden", "FileDialog", icons["visibility_visible"]);
+ theme->set_icon("folder", "FileDialog", icons["folder"]);
+ theme->set_icon("file", "FileDialog", icons["file"]);
+ theme->set_color("folder_icon_modulate", "FileDialog", Color(1, 1, 1));
+ theme->set_color("file_icon_modulate", "FileDialog", Color(1, 1, 1));
+ theme->set_color("files_disabled", "FileDialog", Color(0, 0, 0, 0.7));
// Popup
- Ref<StyleBoxTexture> style_pp = sb_expand(make_stylebox(popup_bg_png, 5, 5, 5, 5, 4, 4, 4, 4), 2, 2, 2, 2);
-
- Ref<StyleBoxTexture> selected = make_stylebox(selection_png, 6, 6, 6, 6);
- for (int i = 0; i < 4; i++) {
- selected->set_expand_margin_size(Side(i), 2 * scale);
- }
-
- theme->set_stylebox("panel", "PopupPanel", style_pp);
+ theme->set_stylebox("panel", "PopupPanel", make_flat_stylebox(style_normal_color));
// PopupDialog
- Ref<StyleBoxTexture> style_pd = make_stylebox(popup_bg_png, 4, 4, 4, 4, 10, 10, 10, 10);
- theme->set_stylebox("panel", "PopupDialog", style_pd);
+ theme->set_stylebox("panel", "PopupDialog", make_flat_stylebox(style_normal_color));
// PopupMenu
- theme->set_stylebox("panel", "PopupMenu", style_pd);
- theme->set_stylebox("panel_disabled", "PopupMenu", make_stylebox(popup_bg_disabled_png, 4, 4, 4, 4));
- theme->set_stylebox("hover", "PopupMenu", selected);
- theme->set_stylebox("separator", "PopupMenu", make_stylebox(vseparator_png, 3, 3, 3, 3));
- theme->set_stylebox("labeled_separator_left", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0));
- theme->set_stylebox("labeled_separator_right", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0));
-
- theme->set_icon("checked", "PopupMenu", make_icon(checked_png));
- theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png));
- theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png));
- theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png));
- theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png));
- theme->set_icon("submenu_mirrored", "PopupMenu", make_icon(submenu_mirrored_png));
+ Ref<StyleBoxLine> separator_horizontal = memnew(StyleBoxLine);
+ separator_horizontal->set_thickness(Math::round(scale));
+ separator_horizontal->set_color(style_separator_color);
+ separator_horizontal->set_default_margin(SIDE_LEFT, default_margin);
+ separator_horizontal->set_default_margin(SIDE_TOP, 0);
+ separator_horizontal->set_default_margin(SIDE_RIGHT, default_margin);
+ separator_horizontal->set_default_margin(SIDE_BOTTOM, 0);
+ Ref<StyleBoxLine> separator_vertical = separator_horizontal->duplicate();
+ separator_vertical->set_vertical(true);
+ separator_vertical->set_default_margin(SIDE_LEFT, 0);
+ separator_vertical->set_default_margin(SIDE_TOP, default_margin);
+ separator_vertical->set_default_margin(SIDE_RIGHT, 0);
+ separator_vertical->set_default_margin(SIDE_BOTTOM, default_margin);
+
+ // Always display a border for PopupMenus so they can be distinguished from their background.
+ Ref<StyleBoxFlat> style_popup_panel = make_flat_stylebox(style_popup_color);
+ style_popup_panel->set_border_width_all(2);
+ style_popup_panel->set_border_color(style_popup_border_color);
+ Ref<StyleBoxFlat> style_popup_panel_disabled = style_popup_panel->duplicate();
+ style_popup_panel_disabled->set_bg_color(style_disabled_color);
+
+ theme->set_stylebox("panel", "PopupMenu", style_popup_panel);
+ theme->set_stylebox("panel_disabled", "PopupMenu", style_popup_panel_disabled);
+ theme->set_stylebox("hover", "PopupMenu", make_flat_stylebox(style_popup_hover_color));
+ theme->set_stylebox("separator", "PopupMenu", separator_horizontal);
+ theme->set_stylebox("labeled_separator_left", "PopupMenu", separator_horizontal);
+ theme->set_stylebox("labeled_separator_right", "PopupMenu", separator_horizontal);
+
+ theme->set_icon("checked", "PopupMenu", icons["checked"]);
+ theme->set_icon("unchecked", "PopupMenu", icons["unchecked"]);
+ theme->set_icon("radio_checked", "PopupMenu", icons["radio_checked"]);
+ theme->set_icon("radio_unchecked", "PopupMenu", icons["radio_unchecked"]);
+ theme->set_icon("submenu", "PopupMenu", icons["popup_menu_arrow_right"]);
+ theme->set_icon("submenu_mirrored", "PopupMenu", icons["popup_menu_arrow_left"]);
theme->set_font("font", "PopupMenu", Ref<Font>());
+ theme->set_font("font_separator", "PopupMenu", Ref<Font>());
theme->set_font_size("font_size", "PopupMenu", -1);
+ theme->set_font_size("font_separator_size", "PopupMenu", -1);
theme->set_color("font_color", "PopupMenu", control_font_color);
theme->set_color("font_accelerator_color", "PopupMenu", Color(0.7, 0.7, 0.7, 0.8));
@@ -639,73 +663,76 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_hover_color", "PopupMenu", control_font_color);
theme->set_color("font_separator_color", "PopupMenu", control_font_color);
theme->set_color("font_outline_color", "PopupMenu", Color(1, 1, 1));
+ theme->set_color("font_separator_outline_color", "PopupMenu", Color(1, 1, 1));
- theme->set_constant("hseparation", "PopupMenu", 4 * scale);
- theme->set_constant("vseparation", "PopupMenu", 4 * scale);
+ theme->set_constant("h_separation", "PopupMenu", 4 * scale);
+ theme->set_constant("v_separation", "PopupMenu", 4 * scale);
theme->set_constant("outline_size", "PopupMenu", 0);
+ theme->set_constant("separator_outline_size", "PopupMenu", 0);
theme->set_constant("item_start_padding", "PopupMenu", 2 * scale);
theme->set_constant("item_end_padding", "PopupMenu", 2 * scale);
// GraphNode
-
- Ref<StyleBoxTexture> graphsb = make_stylebox(graph_node_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graphsbcomment = make_stylebox(graph_node_comment_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graphsbcommentselected = make_stylebox(graph_node_comment_focus_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graphsbselected = make_stylebox(graph_node_selected_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graphsbdefault = make_stylebox(graph_node_default_png, 4, 4, 4, 4, 6, 4, 4, 4);
- Ref<StyleBoxTexture> graphsbdeffocus = make_stylebox(graph_node_default_focus_png, 4, 4, 4, 4, 6, 4, 4, 4);
- Ref<StyleBoxTexture> graph_bpoint = make_stylebox(graph_node_breakpoint_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 6);
-
- //graphsb->set_expand_margin_size(SIDE_LEFT,10);
- //graphsb->set_expand_margin_size(SIDE_RIGHT,10);
- theme->set_stylebox("frame", "GraphNode", graphsb);
- theme->set_stylebox("selectedframe", "GraphNode", graphsbselected);
- theme->set_stylebox("defaultframe", "GraphNode", graphsbdefault);
- theme->set_stylebox("defaultfocus", "GraphNode", graphsbdeffocus);
- theme->set_stylebox("comment", "GraphNode", graphsbcomment);
- theme->set_stylebox("commentfocus", "GraphNode", graphsbcommentselected);
- theme->set_stylebox("breakpoint", "GraphNode", graph_bpoint);
- theme->set_stylebox("position", "GraphNode", graph_position);
- theme->set_constant("separation", "GraphNode", 1 * scale);
- theme->set_icon("port", "GraphNode", make_icon(graph_port_png));
- theme->set_icon("close", "GraphNode", make_icon(graph_node_close_png));
- theme->set_icon("resizer", "GraphNode", make_icon(window_resizer_png));
+ Ref<StyleBoxFlat> graphnode_normal = make_flat_stylebox(style_normal_color, 18, 42, 18, 12);
+ graphnode_normal->set_border_width(SIDE_TOP, 30);
+ graphnode_normal->set_border_color(Color(0.325, 0.325, 0.325, 0.6));
+ Ref<StyleBoxFlat> graphnode_selected = graphnode_normal->duplicate();
+ graphnode_selected->set_border_color(Color(0.625, 0.625, 0.625, 0.6));
+ Ref<StyleBoxFlat> graphnode_comment_normal = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 3, true, 2);
+ graphnode_comment_normal->set_border_color(style_pressed_color);
+ Ref<StyleBoxFlat> graphnode_comment_selected = graphnode_comment_normal->duplicate();
+ graphnode_comment_selected->set_border_color(style_hover_color);
+ Ref<StyleBoxFlat> graphnode_breakpoint = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 6, true, 4);
+ graphnode_breakpoint->set_border_color(Color(0.9, 0.29, 0.3));
+ Ref<StyleBoxFlat> graphnode_position = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 6, true, 4);
+ graphnode_position->set_border_color(Color(0.98, 0.89, 0.27));
+ Ref<StyleBoxEmpty> graphnode_slot = make_empty_stylebox(0, 0, 0, 0);
+
+ theme->set_stylebox("frame", "GraphNode", graphnode_normal);
+ theme->set_stylebox("selected_frame", "GraphNode", graphnode_selected);
+ theme->set_stylebox("comment", "GraphNode", graphnode_comment_normal);
+ theme->set_stylebox("comment_focus", "GraphNode", graphnode_comment_selected);
+ theme->set_stylebox("breakpoint", "GraphNode", graphnode_breakpoint);
+ theme->set_stylebox("position", "GraphNode", graphnode_position);
+ theme->set_stylebox("slot", "GraphNode", graphnode_slot);
+
+ theme->set_icon("port", "GraphNode", icons["graph_port"]);
+ theme->set_icon("close", "GraphNode", icons["close"]);
+ theme->set_icon("resizer", "GraphNode", icons["resizer_se"]);
theme->set_font("title_font", "GraphNode", Ref<Font>());
- theme->set_color("title_color", "GraphNode", Color(0, 0, 0, 1));
- theme->set_color("close_color", "GraphNode", Color(0, 0, 0, 1));
- theme->set_color("resizer_color", "GraphNode", Color(0, 0, 0, 1));
- theme->set_constant("title_offset", "GraphNode", 20 * scale);
- theme->set_constant("close_offset", "GraphNode", 18 * scale);
- theme->set_constant("port_offset", "GraphNode", 3 * scale);
+ theme->set_color("title_color", "GraphNode", control_font_color);
+ theme->set_color("close_color", "GraphNode", control_font_color);
+ theme->set_color("resizer_color", "GraphNode", control_font_color);
+ theme->set_constant("separation", "GraphNode", 2 * scale);
+ theme->set_constant("title_offset", "GraphNode", 26 * scale);
+ theme->set_constant("close_offset", "GraphNode", 22 * scale);
+ theme->set_constant("close_h_offset", "GraphNode", 22 * scale);
+ theme->set_constant("port_offset", "GraphNode", 0);
// Tree
- Ref<StyleBoxTexture> tree_selected = make_stylebox(selection_png, 4, 4, 4, 4, 8, 0, 8, 0);
- Ref<StyleBoxTexture> tree_selected_oof = make_stylebox(selection_oof_png, 4, 4, 4, 4, 8, 0, 8, 0);
-
- theme->set_stylebox("bg", "Tree", make_stylebox(tree_bg_png, 4, 4, 4, 5));
+ theme->set_stylebox("bg", "Tree", make_flat_stylebox(style_normal_color, 4, 4, 4, 5));
theme->set_stylebox("bg_focus", "Tree", focus);
- theme->set_stylebox("selected", "Tree", tree_selected_oof);
- theme->set_stylebox("selected_focus", "Tree", tree_selected);
+ theme->set_stylebox("selected", "Tree", make_flat_stylebox(style_selected_color));
+ theme->set_stylebox("selected_focus", "Tree", make_flat_stylebox(style_selected_color));
theme->set_stylebox("cursor", "Tree", focus);
theme->set_stylebox("cursor_unfocused", "Tree", focus);
- theme->set_stylebox("button_pressed", "Tree", make_stylebox(button_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("title_button_normal", "Tree", make_stylebox(tree_title_png, 4, 4, 4, 4));
- theme->set_stylebox("title_button_pressed", "Tree", make_stylebox(tree_title_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("title_button_hover", "Tree", make_stylebox(tree_title_png, 4, 4, 4, 4));
- theme->set_stylebox("custom_button", "Tree", sb_button_normal);
- theme->set_stylebox("custom_button_pressed", "Tree", sb_button_pressed);
- theme->set_stylebox("custom_button_hover", "Tree", sb_button_hover);
-
- theme->set_icon("checked", "Tree", make_icon(checked_png));
- theme->set_icon("unchecked", "Tree", make_icon(unchecked_png));
- theme->set_icon("indeterminate", "Tree", make_icon(indeterminate_png));
- theme->set_icon("updown", "Tree", make_icon(updown_png));
- theme->set_icon("select_arrow", "Tree", make_icon(dropdown_png));
- theme->set_icon("arrow", "Tree", make_icon(arrow_down_png));
- theme->set_icon("arrow_collapsed", "Tree", make_icon(arrow_right_png));
- theme->set_icon("arrow_collapsed_mirrored", "Tree", make_icon(arrow_left_png));
+ theme->set_stylebox("button_pressed", "Tree", button_pressed);
+ theme->set_stylebox("title_button_normal", "Tree", make_flat_stylebox(style_pressed_color, 4, 4, 4, 4));
+ theme->set_stylebox("title_button_pressed", "Tree", make_flat_stylebox(style_hover_color, 4, 4, 4, 4));
+ theme->set_stylebox("title_button_hover", "Tree", make_flat_stylebox(style_normal_color, 4, 4, 4, 4));
+ theme->set_stylebox("custom_button", "Tree", button_normal);
+ theme->set_stylebox("custom_button_pressed", "Tree", button_pressed);
+ theme->set_stylebox("custom_button_hover", "Tree", button_hover);
+
+ theme->set_icon("checked", "Tree", icons["checked"]);
+ theme->set_icon("unchecked", "Tree", icons["unchecked"]);
+ theme->set_icon("indeterminate", "Tree", icons["indeterminate"]);
+ theme->set_icon("updown", "Tree", icons["updown"]);
+ theme->set_icon("select_arrow", "Tree", icons["option_button_arrow"]);
+ theme->set_icon("arrow", "Tree", icons["arrow_down"]);
+ theme->set_icon("arrow_collapsed", "Tree", icons["arrow_right"]);
+ theme->set_icon("arrow_collapsed_mirrored", "Tree", icons["arrow_left"]);
theme->set_font("title_button_font", "Tree", Ref<Font>());
theme->set_font("font", "Tree", Ref<Font>());
@@ -715,16 +742,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "Tree", control_font_low_color);
theme->set_color("font_selected_color", "Tree", control_font_pressed_color);
theme->set_color("font_outline_color", "Tree", Color(1, 1, 1));
- theme->set_color("guide_color", "Tree", Color(0, 0, 0, 0.1));
+ theme->set_color("guide_color", "Tree", Color(0.7, 0.7, 0.7, 0.25));
theme->set_color("drop_position_color", "Tree", Color(1, 0.3, 0.2));
theme->set_color("relationship_line_color", "Tree", Color(0.27, 0.27, 0.27));
theme->set_color("parent_hl_line_color", "Tree", Color(0.27, 0.27, 0.27));
theme->set_color("children_hl_line_color", "Tree", Color(0.27, 0.27, 0.27));
theme->set_color("custom_button_font_highlight", "Tree", control_font_hover_color);
- theme->set_constant("hseparation", "Tree", 4 * scale);
- theme->set_constant("vseparation", "Tree", 4 * scale);
- theme->set_constant("item_margin", "Tree", 12 * scale);
+ theme->set_constant("h_separation", "Tree", 4 * scale);
+ theme->set_constant("v_separation", "Tree", 4 * scale);
+ theme->set_constant("item_margin", "Tree", 16 * scale);
theme->set_constant("button_margin", "Tree", 4 * scale);
theme->set_constant("draw_relationship_lines", "Tree", 0);
theme->set_constant("relationship_line_width", "Tree", 1);
@@ -738,13 +765,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// ItemList
- Ref<StyleBoxTexture> item_selected = make_stylebox(selection_png, 4, 4, 4, 4, 8, 2, 8, 2);
- Ref<StyleBoxTexture> item_selected_oof = make_stylebox(selection_oof_png, 4, 4, 4, 4, 8, 2, 8, 2);
-
- theme->set_stylebox("bg", "ItemList", make_stylebox(tree_bg_png, 4, 4, 4, 5));
+ theme->set_stylebox("bg", "ItemList", make_flat_stylebox(style_normal_color));
theme->set_stylebox("bg_focus", "ItemList", focus);
- theme->set_constant("hseparation", "ItemList", 4);
- theme->set_constant("vseparation", "ItemList", 2);
+ theme->set_constant("h_separation", "ItemList", 4);
+ theme->set_constant("v_separation", "ItemList", 2);
theme->set_constant("icon_margin", "ItemList", 4);
theme->set_constant("line_separation", "ItemList", 2 * scale);
@@ -755,8 +779,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_selected_color", "ItemList", control_font_pressed_color);
theme->set_color("font_outline_color", "ItemList", Color(1, 1, 1));
theme->set_color("guide_color", "ItemList", Color(0, 0, 0, 0.1));
- theme->set_stylebox("selected", "ItemList", item_selected_oof);
- theme->set_stylebox("selected_focus", "ItemList", item_selected);
+ theme->set_stylebox("selected", "ItemList", make_flat_stylebox(style_selected_color));
+ theme->set_stylebox("selected_focus", "ItemList", make_flat_stylebox(style_selected_color));
theme->set_stylebox("cursor", "ItemList", focus);
theme->set_stylebox("cursor_unfocused", "ItemList", focus);
@@ -764,22 +788,29 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// TabContainer
- Ref<StyleBoxTexture> tc_sb = sb_expand(make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 4, 4, 4, 4), 3, 3, 3, 3);
-
- tc_sb->set_expand_margin_size(SIDE_TOP, 2 * scale);
- tc_sb->set_default_margin(SIDE_TOP, 8 * scale);
-
- theme->set_stylebox("tab_selected", "TabContainer", sb_expand(make_stylebox(tab_current_png, 4, 4, 4, 1, 16, 4, 16, 4), 2, 2, 2, 2));
- theme->set_stylebox("tab_unselected", "TabContainer", sb_expand(make_stylebox(tab_behind_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("tab_disabled", "TabContainer", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("panel", "TabContainer", tc_sb);
-
- theme->set_icon("increment", "TabContainer", make_icon(scroll_button_right_png));
- theme->set_icon("increment_highlight", "TabContainer", make_icon(scroll_button_right_hl_png));
- theme->set_icon("decrement", "TabContainer", make_icon(scroll_button_left_png));
- theme->set_icon("decrement_highlight", "TabContainer", make_icon(scroll_button_left_hl_png));
- theme->set_icon("menu", "TabContainer", make_icon(tab_menu_png));
- theme->set_icon("menu_highlight", "TabContainer", make_icon(tab_menu_hl_png));
+ Ref<StyleBoxFlat> style_tab_selected = make_flat_stylebox(style_normal_color, 10, 4, 10, 4, 0);
+ style_tab_selected->set_border_width(SIDE_TOP, Math::round(2 * scale));
+ style_tab_selected->set_border_color(style_focus_color);
+ Ref<StyleBoxFlat> style_tab_unselected = make_flat_stylebox(style_pressed_color, 10, 4, 10, 4, 0);
+ // Add some spacing between unselected tabs to make them easier to distinguish from each other.
+ style_tab_unselected->set_border_width(SIDE_LEFT, Math::round(scale));
+ style_tab_unselected->set_border_width(SIDE_RIGHT, Math::round(scale));
+ style_tab_unselected->set_border_color(style_popup_border_color);
+ Ref<StyleBoxFlat> style_tab_disabled = style_tab_unselected->duplicate();
+ style_tab_disabled->set_bg_color(style_disabled_color);
+
+ theme->set_stylebox("tab_selected", "TabContainer", style_tab_selected);
+ theme->set_stylebox("tab_unselected", "TabContainer", style_tab_unselected);
+ theme->set_stylebox("tab_disabled", "TabContainer", style_tab_disabled);
+ theme->set_stylebox("panel", "TabContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+
+ theme->set_icon("increment", "TabContainer", icons["scroll_button_right"]);
+ theme->set_icon("increment_highlight", "TabContainer", icons["scroll_button_right_hl"]);
+ theme->set_icon("decrement", "TabContainer", icons["scroll_button_left"]);
+ theme->set_icon("decrement_highlight", "TabContainer", icons["scroll_button_left_hl"]);
+ theme->set_icon("drop_mark", "TabContainer", icons["tabs_drop_mark"]);
+ theme->set_icon("menu", "TabContainer", icons["tabs_menu"]);
+ theme->set_icon("menu_highlight", "TabContainer", icons["tabs_menu_hl"]);
theme->set_font("font", "TabContainer", Ref<Font>());
theme->set_font_size("font_size", "TabContainer", -1);
@@ -788,6 +819,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_unselected_color", "TabContainer", control_font_low_color);
theme->set_color("font_disabled_color", "TabContainer", control_font_disabled_color);
theme->set_color("font_outline_color", "TabContainer", Color(1, 1, 1));
+ theme->set_color("drop_mark_color", "TabContainer", Color(1, 1, 1));
theme->set_constant("side_margin", "TabContainer", 8 * scale);
theme->set_constant("icon_separation", "TabContainer", 4 * scale);
@@ -795,17 +827,18 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// TabBar
- theme->set_stylebox("tab_selected", "TabBar", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2));
- theme->set_stylebox("tab_unselected", "TabBar", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3));
- theme->set_stylebox("tab_disabled", "TabBar", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("close_bg_pressed", "TabBar", make_stylebox(button_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("close_bg_highlight", "TabBar", make_stylebox(button_normal_png, 4, 4, 4, 4));
+ theme->set_stylebox("tab_selected", "TabBar", style_tab_selected);
+ theme->set_stylebox("tab_unselected", "TabBar", style_tab_unselected);
+ theme->set_stylebox("tab_disabled", "TabBar", style_tab_disabled);
+ theme->set_stylebox("button_pressed", "TabBar", button_pressed);
+ theme->set_stylebox("button_highlight", "TabBar", button_normal);
- theme->set_icon("increment", "TabBar", make_icon(scroll_button_right_png));
- theme->set_icon("increment_highlight", "TabBar", make_icon(scroll_button_right_hl_png));
- theme->set_icon("decrement", "TabBar", make_icon(scroll_button_left_png));
- theme->set_icon("decrement_highlight", "TabBar", make_icon(scroll_button_left_hl_png));
- theme->set_icon("close", "TabBar", make_icon(tab_close_png));
+ theme->set_icon("increment", "TabBar", icons["scroll_button_right"]);
+ theme->set_icon("increment_highlight", "TabBar", icons["scroll_button_right_hl"]);
+ theme->set_icon("decrement", "TabBar", icons["scroll_button_left"]);
+ theme->set_icon("decrement_highlight", "TabBar", icons["scroll_button_left_hl"]);
+ theme->set_icon("drop_mark", "TabBar", icons["tabs_drop_mark"]);
+ theme->set_icon("close", "TabBar", icons["close"]);
theme->set_font("font", "TabBar", Ref<Font>());
theme->set_font_size("font_size", "TabBar", -1);
@@ -814,35 +847,23 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_unselected_color", "TabBar", control_font_low_color);
theme->set_color("font_disabled_color", "TabBar", control_font_disabled_color);
theme->set_color("font_outline_color", "TabBar", Color(1, 1, 1));
+ theme->set_color("drop_mark_color", "TabBar", Color(1, 1, 1));
- theme->set_constant("hseparation", "TabBar", 4 * scale);
+ theme->set_constant("h_separation", "TabBar", 4 * scale);
theme->set_constant("outline_size", "TabBar", 0);
// Separators
- theme->set_stylebox("separator", "HSeparator", make_stylebox(vseparator_png, 3, 3, 3, 3));
- theme->set_stylebox("separator", "VSeparator", make_stylebox(hseparator_png, 3, 3, 3, 3));
+ theme->set_stylebox("separator", "HSeparator", separator_horizontal);
+ theme->set_stylebox("separator", "VSeparator", separator_vertical);
- theme->set_icon("close", "Icons", make_icon(icon_close_png));
+ theme->set_icon("close", "Icons", icons["close"]);
theme->set_font("normal", "Fonts", Ref<Font>());
- theme->set_font("large", "Fonts", large_font);
+ theme->set_font("large", "Fonts", Ref<Font>());
theme->set_constant("separation", "HSeparator", 4 * scale);
theme->set_constant("separation", "VSeparator", 4 * scale);
- // Dialogs
-
- theme->set_constant("margin", "Dialogs", 8 * scale);
- theme->set_constant("button_margin", "Dialogs", 32 * scale);
-
- // FileDialog
-
- theme->set_icon("folder", "FileDialog", make_icon(icon_folder_png));
- theme->set_icon("file", "FileDialog", make_icon(icon_file_png));
- theme->set_color("folder_icon_modulate", "FileDialog", Color(1, 1, 1));
- theme->set_color("file_icon_modulate", "FileDialog", Color(1, 1, 1));
- theme->set_color("files_disabled", "FileDialog", Color(0, 0, 0, 0.7));
-
// ColorPicker
theme->set_constant("margin", "ColorPicker", 4 * scale);
@@ -851,23 +872,22 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("h_width", "ColorPicker", 30 * scale);
theme->set_constant("label_width", "ColorPicker", 10 * scale);
- theme->set_icon("screen_picker", "ColorPicker", make_icon(icon_color_pick_png));
- theme->set_icon("add_preset", "ColorPicker", make_icon(icon_add_png));
- theme->set_icon("color_hue", "ColorPicker", make_icon(color_picker_hue_png));
- theme->set_icon("color_sample", "ColorPicker", make_icon(color_picker_sample_png));
- theme->set_icon("sample_bg", "ColorPicker", make_icon(mini_checkerboard_png));
- theme->set_icon("overbright_indicator", "ColorPicker", make_icon(overbright_indicator_png));
- theme->set_icon("bar_arrow", "ColorPicker", make_icon(bar_arrow_png));
- theme->set_icon("picker_cursor", "ColorPicker", make_icon(picker_cursor_png));
+ theme->set_icon("screen_picker", "ColorPicker", icons["color_picker_pipette"]);
+ theme->set_icon("add_preset", "ColorPicker", icons["add"]);
+ theme->set_icon("color_hue", "ColorPicker", icons["color_picker_hue"]);
+ theme->set_icon("sample_bg", "ColorPicker", icons["mini_checkerboard"]);
+ theme->set_icon("overbright_indicator", "ColorPicker", icons["color_picker_overbright"]);
+ theme->set_icon("bar_arrow", "ColorPicker", icons["color_picker_bar_arrow"]);
+ theme->set_icon("picker_cursor", "ColorPicker", icons["color_picker_cursor"]);
// ColorPickerButton
- theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png));
- theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal);
- theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed);
- theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover);
- theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled);
- theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
+ theme->set_icon("bg", "ColorPickerButton", icons["mini_checkerboard"]);
+ theme->set_stylebox("normal", "ColorPickerButton", button_normal);
+ theme->set_stylebox("pressed", "ColorPickerButton", button_pressed);
+ theme->set_stylebox("hover", "ColorPickerButton", button_hover);
+ theme->set_stylebox("disabled", "ColorPickerButton", button_disabled);
+ theme->set_stylebox("focus", "ColorPickerButton", focus);
theme->set_font("font", "ColorPickerButton", Ref<Font>());
theme->set_font_size("font_size", "ColorPickerButton", -1);
@@ -879,7 +899,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
- theme->set_constant("hseparation", "ColorPickerButton", 2 * scale);
+ theme->set_constant("h_separation", "ColorPickerButton", 2 * scale);
theme->set_constant("outline_size", "ColorPickerButton", 0);
// ColorPresetButton
@@ -890,24 +910,20 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
preset_sb->set_anti_aliased(false);
theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
- theme->set_icon("preset_bg", "ColorPresetButton", make_icon(mini_checkerboard_png));
- theme->set_icon("overbright_indicator", "ColorPresetButton", make_icon(overbright_indicator_png));
-
- // TooltipPanel
+ theme->set_icon("preset_bg", "ColorPresetButton", icons["mini_checkerboard"]);
+ theme->set_icon("overbright_indicator", "ColorPresetButton", icons["color_picker_overbright"]);
- Ref<StyleBoxTexture> style_tt = make_stylebox(tooltip_bg_png, 4, 4, 4, 4);
- for (int i = 0; i < 4; i++) {
- style_tt->set_expand_margin_size((Side)i, 4 * scale);
- }
+ // TooltipPanel + TooltipLabel
- theme->set_stylebox("panel", "TooltipPanel", style_tt);
+ theme->set_stylebox("panel", "TooltipPanel",
+ make_flat_stylebox(Color(0, 0, 0, 0.5), 2 * default_margin, 0.5 * default_margin, 2 * default_margin, 0.5 * default_margin));
theme->set_font("font", "TooltipLabel", Ref<Font>());
theme->set_font_size("font_size", "TooltipLabel", -1);
- theme->set_color("font_color", "TooltipLabel", Color(0, 0, 0));
- theme->set_color("font_shadow_color", "TooltipLabel", Color(0, 0, 0, 0.1));
- theme->set_color("font_outline_color", "TooltipLabel", Color(1, 1, 1));
+ theme->set_color("font_color", "TooltipLabel", control_font_color);
+ theme->set_color("font_shadow_color", "TooltipLabel", Color(0, 0, 0, 0));
+ theme->set_color("font_outline_color", "TooltipLabel", Color(0, 0, 0, 0));
theme->set_constant("shadow_offset_x", "TooltipLabel", 1);
theme->set_constant("shadow_offset_y", "TooltipLabel", 1);
@@ -919,9 +935,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("normal", "RichTextLabel", make_empty_stylebox(0, 0, 0, 0));
theme->set_font("normal_font", "RichTextLabel", Ref<Font>());
- theme->set_font("bold_font", "RichTextLabel", Ref<Font>());
- theme->set_font("italics_font", "RichTextLabel", Ref<Font>());
- theme->set_font("bold_italics_font", "RichTextLabel", Ref<Font>());
+ theme->set_font("bold_font", "RichTextLabel", bold_font);
+ theme->set_font("italics_font", "RichTextLabel", italics_font);
+ theme->set_font("bold_italics_font", "RichTextLabel", bold_italics_font);
theme->set_font("mono_font", "RichTextLabel", Ref<Font>());
theme->set_font_size("normal_font_size", "RichTextLabel", -1);
@@ -943,8 +959,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * scale);
theme->set_constant("line_separation", "RichTextLabel", 0 * scale);
- theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
- theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale);
+ theme->set_constant("table_h_separation", "RichTextLabel", 3 * scale);
+ theme->set_constant("table_v_separation", "RichTextLabel", 3 * scale);
theme->set_constant("outline_size", "RichTextLabel", 0);
@@ -954,11 +970,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Containers
- theme->set_stylebox("bg", "VSplitContainer", make_stylebox(vsplit_bg_png, 1, 1, 1, 1));
- theme->set_stylebox("bg", "HSplitContainer", make_stylebox(hsplit_bg_png, 1, 1, 1, 1));
-
- theme->set_icon("grabber", "VSplitContainer", make_icon(vsplitter_png));
- theme->set_icon("grabber", "HSplitContainer", make_icon(hsplitter_png));
+ theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]);
+ theme->set_icon("grabber", "HSplitContainer", icons["hsplitter"]);
theme->set_constant("separation", "HBoxContainer", 4 * scale);
theme->set_constant("separation", "VBoxContainer", 4 * scale);
@@ -966,23 +979,26 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("margin_top", "MarginContainer", 0 * scale);
theme->set_constant("margin_right", "MarginContainer", 0 * scale);
theme->set_constant("margin_bottom", "MarginContainer", 0 * scale);
- theme->set_constant("hseparation", "GridContainer", 4 * scale);
- theme->set_constant("vseparation", "GridContainer", 4 * scale);
+ theme->set_constant("h_separation", "GridContainer", 4 * scale);
+ theme->set_constant("v_separation", "GridContainer", 4 * scale);
theme->set_constant("separation", "HSplitContainer", 12 * scale);
theme->set_constant("separation", "VSplitContainer", 12 * scale);
theme->set_constant("autohide", "HSplitContainer", 1 * scale);
theme->set_constant("autohide", "VSplitContainer", 1 * scale);
-
- Ref<StyleBoxTexture> sb_pc = make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 7, 7, 7, 7);
- theme->set_stylebox("panel", "PanelContainer", sb_pc);
-
- theme->set_icon("minus", "GraphEdit", make_icon(icon_zoom_less_png));
- theme->set_icon("reset", "GraphEdit", make_icon(icon_zoom_reset_png));
- theme->set_icon("more", "GraphEdit", make_icon(icon_zoom_more_png));
- theme->set_icon("snap", "GraphEdit", make_icon(icon_snap_grid_png));
- theme->set_icon("minimap", "GraphEdit", make_icon(icon_grid_minimap_png));
- theme->set_icon("layout", "GraphEdit", make_icon(icon_grid_layout_png));
- theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5));
+ theme->set_constant("h_separation", "HFlowContainer", 4 * scale);
+ theme->set_constant("v_separation", "HFlowContainer", 4 * scale);
+ theme->set_constant("h_separation", "VFlowContainer", 4 * scale);
+ theme->set_constant("v_separation", "VFlowContainer", 4 * scale);
+
+ theme->set_stylebox("panel", "PanelContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+
+ theme->set_icon("minus", "GraphEdit", icons["zoom_less"]);
+ theme->set_icon("reset", "GraphEdit", icons["zoom_reset"]);
+ theme->set_icon("more", "GraphEdit", icons["zoom_more"]);
+ theme->set_icon("snap", "GraphEdit", icons["grid_snap"]);
+ theme->set_icon("minimap", "GraphEdit", icons["grid_minimap"]);
+ theme->set_icon("layout", "GraphEdit", icons["grid_layout"]);
+ theme->set_stylebox("bg", "GraphEdit", make_flat_stylebox(style_normal_color, 4, 4, 4, 5));
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
theme->set_color("selection_fill", "GraphEdit", Color(1, 1, 1, 0.3));
@@ -994,36 +1010,36 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Visual Node Ports
theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale);
- theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale);
+ theme->set_constant("port_grab_distance_vertical", "GraphEdit", 26 * scale);
theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
- Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0);
+ Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0, 0);
style_minimap_camera->set_border_color(Color(0.65, 0.65, 0.65, 0.45));
style_minimap_camera->set_border_width_all(1);
theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
- Ref<StyleBoxFlat> style_minimap_node = make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0);
- style_minimap_node->set_corner_radius_all(2);
- theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
+ theme->set_stylebox("node", "GraphEditMinimap", make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0, 2));
- Ref<Texture2D> resizer_icon = make_icon(window_resizer_png);
- theme->set_icon("resizer", "GraphEditMinimap", flip_icon(resizer_icon, true, true));
+ theme->set_icon("resizer", "GraphEditMinimap", icons["resizer_nw"]);
theme->set_color("resizer_color", "GraphEditMinimap", Color(1, 1, 1, 0.85));
// Theme
- default_icon = make_icon(error_icon_png);
- default_style = make_stylebox(error_icon_png, 2, 2, 2, 2);
-
- memdelete(tex_cache);
+ default_icon = icons["error_icon"];
+ // Same color as the error icon.
+ default_style = make_flat_stylebox(Color(1, 0.365, 0.365), 4, 4, 4, 4, 0, false, 2);
}
-void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
+void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel, TextServer::Hinting p_font_hinting, bool p_font_antialiased, bool p_font_msdf, bool p_font_generate_mipmaps) {
Ref<Theme> t;
t.instantiate();
Ref<StyleBox> default_style;
Ref<Texture2D> default_icon;
Ref<Font> default_font;
+ Ref<Font> bold_font;
+ Ref<Font> bold_italics_font;
+ Ref<Font> italics_font;
+ float default_scale = CLAMP(p_scale, 0.5, 8.0);
if (p_font.is_valid()) {
// Use the custom font defined in the Project Settings.
@@ -1038,32 +1054,55 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
Ref<FontData> dynamic_font_data;
dynamic_font_data.instantiate();
dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size);
+ dynamic_font_data->set_subpixel_positioning(p_font_subpixel);
+ dynamic_font_data->set_hinting(p_font_hinting);
+ dynamic_font_data->set_antialiased(p_font_antialiased);
+ dynamic_font_data->set_multichannel_signed_distance_field(p_font_msdf);
+ dynamic_font_data->set_generate_mipmaps(p_font_generate_mipmaps);
+
dynamic_font->add_data(dynamic_font_data);
default_font = dynamic_font;
}
- Ref<Font> large_font = default_font;
-
- float default_scale = 1.0;
- if (p_hidpi) {
- default_scale = 2.0;
+ if (default_font.is_valid()) {
+ bold_font.instantiate();
+ for (int i = 0; i < default_font->get_data_count(); i++) {
+ Ref<FontData> data = default_font->get_data(i)->duplicate();
+ data->set_embolden(1.2);
+ bold_font->add_data(data);
+ }
+
+ bold_italics_font.instantiate();
+ for (int i = 0; i < default_font->get_data_count(); i++) {
+ Ref<FontData> data = default_font->get_data(i)->duplicate();
+ data->set_embolden(1.2);
+ data->set_transform(Transform2D(1.0, 0.4, 0.0, 1.0, 0.0, 0.0));
+ bold_italics_font->add_data(data);
+ }
+
+ italics_font.instantiate();
+ for (int i = 0; i < default_font->get_data_count(); i++) {
+ Ref<FontData> data = default_font->get_data(i)->duplicate();
+ data->set_transform(Transform2D(1.0, 0.4, 0.0, 1.0, 0.0, 0.0));
+ italics_font->add_data(data);
+ }
}
- fill_default_theme(t, default_font, large_font, default_icon, default_style, default_scale);
+ fill_default_theme(t, default_font, bold_font, bold_italics_font, italics_font, default_icon, default_style, default_scale);
Theme::set_default(t);
- Theme::set_default_base_scale(default_scale);
- Theme::set_default_icon(default_icon);
- Theme::set_default_style(default_style);
- Theme::set_default_font(default_font);
- Theme::set_default_font_size(default_font_size);
+ Theme::set_fallback_base_scale(default_scale);
+ Theme::set_fallback_icon(default_icon);
+ Theme::set_fallback_style(default_style);
+ Theme::set_fallback_font(default_font);
+ Theme::set_fallback_font_size(default_font_size * default_scale);
}
void clear_default_theme() {
Theme::set_project_default(nullptr);
Theme::set_default(nullptr);
- Theme::set_default_icon(nullptr);
- Theme::set_default_style(nullptr);
- Theme::set_default_font(nullptr);
+ Theme::set_fallback_icon(nullptr);
+ Theme::set_fallback_style(nullptr);
+ Theme::set_fallback_font(nullptr);
}
diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h
index 4cd781e814..f777330a07 100644
--- a/scene/resources/default_theme/default_theme.h
+++ b/scene/resources/default_theme/default_theme.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,8 +35,8 @@
const int default_font_size = 16;
-void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &large_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale);
-void make_default_theme(bool p_hidpi, Ref<Font> p_font);
+void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale);
+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
diff --git a/scene/resources/default_theme/default_theme_icons_builders.py b/scene/resources/default_theme/default_theme_icons_builders.py
new file mode 100644
index 0000000000..4dd5819a23
--- /dev/null
+++ b/scene/resources/default_theme/default_theme_icons_builders.py
@@ -0,0 +1,78 @@
+"""Functions used to generate source files during build time
+
+All such functions are invoked in a subprocess on Windows to prevent build flakiness.
+
+"""
+
+import os
+from io import StringIO
+from platform_methods import subprocess_main
+
+
+# See also `editor/icons/editor_icons_builders.py`.
+def make_default_theme_icons_action(target, source, env):
+
+ dst = target[0]
+ svg_icons = source
+
+ icons_string = StringIO()
+
+ for f in svg_icons:
+
+ fname = str(f)
+
+ icons_string.write('\t"')
+
+ with open(fname, "rb") as svgf:
+ b = svgf.read(1)
+ while len(b) == 1:
+ icons_string.write("\\" + str(hex(ord(b)))[1:])
+ b = svgf.read(1)
+
+ icons_string.write('"')
+ if fname != svg_icons[-1]:
+ icons_string.write(",")
+ icons_string.write("\n")
+
+ s = StringIO()
+ s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n\n")
+ s.write('#include "modules/modules_enabled.gen.h"\n\n')
+ s.write("#ifndef _DEFAULT_THEME_ICONS_H\n")
+ s.write("#define _DEFAULT_THEME_ICONS_H\n")
+ s.write("static const int default_theme_icons_count = {};\n\n".format(len(svg_icons)))
+ s.write("#ifdef MODULE_SVG_ENABLED\n")
+ s.write("static const char *default_theme_icons_sources[] = {\n")
+ s.write(icons_string.getvalue())
+ s.write("};\n")
+ s.write("#endif // MODULE_SVG_ENABLED\n\n")
+ s.write("static const char *default_theme_icons_names[] = {\n")
+
+ index = 0
+ for f in svg_icons:
+
+ fname = str(f)
+
+ # Trim the `.svg` extension from the string.
+ icon_name = os.path.basename(fname)[:-4]
+
+ s.write('\t"{0}"'.format(icon_name))
+
+ if fname != svg_icons[-1]:
+ s.write(",")
+ s.write("\n")
+
+ index += 1
+
+ s.write("};\n")
+
+ s.write("#endif\n")
+
+ with open(dst, "w") as f:
+ f.write(s.getvalue())
+
+ s.close()
+ icons_string.close()
+
+
+if __name__ == "__main__":
+ subprocess_main(globals())
diff --git a/scene/resources/default_theme/dialog_bg.png b/scene/resources/default_theme/dialog_bg.png
deleted file mode 100644
index a23a10b48a..0000000000
--- a/scene/resources/default_theme/dialog_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/dropdown.png b/scene/resources/default_theme/dropdown.png
deleted file mode 100644
index b5d9ffbbb4..0000000000
--- a/scene/resources/default_theme/dropdown.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/ellipsis.png b/scene/resources/default_theme/ellipsis.png
deleted file mode 100644
index c949e2c95b..0000000000
--- a/scene/resources/default_theme/ellipsis.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/error_icon.png b/scene/resources/default_theme/error_icon.png
deleted file mode 100644
index 30336b91a4..0000000000
--- a/scene/resources/default_theme/error_icon.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/error_icon.svg b/scene/resources/default_theme/error_icon.svg
new file mode 100644
index 0000000000..2f5099aa29
--- /dev/null
+++ b/scene/resources/default_theme/error_icon.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1v8.5859l1.293-1.293a1.0001 1.0001 0 0 1 .69141-.29102 1.0001 1.0001 0 0 1 .72266.29102l2.293 2.293 2.293-2.293a1.0001 1.0001 0 0 1 1.4141 0l2.293 2.293 1-1v-3.5859h-5v-5h-7zm8 0v4h4zm-6 9.4141-2 2v2.5859h12v-2.5859l-.29297.29297a1.0001 1.0001 0 0 1 -1.4141 0l-2.293-2.293-2.293 2.293a1.0001 1.0001 0 0 1 -1.4141 0l-2.293-2.293z" fill="#ff5d5d" transform="translate(0 -.000017)"/></svg>
diff --git a/scene/resources/default_theme/error_icon.xpm b/scene/resources/default_theme/error_icon.xpm
deleted file mode 100644
index 666a60338d..0000000000
--- a/scene/resources/default_theme/error_icon.xpm
+++ /dev/null
@@ -1,38 +0,0 @@
-/* XPM */
-static const char * error_icon_xpm[] = {
-"32 32 3 1",
-" c None",
-". c #FF00FF",
-"+ c #000000",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"....++++.+++..+++...++..+++.....",
-"....+....+..+.+..+.+..+.+..+....",
-"....+....+..+.+..+.+..+.+..+....",
-"....+++..+++..+++..+..+.+++.....",
-"....+....+..+.+..+.+..+.+..+....",
-"....+....+..+.+..+.+..+.+..+....",
-"....++++.+..+.+..+..++..+..+....",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................"};
diff --git a/scene/resources/default_theme/file.svg b/scene/resources/default_theme/file.svg
new file mode 100644
index 0000000000..974644c5da
--- /dev/null
+++ b/scene/resources/default_theme/file.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1v14h12v-9h-5v-5zm8 0v4h4z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/focus.png b/scene/resources/default_theme/focus.png
deleted file mode 100644
index f51ea89e8f..0000000000
--- a/scene/resources/default_theme/focus.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/folder.svg b/scene/resources/default_theme/folder.svg
new file mode 100644
index 0000000000..ca4c8c9c70
--- /dev/null
+++ b/scene/resources/default_theme/folder.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2c-.549 0-1 .451-1 1v10c0 .549.451 1 1 1h12c.549 0 1-.451 1-1v-7c0-.549-.451-1-1-1h-4c-.549 0-1-.451-1-1v-1c0-.549-.451-1-1-1z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/folder_up.svg b/scene/resources/default_theme/folder_up.svg
new file mode 100644
index 0000000000..aaf5693d38
--- /dev/null
+++ b/scene/resources/default_theme/folder_up.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m3 8c.003.26.107.509.291.693.388.388 1.026.388 1.414 0l2.293-2.293v4.586c0 .553.448 1 1 1s1-.447 1-1v-4.586l2.293 2.293c.388.388 1.026.388 1.414 0 .388-.387.388-1.026 0-1.414l-4-4c-.388-.387-1.026-.387-1.414 0l-4 4c-.19.191-.295.451-.291.721z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/graph_node.png b/scene/resources/default_theme/graph_node.png
deleted file mode 100644
index d4b4dd3c1f..0000000000
--- a/scene/resources/default_theme/graph_node.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_breakpoint.png b/scene/resources/default_theme/graph_node_breakpoint.png
deleted file mode 100644
index e18c6f42e1..0000000000
--- a/scene/resources/default_theme/graph_node_breakpoint.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_close.png b/scene/resources/default_theme/graph_node_close.png
deleted file mode 100644
index 5c962ae1c6..0000000000
--- a/scene/resources/default_theme/graph_node_close.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_comment.png b/scene/resources/default_theme/graph_node_comment.png
deleted file mode 100644
index cdec1d1eac..0000000000
--- a/scene/resources/default_theme/graph_node_comment.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_comment_focus.png b/scene/resources/default_theme/graph_node_comment_focus.png
deleted file mode 100644
index 472a6b6f53..0000000000
--- a/scene/resources/default_theme/graph_node_comment_focus.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_default.png b/scene/resources/default_theme/graph_node_default.png
deleted file mode 100644
index 359bbdc205..0000000000
--- a/scene/resources/default_theme/graph_node_default.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_default_focus.png b/scene/resources/default_theme/graph_node_default_focus.png
deleted file mode 100644
index 204dd16ac0..0000000000
--- a/scene/resources/default_theme/graph_node_default_focus.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_position.png b/scene/resources/default_theme/graph_node_position.png
deleted file mode 100644
index 24c2759be6..0000000000
--- a/scene/resources/default_theme/graph_node_position.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_selected.png b/scene/resources/default_theme/graph_node_selected.png
deleted file mode 100644
index c52d88586b..0000000000
--- a/scene/resources/default_theme/graph_node_selected.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_port.png b/scene/resources/default_theme/graph_port.png
deleted file mode 100644
index 358934f4d8..0000000000
--- a/scene/resources/default_theme/graph_port.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_port.svg b/scene/resources/default_theme/graph_port.svg
new file mode 100644
index 0000000000..423819ea68
--- /dev/null
+++ b/scene/resources/default_theme/graph_port.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m10 5c0 2.743-2.257 5-5 5s-5-2.257-5-5 2.257-5 5-5 5 2.257 5 5" fill="#fff"/><path d="m8.667 5c0 2.011-1.656 3.667-3.667 3.667s-3.667-1.656-3.667-3.667 1.656-3.667 3.667-3.667 3.667 1.656 3.667 3.667" fill="#b2b2b2" fill-opacity=".65"/></g></svg>
diff --git a/scene/resources/default_theme/grid_layout.svg b/scene/resources/default_theme/grid_layout.svg
new file mode 100644
index 0000000000..d5cafca687
--- /dev/null
+++ b/scene/resources/default_theme/grid_layout.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="1.5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke-width="1.3"><g stroke="#fefffe"><path d="m1.87 6.541h2.917v2.917h-2.917z"/><path d="m6.536 6.541h2.917v2.917h-2.917z"/><path d="m11.202 6.541h2.917v2.917h-2.917z"/></g><g stroke="#e0e0e0" stroke-opacity=".65"><path d="m5.432 1.112-1.95 1.95 1.95 1.95" stroke-linejoin="miter" stroke-miterlimit="10"/><path d="m3.482 3.062h9.386"/><path d="m10.731 11.112 1.95 1.95-1.95 1.95" stroke-linejoin="miter" stroke-miterlimit="10"/><path d="m3.294 13.062h9.387"/></g></g></svg>
diff --git a/scene/resources/default_theme/grid_minimap.svg b/scene/resources/default_theme/grid_minimap.svg
new file mode 100644
index 0000000000..a7e5150bc3
--- /dev/null
+++ b/scene/resources/default_theme/grid_minimap.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m14 2.199v2.615l-2.625 1.313v-2.615zm-12 4.065 2.625 1.312v2.551l-2.625-1.313zm12 0v2.55l-2.625 1.313v-2.551zm-8 1.455h4v2.64h-4zm-4 2.56 2.625 1.313v2.521l-2.625-1.312zm12 0v2.522l-2.625 1.312v-2.521zm-8 1.455h4v2.641h-4zm1.701-8.109h2.299v2.734h-4.156s-.749.647-.875.641c-.131-.007-1.51-1.456-1.51-1.456l-1.459-.73v-2.615l.068.034s.027-.457.063-.676c.034-.212.197-.592.197-.592l-1.049-.524c-.079-.04-.167-.062-.256-.066-.354-.013-.648.27-.648.625v12c0 .237.134.453.346.559l4 2c.086.043.182.066.279.066h6c.097 0 .193-.023.279-.066l4-2c.212-.106.346-.322.346-.559v-12c0-.464-.489-.766-.904-.559l-3.869 1.934h-2.971s.033.417.016.625c-.03.346-.196.625-.196.625z" fill="#b2b2b2" fill-opacity=".65"/><path d="m5 6s-2.219-2.162-2.219-3.243c0-1.08 0-2.607 2.219-2.607s2.219 1.527 2.219 2.607c0 1.081-2.219 3.243-2.219 3.243z" fill="#fefffe"/></g></svg>
diff --git a/scene/resources/default_theme/grid_snap.svg b/scene/resources/default_theme/grid_snap.svg
new file mode 100644
index 0000000000..633c85fe79
--- /dev/null
+++ b/scene/resources/default_theme/grid_snap.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m3 0v3h-3v2h3v4h-3v2h3v3h2v-9h9v-2h-3v-3h-2v3h-4v-3zm4 13v2h2v-2zm6 0v2h2v-2z" fill="#b2b2b2" fill-opacity=".65"/><path d="m11 7c-2.194 0-4 1.806-4 4v2h2v-2c0-1.097.903-2 2-2s2 .903 2 2v2h2v-2c0-2.194-1.806-4-4-4z" fill="#fefffe"/></g></svg>
diff --git a/scene/resources/default_theme/hseparator.png b/scene/resources/default_theme/hseparator.png
deleted file mode 100644
index d4fd71ace5..0000000000
--- a/scene/resources/default_theme/hseparator.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_bg.png b/scene/resources/default_theme/hslider_bg.png
deleted file mode 100644
index b402bd370d..0000000000
--- a/scene/resources/default_theme/hslider_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber.png b/scene/resources/default_theme/hslider_grabber.png
deleted file mode 100644
index d273b491ee..0000000000
--- a/scene/resources/default_theme/hslider_grabber.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber_disabled.png b/scene/resources/default_theme/hslider_grabber_disabled.png
deleted file mode 100644
index dddd1a468e..0000000000
--- a/scene/resources/default_theme/hslider_grabber_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber_hl.png b/scene/resources/default_theme/hslider_grabber_hl.png
deleted file mode 100644
index e3defb3610..0000000000
--- a/scene/resources/default_theme/hslider_grabber_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_tick.png b/scene/resources/default_theme/hslider_tick.png
deleted file mode 100644
index 1ba19c37a1..0000000000
--- a/scene/resources/default_theme/hslider_tick.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_tick.svg b/scene/resources/default_theme/hslider_tick.svg
new file mode 100644
index 0000000000..a97d05b9e8
--- /dev/null
+++ b/scene/resources/default_theme/hslider_tick.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 4 15.999999" width="4" xmlns="http://www.w3.org/2000/svg"><path d="m1 0h2v16h-2z" fill="#fff" fill-opacity=".25" stroke-width=".285079"/></svg>
diff --git a/scene/resources/default_theme/hsplit_bg.png b/scene/resources/default_theme/hsplit_bg.png
deleted file mode 100644
index a5749f6d5c..0000000000
--- a/scene/resources/default_theme/hsplit_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hsplitter.png b/scene/resources/default_theme/hsplitter.png
deleted file mode 100644
index 2287753c9d..0000000000
--- a/scene/resources/default_theme/hsplitter.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hsplitter.svg b/scene/resources/default_theme/hsplitter.svg
new file mode 100644
index 0000000000..effc6996cc
--- /dev/null
+++ b/scene/resources/default_theme/hsplitter.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" viewBox="0 0 8 48" xmlns="http://www.w3.org/2000/svg"><path d="m4 4.012v39.976" fill="none" stroke="#808080" stroke-opacity=".65" stroke-width="1.7"/></svg>
diff --git a/scene/resources/default_theme/icon_add.png b/scene/resources/default_theme/icon_add.png
deleted file mode 100644
index eccb69b363..0000000000
--- a/scene/resources/default_theme/icon_add.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_close.png b/scene/resources/default_theme/icon_close.png
deleted file mode 100644
index 4d4ac4a551..0000000000
--- a/scene/resources/default_theme/icon_close.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_color_pick.png b/scene/resources/default_theme/icon_color_pick.png
deleted file mode 100644
index 46953febb8..0000000000
--- a/scene/resources/default_theme/icon_color_pick.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_file.png b/scene/resources/default_theme/icon_file.png
deleted file mode 100644
index bb4c361a8d..0000000000
--- a/scene/resources/default_theme/icon_file.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_folder.png b/scene/resources/default_theme/icon_folder.png
deleted file mode 100644
index d1b308e88d..0000000000
--- a/scene/resources/default_theme/icon_folder.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_grid_layout.png b/scene/resources/default_theme/icon_grid_layout.png
deleted file mode 100644
index a249252a79..0000000000
--- a/scene/resources/default_theme/icon_grid_layout.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_grid_minimap.png b/scene/resources/default_theme/icon_grid_minimap.png
deleted file mode 100644
index 00a6179d5e..0000000000
--- a/scene/resources/default_theme/icon_grid_minimap.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_parent_folder.png b/scene/resources/default_theme/icon_parent_folder.png
deleted file mode 100644
index 35d218722e..0000000000
--- a/scene/resources/default_theme/icon_parent_folder.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_reload.png b/scene/resources/default_theme/icon_reload.png
deleted file mode 100644
index bec5f3f4f9..0000000000
--- a/scene/resources/default_theme/icon_reload.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_snap_grid.png b/scene/resources/default_theme/icon_snap_grid.png
deleted file mode 100644
index 0680317d86..0000000000
--- a/scene/resources/default_theme/icon_snap_grid.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_visibility.png b/scene/resources/default_theme/icon_visibility.png
deleted file mode 100644
index b2df8cbdb8..0000000000
--- a/scene/resources/default_theme/icon_visibility.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_less.png b/scene/resources/default_theme/icon_zoom_less.png
deleted file mode 100644
index 03119c60ca..0000000000
--- a/scene/resources/default_theme/icon_zoom_less.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_more.png b/scene/resources/default_theme/icon_zoom_more.png
deleted file mode 100644
index 31467ec3de..0000000000
--- a/scene/resources/default_theme/icon_zoom_more.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_reset.png b/scene/resources/default_theme/icon_zoom_reset.png
deleted file mode 100644
index cac68c09fa..0000000000
--- a/scene/resources/default_theme/icon_zoom_reset.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/indeterminate.png b/scene/resources/default_theme/indeterminate.png
deleted file mode 100644
index 28a457b251..0000000000
--- a/scene/resources/default_theme/indeterminate.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/indeterminate.svg b/scene/resources/default_theme/indeterminate.svg
new file mode 100644
index 0000000000..2a742e1475
--- /dev/null
+++ b/scene/resources/default_theme/indeterminate.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#fff" fill-opacity=".75" stroke-width="1.16667"/><path d="m3 7h10v2h-10z" fill="#1a1a1a" stroke-linecap="square" stroke-opacity=".75" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/line_edit.png b/scene/resources/default_theme/line_edit.png
deleted file mode 100644
index 2b0c506f34..0000000000
--- a/scene/resources/default_theme/line_edit.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/line_edit_clear.png b/scene/resources/default_theme/line_edit_clear.png
deleted file mode 100644
index af2775a132..0000000000
--- a/scene/resources/default_theme/line_edit_clear.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/line_edit_clear.svg b/scene/resources/default_theme/line_edit_clear.svg
new file mode 100644
index 0000000000..3709c43410
--- /dev/null
+++ b/scene/resources/default_theme/line_edit_clear.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m1 3 2-2 5 5 5-5 2 2-5 5 5 5-2 2-5.302-5-4.698 5-2-2 5-5z" fill="#fff" fill-opacity=".75" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/line_edit_disabled.png b/scene/resources/default_theme/line_edit_disabled.png
deleted file mode 100644
index 69d78febd9..0000000000
--- a/scene/resources/default_theme/line_edit_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/make_header.py b/scene/resources/default_theme/make_header.py
deleted file mode 100755
index efad3b2815..0000000000
--- a/scene/resources/default_theme/make_header.py
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env python
-
-import glob
-import os
-
-enc = "utf-8"
-
-# Change to the directory where the script is located,
-# so that the script can be run from any location
-os.chdir(os.path.dirname(os.path.realpath(__file__)))
-
-# Generate include files
-
-f = open("theme_data.h", "wb")
-
-f.write(b"// THIS FILE HAS BEEN AUTOGENERATED, DON'T EDIT!!\n")
-
-# Generate png image block
-f.write(b"\n// png image block\n")
-
-pixmaps = glob.glob("*.png")
-pixmaps.sort()
-
-for x in pixmaps:
-
- var_str = x[:-4] + "_png"
-
- s = "\nstatic const unsigned char " + var_str + "[] = {\n\t"
- f.write(s.encode(enc))
-
- pngf = open(x, "rb")
-
- b = pngf.read(1)
- while len(b) == 1:
- f.write(hex(ord(b)).encode(enc))
- b = pngf.read(1)
- if len(b) == 1:
- f.write(b", ")
-
- f.write(b"\n};\n")
- pngf.close()
-
-# Generate shaders block
-f.write(b"\n// shaders block\n")
-
-shaders = glob.glob("*.gsl")
-shaders.sort()
-
-for x in shaders:
-
- var_str = x[:-4] + "_shader_code"
-
- s = "\nstatic const char *" + var_str + " = \n"
- f.write(s.encode(enc))
-
- sf = open(x, "rb")
-
- b = sf.readline()
- while b != "":
- if b.endswith("\r\n"):
- b = b[:-2]
- if b.endswith("\n"):
- b = b[:-1]
- s = ' "' + b
- f.write(s.encode(enc))
- b = sf.readline()
- if b != "":
- f.write(b'"\n')
-
- f.write(b'";\n')
- sf.close()
-
-f.close()
diff --git a/scene/resources/default_theme/mini_checkerboard.png b/scene/resources/default_theme/mini_checkerboard.png
deleted file mode 100644
index d8279bda80..0000000000
--- a/scene/resources/default_theme/mini_checkerboard.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/mini_checkerboard.svg b/scene/resources/default_theme/mini_checkerboard.svg
new file mode 100644
index 0000000000..03438f75a6
--- /dev/null
+++ b/scene/resources/default_theme/mini_checkerboard.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="2"><path d="m0 0v8h8v-8zm8 8v8h8v-8z" fill="#808080"/><path d="m8 0v8h8v-8zm0 8h-8v8h8z" fill="#fff"/></g></svg>
diff --git a/scene/resources/default_theme/option_arrow.png b/scene/resources/default_theme/option_arrow.png
deleted file mode 100644
index 40590fd60a..0000000000
--- a/scene/resources/default_theme/option_arrow.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_arrow.svg b/scene/resources/default_theme/option_button_arrow.svg
new file mode 100644
index 0000000000..1a33668708
--- /dev/null
+++ b/scene/resources/default_theme/option_button_arrow.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="m10 3c-.264.01-.514.117-.697.307l-3.293 3.293-3.293-3.293c-.189-.194-.447-.303-.717-.303-.897 0-1.34 1.091-.697 1.717l4 4c.39.39 1.023.39 1.414 0l4-4c.657-.632.195-1.742-.717-1.721z" fill="#b2b2b2" fill-opacity=".85" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/option_button_disabled.png b/scene/resources/default_theme/option_button_disabled.png
deleted file mode 100644
index 1961b673cd..0000000000
--- a/scene/resources/default_theme/option_button_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_disabled_mirrored.png b/scene/resources/default_theme/option_button_disabled_mirrored.png
deleted file mode 100644
index 9d149a35ca..0000000000
--- a/scene/resources/default_theme/option_button_disabled_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_hover.png b/scene/resources/default_theme/option_button_hover.png
deleted file mode 100644
index 826fe1c9ca..0000000000
--- a/scene/resources/default_theme/option_button_hover.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_hover_mirrored.png b/scene/resources/default_theme/option_button_hover_mirrored.png
deleted file mode 100644
index d49c165645..0000000000
--- a/scene/resources/default_theme/option_button_hover_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_normal.png b/scene/resources/default_theme/option_button_normal.png
deleted file mode 100644
index 43fc29e958..0000000000
--- a/scene/resources/default_theme/option_button_normal.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_normal_mirrored.png b/scene/resources/default_theme/option_button_normal_mirrored.png
deleted file mode 100644
index feec848f33..0000000000
--- a/scene/resources/default_theme/option_button_normal_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_pressed.png b/scene/resources/default_theme/option_button_pressed.png
deleted file mode 100644
index 68796f9d85..0000000000
--- a/scene/resources/default_theme/option_button_pressed.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_pressed_mirrored.png b/scene/resources/default_theme/option_button_pressed_mirrored.png
deleted file mode 100644
index 94cabb18d6..0000000000
--- a/scene/resources/default_theme/option_button_pressed_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/overbright_indicator.png b/scene/resources/default_theme/overbright_indicator.png
deleted file mode 100644
index e13f15dd02..0000000000
--- a/scene/resources/default_theme/overbright_indicator.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/panel_bg.png b/scene/resources/default_theme/panel_bg.png
deleted file mode 100644
index b496e2177e..0000000000
--- a/scene/resources/default_theme/panel_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/picker_cursor.png b/scene/resources/default_theme/picker_cursor.png
deleted file mode 100644
index 2f403492d2..0000000000
--- a/scene/resources/default_theme/picker_cursor.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/popup_bg.png b/scene/resources/default_theme/popup_bg.png
deleted file mode 100644
index 023029f936..0000000000
--- a/scene/resources/default_theme/popup_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/popup_bg_disabled.png b/scene/resources/default_theme/popup_bg_disabled.png
deleted file mode 100644
index 8eab5f27bc..0000000000
--- a/scene/resources/default_theme/popup_bg_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/popup_menu_arrow_left.svg b/scene/resources/default_theme/popup_menu_arrow_left.svg
new file mode 100644
index 0000000000..8fae265a3b
--- /dev/null
+++ b/scene/resources/default_theme/popup_menu_arrow_left.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" height="16" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 8 16" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m5.4999793 11-3-3 3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/popup_menu_arrow_right.svg b/scene/resources/default_theme/popup_menu_arrow_right.svg
new file mode 100644
index 0000000000..03f05fc46e
--- /dev/null
+++ b/scene/resources/default_theme/popup_menu_arrow_right.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" height="16" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 8 16" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m2.5000207 11 3-3-3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/popup_window.png b/scene/resources/default_theme/popup_window.png
deleted file mode 100644
index 442084049d..0000000000
--- a/scene/resources/default_theme/popup_window.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/progress_bar.png b/scene/resources/default_theme/progress_bar.png
deleted file mode 100644
index 057557e567..0000000000
--- a/scene/resources/default_theme/progress_bar.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/progress_fill.png b/scene/resources/default_theme/progress_fill.png
deleted file mode 100644
index e39bb2a021..0000000000
--- a/scene/resources/default_theme/progress_fill.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked.png b/scene/resources/default_theme/radio_checked.png
deleted file mode 100644
index 0ce575c15f..0000000000
--- a/scene/resources/default_theme/radio_checked.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked.svg b/scene/resources/default_theme/radio_checked.svg
new file mode 100644
index 0000000000..73d1c8e34a
--- /dev/null
+++ b/scene/resources/default_theme/radio_checked.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fff" fill-opacity=".75" stroke-width="2.33333"/><path d="m12 8a4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4 4 4 0 0 1 4 4" fill="#1a1a1a" stroke-width="1.33333"/></svg>
diff --git a/scene/resources/default_theme/radio_checked_disabled.png b/scene/resources/default_theme/radio_checked_disabled.png
deleted file mode 100644
index 72f08ecb96..0000000000
--- a/scene/resources/default_theme/radio_checked_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked_disabled.svg b/scene/resources/default_theme/radio_checked_disabled.svg
new file mode 100644
index 0000000000..ef5eff8d97
--- /dev/null
+++ b/scene/resources/default_theme/radio_checked_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fff" fill-opacity=".37" stroke-width="2.33333"/><path d="m12 8a4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4 4 4 0 0 1 4 4" fill="#1a1a1a" fill-opacity=".5" stroke-width="1.33333"/></svg>
diff --git a/scene/resources/default_theme/radio_unchecked.png b/scene/resources/default_theme/radio_unchecked.png
deleted file mode 100644
index fe5bcf6ab1..0000000000
--- a/scene/resources/default_theme/radio_unchecked.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_unchecked.svg b/scene/resources/default_theme/radio_unchecked.svg
new file mode 100644
index 0000000000..5429855db5
--- /dev/null
+++ b/scene/resources/default_theme/radio_unchecked.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#1a1a1a" fill-opacity=".5" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/radio_unchecked_disabled.png b/scene/resources/default_theme/radio_unchecked_disabled.png
deleted file mode 100644
index a8f4c1b555..0000000000
--- a/scene/resources/default_theme/radio_unchecked_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_unchecked_disabled.svg b/scene/resources/default_theme/radio_unchecked_disabled.svg
new file mode 100644
index 0000000000..1e827d9374
--- /dev/null
+++ b/scene/resources/default_theme/radio_unchecked_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#1a1a1a" fill-opacity=".25" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/reload.svg b/scene/resources/default_theme/reload.svg
new file mode 100644
index 0000000000..66809f8374
--- /dev/null
+++ b/scene/resources/default_theme/reload.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#b2b2b2" fill-rule="nonzero"><path d="m8.987 2c-3.292 0-6 2.708-6 6h2c0-2.194 1.806-4 4-4s4 1.806 4 4-1.806 4-4 4v2c3.291 0 6-2.708 6-6s-2.709-6-6-6z"/><path d="m7.013 7.989-1.5 2-1.5 2-1.5-2-1.5-2z"/></g></svg>
diff --git a/scene/resources/default_theme/resizer_nw.svg b/scene/resources/default_theme/resizer_nw.svg
new file mode 100644
index 0000000000..92e25c3be2
--- /dev/null
+++ b/scene/resources/default_theme/resizer_nw.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".65"><path d="m4 12c.552 0 1-.448 1-1v-6h6c.552 0 1-.448 1-1s-.448-1-1-1h-7c-.552 0-1 .448-1 1v7c0 .552.448 1 1 1z" fill="#fefffe" fill-rule="nonzero"/><circle cx="7.5" cy="7.5" fill="#b2b2b2" r="1.479"/></g></svg>
diff --git a/scene/resources/default_theme/resizer_se.svg b/scene/resources/default_theme/resizer_se.svg
new file mode 100644
index 0000000000..2d492e976e
--- /dev/null
+++ b/scene/resources/default_theme/resizer_se.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".65"><path d="m11 3c-.552 0-1 .448-1 1v6h-6c-.552 0-1 .448-1 1s.448 1 1 1h7c.552 0 1-.448 1-1v-7c0-.552-.448-1-1-1z" fill="#fefffe" fill-rule="nonzero"/><circle cx="7.5" cy="7.5" fill="#b2b2b2" r="1.479"/></g></svg>
diff --git a/scene/resources/default_theme/scroll_bg.png b/scene/resources/default_theme/scroll_bg.png
deleted file mode 100644
index fb151a48b1..0000000000
--- a/scene/resources/default_theme/scroll_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left.png b/scene/resources/default_theme/scroll_button_left.png
deleted file mode 100644
index e430cb4673..0000000000
--- a/scene/resources/default_theme/scroll_button_left.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left.svg b/scene/resources/default_theme/scroll_button_left.svg
new file mode 100644
index 0000000000..947b417a00
--- /dev/null
+++ b/scene/resources/default_theme/scroll_button_left.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefffe" fill-opacity=".75" r="6"/><path d="m9.014 4c-.27-.004-.53.102-.721.293l-3 3c-.388.388-.388 1.026 0 1.414l3 3c.388.388 1.026.388 1.414 0s.388-1.026 0-1.414l-2.293-2.293 2.293-2.293c.388-.388.388-1.026 0-1.414-.184-.184-.433-.289-.693-.293z" fill="#1a1a1a" fill-opacity=".65"/></svg>
diff --git a/scene/resources/default_theme/scroll_button_left_hl.png b/scene/resources/default_theme/scroll_button_left_hl.png
deleted file mode 100644
index 2a6ef17a34..0000000000
--- a/scene/resources/default_theme/scroll_button_left_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left_hl.svg b/scene/resources/default_theme/scroll_button_left_hl.svg
new file mode 100644
index 0000000000..fa092c861a
--- /dev/null
+++ b/scene/resources/default_theme/scroll_button_left_hl.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefffe" r="6"/><path d="m9.014 4c-.27-.004-.53.102-.721.293l-3 3c-.388.388-.388 1.026 0 1.414l3 3c.388.388 1.026.388 1.414 0s.388-1.026 0-1.414l-2.293-2.293 2.293-2.293c.388-.388.388-1.026 0-1.414-.184-.184-.433-.289-.693-.293z" fill="#1a1a1a"/></svg>
diff --git a/scene/resources/default_theme/scroll_button_right.png b/scene/resources/default_theme/scroll_button_right.png
deleted file mode 100644
index 4f61687aa4..0000000000
--- a/scene/resources/default_theme/scroll_button_right.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_right.svg b/scene/resources/default_theme/scroll_button_right.svg
new file mode 100644
index 0000000000..84619e7aa9
--- /dev/null
+++ b/scene/resources/default_theme/scroll_button_right.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefffe" fill-opacity=".75" r="6"/><path d="m6.986 4c.27-.004.53.102.721.293l3 3c.388.388.388 1.026 0 1.414l-3 3c-.388.388-1.026.388-1.414 0s-.388-1.026 0-1.414l2.293-2.293-2.293-2.293c-.388-.388-.388-1.026 0-1.414.184-.184.433-.289.693-.293z" fill="#1a1a1a" fill-opacity=".65"/></svg>
diff --git a/scene/resources/default_theme/scroll_button_right_hl.png b/scene/resources/default_theme/scroll_button_right_hl.png
deleted file mode 100644
index 10e2722509..0000000000
--- a/scene/resources/default_theme/scroll_button_right_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_right_hl.svg b/scene/resources/default_theme/scroll_button_right_hl.svg
new file mode 100644
index 0000000000..601f98c0d5
--- /dev/null
+++ b/scene/resources/default_theme/scroll_button_right_hl.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefffe" r="6"/><path d="m6.986 4c.27-.004.53.102.721.293l3 3c.388.388.388 1.026 0 1.414l-3 3c-.388.388-1.026.388-1.414 0s-.388-1.026 0-1.414l2.293-2.293-2.293-2.293c-.388-.388-.388-1.026 0-1.414.184-.184.433-.289.693-.293z" fill="#1a1a1a"/></svg>
diff --git a/scene/resources/default_theme/scroll_grabber.png b/scene/resources/default_theme/scroll_grabber.png
deleted file mode 100644
index 732725a28f..0000000000
--- a/scene/resources/default_theme/scroll_grabber.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber_hl.png b/scene/resources/default_theme/scroll_grabber_hl.png
deleted file mode 100644
index 21ee486e0b..0000000000
--- a/scene/resources/default_theme/scroll_grabber_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber_pressed.png b/scene/resources/default_theme/scroll_grabber_pressed.png
deleted file mode 100644
index f4886158fa..0000000000
--- a/scene/resources/default_theme/scroll_grabber_pressed.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/selection.png b/scene/resources/default_theme/selection.png
deleted file mode 100644
index 7d1c985b35..0000000000
--- a/scene/resources/default_theme/selection.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/selection_oof.png b/scene/resources/default_theme/selection_oof.png
deleted file mode 100644
index 2da0538389..0000000000
--- a/scene/resources/default_theme/selection_oof.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/slider_grabber.svg b/scene/resources/default_theme/slider_grabber.svg
new file mode 100644
index 0000000000..0813830134
--- /dev/null
+++ b/scene/resources/default_theme/slider_grabber.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fefefe" fill-opacity=".75" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/slider_grabber_disabled.svg b/scene/resources/default_theme/slider_grabber_disabled.svg
new file mode 100644
index 0000000000..659c2e9e14
--- /dev/null
+++ b/scene/resources/default_theme/slider_grabber_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fefefe" fill-opacity=".37" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/slider_grabber_hl.svg b/scene/resources/default_theme/slider_grabber_hl.svg
new file mode 100644
index 0000000000..d7d0594d67
--- /dev/null
+++ b/scene/resources/default_theme/slider_grabber_hl.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fefefe" fill-opacity=".997951" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/space.png b/scene/resources/default_theme/space.png
deleted file mode 100644
index 3c66316074..0000000000
--- a/scene/resources/default_theme/space.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/spinbox_updown.png b/scene/resources/default_theme/spinbox_updown.png
deleted file mode 100644
index 74fab19f34..0000000000
--- a/scene/resources/default_theme/spinbox_updown.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/submenu.png b/scene/resources/default_theme/submenu.png
deleted file mode 100644
index 8f7de446d4..0000000000
--- a/scene/resources/default_theme/submenu.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/submenu_mirrored.png b/scene/resources/default_theme/submenu_mirrored.png
deleted file mode 100644
index 1142b9ba9f..0000000000
--- a/scene/resources/default_theme/submenu_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab.png b/scene/resources/default_theme/tab.png
deleted file mode 100644
index 895daa65e2..0000000000
--- a/scene/resources/default_theme/tab.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_behind.png b/scene/resources/default_theme/tab_behind.png
deleted file mode 100644
index 2803d9db65..0000000000
--- a/scene/resources/default_theme/tab_behind.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_close.png b/scene/resources/default_theme/tab_close.png
deleted file mode 100644
index af2775a132..0000000000
--- a/scene/resources/default_theme/tab_close.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_container_bg.png b/scene/resources/default_theme/tab_container_bg.png
deleted file mode 100644
index c189b61b89..0000000000
--- a/scene/resources/default_theme/tab_container_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_current.png b/scene/resources/default_theme/tab_current.png
deleted file mode 100644
index d5641e917a..0000000000
--- a/scene/resources/default_theme/tab_current.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_disabled.png b/scene/resources/default_theme/tab_disabled.png
deleted file mode 100644
index a7c04effa3..0000000000
--- a/scene/resources/default_theme/tab_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_menu.png b/scene/resources/default_theme/tab_menu.png
deleted file mode 100644
index fa4421a28a..0000000000
--- a/scene/resources/default_theme/tab_menu.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_menu_hl.png b/scene/resources/default_theme/tab_menu_hl.png
deleted file mode 100644
index fa4421a28a..0000000000
--- a/scene/resources/default_theme/tab_menu_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tabs_drop_mark.svg b/scene/resources/default_theme/tabs_drop_mark.svg
new file mode 100644
index 0000000000..b1415bec45
--- /dev/null
+++ b/scene/resources/default_theme/tabs_drop_mark.svg
@@ -0,0 +1 @@
+<svg height="32" viewBox="0 0 16 32" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 1h6v30h-6z" fill="#d3d3d3"/></svg>
diff --git a/scene/resources/default_theme/tabs_menu.svg b/scene/resources/default_theme/tabs_menu.svg
new file mode 100644
index 0000000000..4670a5deae
--- /dev/null
+++ b/scene/resources/default_theme/tabs_menu.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 0c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2zm0 6c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2zm0 6c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2z" fill="#b2b2b2" fill-opacity=".45" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/tabs_menu_hl.svg b/scene/resources/default_theme/tabs_menu_hl.svg
new file mode 100644
index 0000000000..4ea54a2fda
--- /dev/null
+++ b/scene/resources/default_theme/tabs_menu_hl.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 0c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2zm0 6c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2zm0 6c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2z" fill="#fefffe" fill-opacity=".65" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/text_edit_ellipsis.svg b/scene/resources/default_theme/text_edit_ellipsis.svg
new file mode 100644
index 0000000000..026a1fcba7
--- /dev/null
+++ b/scene/resources/default_theme/text_edit_ellipsis.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 14 8" xmlns="http://www.w3.org/2000/svg"><path d="m3.859 0c-2.138 0-3.859 1.721-3.859 3.859v.282c0 2.138 1.721 3.859 3.859 3.859h6.282c2.138 0 3.859-1.721 3.859-3.859v-.282c0-2.138-1.721-3.859-3.859-3.859zm-.859 3c.549 0 1 .451 1 1s-.451 1-1 1-1-.451-1-1 .451-1 1-1zm4 0c.549 0 1 .451 1 1s-.451 1-1 1-1-.451-1-1 .451-1 1-1zm4 0c.549 0 1 .451 1 1s-.451 1-1 1-1-.451-1-1 .451-1 1-1z" fill="#fefffe" fill-opacity=".25" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/text_edit_space.svg b/scene/resources/default_theme/text_edit_space.svg
new file mode 100644
index 0000000000..b2b1dd4676
--- /dev/null
+++ b/scene/resources/default_theme/text_edit_space.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg"><circle cx="4" cy="4" fill="#b2b2b2" fill-opacity=".25" r="1.5"/></svg>
diff --git a/scene/resources/default_theme/text_edit_tab.svg b/scene/resources/default_theme/text_edit_tab.svg
new file mode 100644
index 0000000000..1968afb1e7
--- /dev/null
+++ b/scene/resources/default_theme/text_edit_tab.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg"><path d="m6 0v8h2v-8zm-5.014.002c-.26.003-.509.108-.693.291-.388.388-.388 1.026 0 1.414l2.293 2.293-2.293 2.293c-.388.388-.388 1.026 0 1.414s1.026.388 1.414 0l3-3c.388-.388.388-1.026 0-1.414l-3-3c-.191-.19-.451-.295-.721-.291z" fill="#b2b2b2" fill-opacity=".25" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
deleted file mode 100644
index 57ff9a5325..0000000000
--- a/scene/resources/default_theme/theme_data.h
+++ /dev/null
@@ -1,517 +0,0 @@
-// THIS FILE HAS BEEN AUTOGENERATED, DON'T EDIT!!
-
-// png image block
-
-static const unsigned char arrow_down_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x34, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x32, 0x78, 0xf0, 0x1f, 0x15, 0x52, 0x20, 0xf1, 0x30, 0xee, 0xc1, 0x17, 0xb8, 0xf0, 0xb7, 0x87, 0x69, 0x48, 0xb6, 0xdc, 0xd7, 0xb8, 0x7f, 0x9, 0x2c, 0x7c, 0xfd, 0xb1, 0x2e, 0x9a, 0x3, 0x5e, 0x70, 0x3f, 0x9c, 0xff, 0x70, 0xfe, 0xb, 0x6e, 0x6, 0xea, 0x3, 0x0, 0xfb, 0x81, 0x48, 0xb8, 0x4d, 0xe4, 0x75, 0xd9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char arrow_left_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0xa0, 0x0, 0xdc, 0x4f, 0x78, 0xf0, 0xff, 0xc1, 0xff, 0x7, 0xff, 0x21, 0x3c, 0x46, 0x98, 0xf0, 0x43, 0xed, 0xff, 0x27, 0x19, 0xb8, 0x19, 0x18, 0x18, 0x18, 0x14, 0x18, 0x19, 0x18, 0x18, 0x18, 0x98, 0x20, 0xc2, 0x2f, 0xb8, 0xff, 0xaf, 0x82, 0x8, 0xc3, 0x0, 0x54, 0xe2, 0xe7, 0x14, 0x6, 0x2d, 0x54, 0x83, 0x99, 0x70, 0xd9, 0x8, 0x95, 0x60, 0xcf, 0x61, 0xb8, 0x86, 0x55, 0x42, 0xe2, 0x2b, 0x63, 0x18, 0xc3, 0x57, 0xac, 0x46, 0xc9, 0x5f, 0xfd, 0x9f, 0x43, 0x89, 0x67, 0x19, 0x18, 0x0, 0xf4, 0x30, 0x14, 0x49, 0xef, 0xe6, 0x74, 0x60, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char arrow_right_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x17, 0x3c, 0xf8, 0xf, 0x82, 0xf7, 0x13, 0x70, 0x48, 0x3c, 0xf8, 0xf2, 0x50, 0x1b, 0x43, 0x2, 0xa, 0xaf, 0xbe, 0xe0, 0xc6, 0x2e, 0xf1, 0xff, 0xe1, 0x7c, 0x12, 0x24, 0x10, 0x46, 0x11, 0xb6, 0x1c, 0xe1, 0x5c, 0xa, 0x0, 0x0, 0xe0, 0x14, 0x48, 0xb1, 0x3d, 0x1b, 0x7a, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char bar_arrow_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x14, 0x8, 0x4, 0x0, 0x0, 0x0, 0x2e, 0x6b, 0x75, 0xfc, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0xe, 0xc0, 0x1, 0x6a, 0xd6, 0x89, 0x9, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0xfc, 0xcf, 0x80, 0x1f, 0x30, 0x8e, 0x20, 0x5, 0x8c, 0x38, 0x24, 0xff, 0x53, 0x5f, 0xc1, 0xb, 0xee, 0x9f, 0x53, 0x18, 0x18, 0xd8, 0x73, 0x24, 0xbe, 0x62, 0x55, 0x70, 0x5f, 0x83, 0x61, 0x15, 0xa3, 0x2e, 0x3, 0x3, 0xc3, 0xd, 0xe6, 0x30, 0xd9, 0xcb, 0x18, 0xa, 0x1e, 0xc6, 0xfd, 0x9f, 0xc6, 0xc0, 0xd, 0x35, 0xea, 0x3b, 0x63, 0x81, 0xfc, 0x2c, 0x14, 0x5, 0xf, 0x2a, 0x18, 0xda, 0xd1, 0x1c, 0x50, 0xa9, 0xd0, 0x1, 0x57, 0xf0, 0x10, 0x53, 0x9a, 0x81, 0x81, 0x81, 0xa1, 0x52, 0xbe, 0x83, 0x81, 0x81, 0xf1, 0x3f, 0x2e, 0x69, 0xa8, 0x12, 0x3a, 0x4, 0x14, 0x0, 0x7b, 0xda, 0x34, 0x1, 0xbb, 0xb5, 0x3e, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char bookmark_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xed, 0x93, 0x31, 0xa, 0xc0, 0x30, 0xc, 0x3, 0xa5, 0xd0, 0xff, 0x7f, 0x59, 0x1d, 0x8a, 0x42, 0x8, 0x9, 0x95, 0xc9, 0xd2, 0xa1, 0x9a, 0x8c, 0xf1, 0xdd, 0x62, 0x1b, 0x38, 0xc, 0x87, 0x5a, 0x5, 0xae, 0x79, 0xde, 0x2, 0x1, 0x80, 0x94, 0x39, 0x48, 0x76, 0x49, 0x17, 0xa4, 0xf0, 0x24, 0x61, 0x2b, 0x51, 0x8b, 0xfc, 0x82, 0xcf, 0xb, 0x48, 0x7a, 0xdf, 0x75, 0x81, 0xf, 0xe5, 0x29, 0xf7, 0x92, 0x6b, 0x3, 0x1a, 0x1e, 0xda, 0x7c, 0x3d, 0x77, 0x21, 0x7b, 0xa8, 0x74, 0x2e, 0xcb, 0xd, 0xc8, 0x75, 0x13, 0x28, 0x9, 0xed, 0xc2, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xc7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd0, 0x81, 0x66, 0x43, 0x31, 0x14, 0x87, 0xf1, 0xf, 0x5, 0x17, 0xb8, 0x28, 0x2e, 0x8, 0x71, 0xf3, 0x6, 0x19, 0xb6, 0xb9, 0xcb, 0xac, 0x95, 0xa4, 0xb7, 0xad, 0x6a, 0xd5, 0x68, 0x5f, 0xe4, 0x3e, 0x76, 0x1e, 0xe1, 0xbf, 0x21, 0xa6, 0xab, 0xf8, 0x1, 0x7c, 0x9c, 0x73, 0xe, 0xac, 0xe8, 0xe8, 0x19, 0x30, 0x58, 0xc6, 0xca, 0x62, 0x18, 0xe8, 0xe9, 0x58, 0x41, 0xc7, 0x1a, 0x87, 0x27, 0x10, 0x49, 0xe4, 0x5f, 0x89, 0x48, 0xc0, 0xe3, 0x58, 0xd3, 0x41, 0x8f, 0xb, 0xcb, 0xbd, 0x7c, 0xeb, 0xbf, 0x7b, 0x9, 0xb, 0x8e, 0x1e, 0x6, 0xfc, 0xad, 0x64, 0x6d, 0xb5, 0x79, 0xb0, 0x55, 0xd6, 0xad, 0xe0, 0x19, 0xc0, 0x10, 0xae, 0xda, 0x34, 0x5c, 0x45, 0xc0, 0x80, 0x25, 0x5e, 0xf4, 0xd5, 0x70, 0x11, 0x11, 0xb, 0x23, 0xe9, 0xac, 0xcf, 0x86, 0xb3, 0x48, 0x8c, 0x30, 0x92, 0x4f, 0xa, 0xd, 0x27, 0x91, 0x6b, 0x70, 0xd4, 0x47, 0xc3, 0xf1, 0x2f, 0x48, 0x7, 0x4d, 0xd, 0x87, 0x3a, 0xc2, 0x12, 0x67, 0xbd, 0x37, 0xcc, 0x75, 0x49, 0x43, 0xd8, 0xe9, 0xad, 0x61, 0x57, 0xcf, 0x1c, 0xf0, 0xfb, 0x32, 0xe9, 0xf5, 0xc9, 0xa4, 0x7d, 0x7d, 0x54, 0x8f, 0x7b, 0x59, 0xe6, 0x92, 0x14, 0x1f, 0x24, 0xcd, 0x3f, 0x7b, 0x6b, 0xa, 0xe, 0x6a, 0x82, 0x91, 0x45, 0x30, 0xba, 0x1, 0x4a, 0x51, 0xc4, 0x35, 0x1f, 0xe5, 0xa1, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_focus_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x62, 0xed, 0x5e, 0xfc, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0x84, 0x4c, 0x42, 0x43, 0x9d, 0x15, 0x81, 0x8c, 0xf4, 0xce, 0x99, 0x33, 0x67, 0x94, 0x1, 0x19, 0xab, 0x41, 0xc2, 0xbb, 0x80, 0x38, 0x6, 0xc4, 0x38, 0xa, 0xc4, 0x91, 0x20, 0xc6, 0x54, 0x3c, 0xc, 0xb8, 0x62, 0x98, 0x76, 0x29, 0x20, 0xce, 0xdd, 0x39, 0x73, 0xe6, 0x3c, 0x90, 0x81, 0x62, 0x10, 0x2b, 0x30, 0x1, 0x0, 0xec, 0xe0, 0x11, 0x6d, 0xb5, 0xe0, 0x8c, 0x99, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_hover_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x8a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x5f, 0x5a, 0x6b, 0x56, 0x53, 0x64, 0x57, 0x53, 0x64, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x57, 0x53, 0x63, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5b, 0x57, 0x66, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x43, 0x40, 0x4c, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x40, 0xdc, 0xe6, 0x80, 0x0, 0x0, 0x0, 0x16, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0x6b, 0x28, 0x52, 0x7a, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0xcf, 0xc5, 0x1, 0x84, 0x40, 0x14, 0x4, 0xd1, 0x2e, 0x5c, 0xf3, 0xf, 0x92, 0xbd, 0xaf, 0xd3, 0xb8, 0x53, 0x30, 0xf6, 0x6e, 0x1f, 0x31, 0x26, 0xc9, 0x63, 0x90, 0xcc, 0xe2, 0xb1, 0x4f, 0x34, 0x48, 0x8a, 0x86, 0xfc, 0xf6, 0x87, 0x1e, 0x82, 0x8c, 0x19, 0xf2, 0x17, 0x4, 0x50, 0xce, 0x6f, 0x8d, 0xd7, 0x88, 0x7e, 0x69, 0xc9, 0x23, 0x4, 0x1c, 0x40, 0x47, 0x50, 0x24, 0xed, 0x41, 0x3d, 0x78, 0xf, 0x56, 0xe4, 0x23, 0xb8, 0x7, 0x69, 0x11, 0xf7, 0x12, 0x12, 0x7e, 0xea, 0x60, 0xa, 0x9a, 0xef, 0x3f, 0xb0, 0x83, 0x26, 0x98, 0x7b, 0x70, 0x33, 0xdc, 0x65, 0xfc, 0xe, 0x81, 0x4e, 0x3b, 0x55, 0xea, 0xaa, 0xb0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_normal_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x87, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x56, 0x52, 0x60, 0x47, 0x44, 0x52, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x47, 0x44, 0x51, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x40, 0x3e, 0x48, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x3f, 0x38, 0xaa, 0x5e, 0x0, 0x0, 0x0, 0x15, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0xe8, 0xff, 0x76, 0xed, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0xcf, 0x45, 0x2, 0x84, 0x30, 0x0, 0x4, 0xc1, 0x69, 0x1c, 0xfe, 0xff, 0xca, 0xd5, 0xeb, 0x2a, 0x83, 0x6b, 0xd2, 0xb7, 0x54, 0x1c, 0x31, 0x26, 0xc9, 0x63, 0x50, 0xcc, 0x32, 0x8d, 0x3f, 0xd9, 0x20, 0x5, 0x1a, 0xf2, 0xc7, 0x1f, 0x7a, 0x48, 0x4a, 0x66, 0xa8, 0xde, 0xc, 0xd0, 0x38, 0xd1, 0x54, 0xdb, 0x4c, 0x2b, 0xd0, 0x5c, 0x62, 0x8e, 0xa0, 0x1, 0x74, 0x4, 0x65, 0xd2, 0x1e, 0xd4, 0x83, 0xf7, 0x60, 0x65, 0x3e, 0x82, 0x3, 0x48, 0x49, 0xdf, 0x55, 0xca, 0xd4, 0xef, 0xfa, 0xfb, 0x27, 0x36, 0xf, 0x92, 0x31, 0x9e, 0x44, 0x3e, 0x17, 0x7c, 0xbf, 0x3, 0xef, 0x34, 0x3f, 0x3e, 0xe0, 0x24, 0x67, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_pressed_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x7, 0x1c, 0xc, 0x14, 0x2b, 0xf9, 0x77, 0x52, 0x64, 0x0, 0x0, 0x1, 0x92, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0x8d, 0x93, 0x4d, 0x4e, 0x1b, 0x41, 0x10, 0x85, 0xbf, 0xea, 0xaa, 0x1e, 0x20, 0x83, 0xf9, 0x9, 0x36, 0xb2, 0x85, 0x72, 0x84, 0x8, 0x65, 0x11, 0xe5, 0xc, 0x9c, 0x80, 0x73, 0x10, 0xee, 0xc1, 0x41, 0x38, 0x1, 0xab, 0xec, 0xa3, 0x2c, 0x2, 0x8a, 0xe0, 0x2, 0x51, 0xc2, 0x48, 0x46, 0x42, 0x64, 0xc6, 0x91, 0x21, 0xf4, 0x74, 0x16, 0x2e, 0xb0, 0x71, 0x2, 0xf2, 0x93, 0x9e, 0xba, 0x16, 0xdd, 0xaf, 0x5e, 0xa9, 0x5f, 0x9, 0x60, 0x40, 0x1, 0x2c, 0x1, 0xcb, 0x5e, 0x2b, 0x10, 0x78, 0x8a, 0x16, 0x48, 0xc0, 0x1d, 0x30, 0x6, 0x6e, 0x81, 0x3b, 0xf3, 0x87, 0x25, 0xb0, 0x9, 0xac, 0x7b, 0xbd, 0xc, 0x88, 0x13, 0x20, 0x3b, 0xc7, 0xc0, 0x8, 0xb8, 0x1, 0xae, 0x81, 0x91, 0xf9, 0xe5, 0xad, 0x77, 0xbb, 0x1f, 0xf6, 0x7b, 0xdd, 0xfe, 0x41, 0x4a, 0x69, 0x2d, 0x93, 0xf9, 0x1f, 0x4, 0x41, 0x55, 0x7f, 0xd, 0xaf, 0xaa, 0xa3, 0xaf, 0x67, 0x9f, 0x8f, 0x81, 0x64, 0xde, 0xb1, 0xbb, 0xdd, 0x1b, 0x7c, 0xac, 0x9b, 0x9b, 0xce, 0xd9, 0xb7, 0x2f, 0xfc, 0xf3, 0x5e, 0xa6, 0xe5, 0xee, 0xdb, 0xf7, 0x6b, 0xdb, 0xbd, 0xc1, 0x21, 0xf0, 0x9, 0x18, 0x5, 0x60, 0x5, 0xd8, 0x48, 0x6d, 0xdb, 0x39, 0xbf, 0x38, 0xc5, 0x34, 0x62, 0x36, 0x47, 0x9d, 0xf2, 0xfc, 0xe2, 0x94, 0xd4, 0xb6, 0x1d, 0x60, 0x3, 0x58, 0x31, 0x20, 0x2, 0x65, 0x40, 0x88, 0x31, 0x92, 0xbd, 0xbb, 0x8, 0xe4, 0x3c, 0x39, 0x67, 0x91, 0x33, 0x84, 0x89, 0xa5, 0x12, 0x88, 0xf, 0x3f, 0x10, 0x45, 0x5, 0xb3, 0xc8, 0x73, 0x10, 0x11, 0xb2, 0xab, 0x8b, 0xa, 0xde, 0xb8, 0x30, 0x9f, 0xb0, 0x8, 0xa2, 0xc4, 0x58, 0xb0, 0x8, 0x82, 0x28, 0xde, 0x58, 0xcc, 0xff, 0x5c, 0x45, 0x64, 0x61, 0x1, 0x99, 0xcc, 0xa5, 0x80, 0x4e, 0x5, 0xc2, 0xcb, 0x2, 0xe2, 0x41, 0x10, 0x40, 0xc2, 0x53, 0x81, 0xc, 0xa8, 0x8a, 0x52, 0x2c, 0xe8, 0x40, 0x27, 0x23, 0x28, 0x90, 0xcd, 0xe3, 0x79, 0x1b, 0x34, 0x50, 0x14, 0x4b, 0xf0, 0x4c, 0x88, 0x66, 0xbd, 0x4, 0xd, 0x78, 0x94, 0x93, 0x79, 0x3c, 0x9b, 0x18, 0x63, 0x7a, 0xbd, 0xb9, 0xa5, 0xcd, 0xa8, 0x7e, 0xb4, 0x3a, 0x2f, 0x15, 0x10, 0xca, 0x72, 0x95, 0x18, 0x8b, 0x4, 0x34, 0xc0, 0xd8, 0x80, 0x1a, 0x18, 0x56, 0xd5, 0xcf, 0x93, 0x41, 0x7f, 0x67, 0xaf, 0xb3, 0xba, 0x1e, 0x5e, 0x8a, 0xb2, 0x99, 0xb5, 0x97, 0xd5, 0x8f, 0x13, 0x60, 0x8, 0xd4, 0x2, 0x74, 0x9d, 0x6f, 0x80, 0xbe, 0x2f, 0x55, 0xe9, 0x26, 0xc2, 0xcc, 0x26, 0x66, 0x5f, 0xa4, 0x6b, 0xa0, 0x2, 0xbe, 0x3, 0x57, 0xe6, 0x56, 0x1e, 0x66, 0x1a, 0x2, 0xaf, 0x3c, 0x24, 0x36, 0x67, 0xe0, 0x1e, 0xf8, 0x3, 0xfc, 0xf6, 0x6d, 0xac, 0x81, 0xe6, 0x2f, 0x7c, 0x22, 0x6d, 0x74, 0x25, 0xb, 0xb3, 0xa2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char checked_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x8d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x58, 0x56, 0x63, 0xb0, 0xaf, 0xb5, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0xb6, 0xb6, 0xb9, 0x57, 0x57, 0x5a, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x8b, 0x8b, 0x8d, 0xff, 0xff, 0xff, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x42, 0x42, 0x47, 0xf8, 0xf8, 0xf8, 0xfe, 0xfe, 0xfe, 0x25, 0x25, 0x2a, 0x4e, 0x4e, 0x52, 0x26, 0x26, 0x2b, 0xc5, 0xc5, 0xc7, 0xaa, 0xaa, 0xab, 0xb8, 0xb8, 0xba, 0x5f, 0x5f, 0x63, 0x74, 0x74, 0x77, 0xed, 0xed, 0xed, 0x33, 0x33, 0x38, 0x8d, 0x8d, 0x8f, 0xb8, 0xb8, 0xb9, 0x35, 0x35, 0x39, 0x3a, 0x3a, 0x3e, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xb2, 0xb2, 0xb4, 0x45, 0x45, 0x49, 0x61, 0x61, 0x65, 0x8f, 0x8f, 0x92, 0x63, 0x63, 0x66, 0x2a, 0x2a, 0x2f, 0x40, 0x82, 0xb, 0xf6, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xfa, 0xdd, 0xfb, 0xfb, 0xb4, 0xfa, 0xb8, 0xf0, 0x7f, 0x59, 0x0, 0x0, 0x0, 0x7e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xca, 0x5, 0xb2, 0x2, 0x30, 0x18, 0x3, 0xe1, 0x4d, 0xed, 0xb9, 0x60, 0xf7, 0x3f, 0x20, 0xee, 0x4e, 0x99, 0xe0, 0xb0, 0x63, 0xfd, 0xbf, 0x86, 0xd7, 0x12, 0x72, 0x38, 0x69, 0x5b, 0x6b, 0x42, 0x45, 0xe5, 0xa, 0xab, 0x95, 0x41, 0x9f, 0x32, 0x20, 0x69, 0x2d, 0xbc, 0x50, 0x46, 0x3a, 0x10, 0x17, 0x5f, 0x49, 0x4, 0x7f, 0x90, 0x57, 0x89, 0xb7, 0xc5, 0x5f, 0x96, 0x17, 0x2e, 0x93, 0xcb, 0x8e, 0x3a, 0x83, 0xb, 0xc4, 0x8e, 0xd4, 0xff, 0x5c, 0x73, 0x83, 0x69, 0x9e, 0x95, 0xfc, 0x3b, 0xf4, 0x33, 0xe0, 0xf8, 0x61, 0xd3, 0xf1, 0x7d, 0x5d, 0x30, 0x7a, 0x6f, 0x89, 0xb, 0xd4, 0x5a, 0xe1, 0x40, 0xf, 0xfc, 0x34, 0x6c, 0xd2, 0x56, 0x80, 0xef, 0xfd, 0x9, 0xd2, 0x3a, 0x5e, 0x41, 0x15, 0x21, 0x77, 0x6, 0xc7, 0x6b, 0x47, 0x4e, 0x3a, 0x2f, 0x53, 0xb4, 0x10, 0xc7, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char checked_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x73, 0x72, 0x7b, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x82, 0x80, 0x8a, 0x90, 0x90, 0x93, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x93, 0x93, 0x95, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x7d, 0x7d, 0x7f, 0x58, 0x58, 0x5b, 0xa4, 0xa4, 0xa4, 0x9e, 0x9e, 0xa0, 0x9e, 0x9e, 0x9e, 0x9b, 0x9b, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x93, 0x93, 0x94, 0x8f, 0x8f, 0x8f, 0x86, 0x86, 0x88, 0x85, 0x85, 0x86, 0x82, 0x82, 0x83, 0x81, 0x81, 0x83, 0x7f, 0x7f, 0x81, 0x7c, 0x7c, 0x7e, 0x7a, 0x7a, 0x7d, 0x78, 0x78, 0x7b, 0x71, 0x71, 0x74, 0x68, 0x68, 0x6c, 0x66, 0x66, 0x6a, 0x65, 0x65, 0x68, 0x63, 0x63, 0x66, 0x5f, 0x5f, 0x63, 0x5c, 0x5c, 0x60, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x59, 0x59, 0x5c, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x10, 0x13, 0xbb, 0xf, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xb4, 0xdd, 0xfa, 0xfa, 0xfb, 0xfb, 0x5b, 0xd1, 0xf1, 0xe6, 0x0, 0x0, 0x0, 0x96, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x5d, 0x8f, 0xc9, 0x12, 0x82, 0x30, 0x14, 0x4, 0x87, 0x18, 0x50, 0x51, 0x44, 0x25, 0x42, 0x4, 0x77, 0xc4, 0x8d, 0x97, 0x0, 0xf9, 0xff, 0x8f, 0xb3, 0x88, 0xa4, 0x4a, 0xed, 0x63, 0x5f, 0xa6, 0x7, 0xf8, 0x7, 0x1e, 0xe3, 0x7e, 0x60, 0x19, 0x4f, 0x46, 0x1e, 0x0, 0x36, 0x8d, 0x4c, 0x67, 0x59, 0x65, 0x33, 0x6, 0x80, 0x47, 0xad, 0x56, 0x3d, 0xb7, 0x3c, 0x5d, 0x70, 0x0, 0xbe, 0xd1, 0x44, 0x65, 0x4d, 0x94, 0xc8, 0xc2, 0xf8, 0x0, 0x82, 0x4e, 0x91, 0x94, 0x15, 0x5d, 0xd2, 0xec, 0xde, 0x5, 0x83, 0x38, 0xc8, 0xe3, 0x63, 0x23, 0xce, 0xca, 0x9, 0x7a, 0x6e, 0xf3, 0x93, 0x48, 0x1a, 0x27, 0x14, 0x35, 0x3b, 0xb9, 0x5e, 0x56, 0xe4, 0x84, 0x22, 0xba, 0xa, 0x51, 0xd0, 0xb7, 0xa8, 0xcb, 0xfd, 0xcb, 0x9, 0x3b, 0xfb, 0x41, 0xdb, 0x59, 0x17, 0xa6, 0x94, 0x6e, 0xe3, 0x3e, 0x8c, 0x85, 0xf1, 0x90, 0x6e, 0xe6, 0x21, 0xfb, 0x39, 0xe7, 0x73, 0xe6, 0xfd, 0x5f, 0x7, 0xde, 0xc3, 0xb5, 0x16, 0x87, 0xb0, 0x9e, 0x42, 0x46, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char checker_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char close_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char close_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char color_picker_hue_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x8, 0x2, 0x0, 0x0, 0x0, 0xfd, 0x5c, 0x8b, 0xcf, 0x0, 0x0, 0x0, 0x54, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xcd, 0xc9, 0x1, 0x6, 0x43, 0x31, 0x10, 0x45, 0xd1, 0x63, 0xc8, 0xcf, 0xfe, 0x97, 0xdb, 0x9f, 0xf0, 0x1a, 0x15, 0xb4, 0x0, 0xa5, 0x1a, 0x1c, 0xd7, 0xcc, 0x13, 0x4a, 0x5b, 0xae, 0x2f, 0xf5, 0xdd, 0xdf, 0x92, 0xc9, 0x88, 0xba, 0x1d, 0x4d, 0xb, 0xa2, 0xb8, 0x96, 0xb6, 0xf4, 0x93, 0xcb, 0x48, 0xb9, 0x9d, 0x8c, 0x16, 0xa4, 0x2e, 0x5e, 0xda, 0x6e, 0xe7, 0xe3, 0xd7, 0xff, 0xbf, 0x9b, 0x22, 0x66, 0x71, 0x2f, 0x8f, 0xdd, 0xc5, 0x78, 0xbb, 0xc7, 0x9, 0xbb, 0xf0, 0x4, 0x75, 0x7b, 0x8a, 0xe5, 0x7c, 0x23, 0x8a, 0xd3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char color_picker_sample_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x47, 0x29, 0xbc, 0x83, 0x0, 0x0, 0x0, 0x3c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xd5, 0x21, 0x11, 0x0, 0x30, 0xc, 0x4, 0xc1, 0xfa, 0x57, 0x53, 0x87, 0xed, 0x4, 0x45, 0xc4, 0xed, 0xa3, 0xc3, 0x4b, 0xfe, 0xbc, 0xd9, 0x9d, 0x35, 0x2b, 0xe, 0x0, 0x0, 0x0, 0x80, 0xed, 0x66, 0xc5, 0x1, 0x0, 0x0, 0x0, 0xe0, 0x6, 0x1, 0x0, 0x0, 0x90, 0x6, 0x70, 0x83, 0x0, 0x0, 0x0, 0x28, 0x3, 0x7c, 0x54, 0x93, 0xd6, 0xf1, 0xd1, 0x16, 0x8a, 0x17, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char dialog_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x26, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf7, 0x10, 0x9b, 0xa4, 0x0, 0x0, 0x2, 0xf5, 0x7a, 0x54, 0x58, 0x74, 0x52, 0x61, 0x77, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x78, 0x69, 0x66, 0x0, 0x0, 0x78, 0xda, 0xb5, 0x95, 0x6d, 0x72, 0xe4, 0x28, 0xc, 0x86, 0xff, 0x73, 0x8a, 0x3d, 0x2, 0xfa, 0x42, 0x70, 0x1c, 0xc, 0xa6, 0x6a, 0x6f, 0xb0, 0xc7, 0x9f, 0x17, 0xec, 0x38, 0xd9, 0x4c, 0x57, 0xda, 0x49, 0xcf, 0x40, 0xb5, 0xa5, 0x92, 0x5, 0x12, 0x3c, 0x92, 0x3b, 0xec, 0xff, 0xfd, 0x3b, 0xc2, 0x3f, 0x18, 0xa4, 0x39, 0x6, 0x35, 0xcf, 0xa9, 0xa4, 0x14, 0x31, 0xb4, 0x68, 0xe1, 0xa, 0x25, 0xc7, 0xf7, 0xb1, 0x9f, 0xf2, 0xb0, 0x51, 0xd4, 0xf5, 0x5c, 0xa3, 0xe8, 0xf9, 0x8e, 0x22, 0x85, 0x8f, 0x2f, 0x2e, 0x8d, 0x21, 0x5, 0x52, 0xe, 0xa3, 0xbf, 0x2d, 0x90, 0xd3, 0xfe, 0xe6, 0x9f, 0x2e, 0x89, 0x8d, 0x1e, 0xbc, 0x20, 0xfb, 0xb4, 0x40, 0xae, 0x30, 0xfc, 0x31, 0xb0, 0xd7, 0xd3, 0xce, 0x91, 0xff, 0x97, 0x91, 0x7a, 0xb4, 0xf8, 0x71, 0xe4, 0xf7, 0xdf, 0x18, 0x3d, 0x8f, 0xb1, 0x1f, 0xa7, 0xab, 0x9a, 0x70, 0xd, 0xe9, 0x38, 0xd4, 0x11, 0x22, 0xbc, 0x6d, 0x3, 0xc7, 0xd, 0x5b, 0xc9, 0x5a, 0x96, 0x30, 0x1d, 0x3f, 0x83, 0xee, 0x6b, 0x16, 0xcc, 0x1c, 0x6b, 0x6c, 0xa4, 0xb1, 0xc7, 0x16, 0x37, 0xcc, 0x46, 0x85, 0x98, 0x24, 0xe, 0x52, 0xea, 0x81, 0x2a, 0xd, 0xda, 0xa9, 0x43, 0x36, 0x6a, 0xc8, 0x51, 0x79, 0x67, 0x87, 0x64, 0x6e, 0x2c, 0xcb, 0x96, 0xc5, 0xb9, 0x70, 0x93, 0x28, 0x24, 0x3a, 0x27, 0xd, 0x76, 0x29, 0xd2, 0x25, 0xb, 0x4b, 0xe3, 0x5d, 0x44, 0x34, 0x8, 0x5f, 0xb9, 0xd0, 0x8a, 0x5b, 0x56, 0xbc, 0x46, 0x19, 0x91, 0x3b, 0xc1, 0x95, 0x9, 0x9b, 0x11, 0x96, 0x7c, 0x39, 0xc3, 0x33, 0x87, 0x3b, 0x73, 0x8c, 0x16, 0x71, 0x47, 0x44, 0x38, 0x3d, 0x9d, 0x75, 0x81, 0xbc, 0x98, 0x27, 0x7, 0x9a, 0xd7, 0x28, 0xf3, 0x9, 0x37, 0x0, 0xa1, 0x71, 0x72, 0xb3, 0x75, 0xc1, 0x6f, 0xf3, 0x1a, 0xe1, 0x3, 0x58, 0x1, 0x41, 0x5b, 0xd7, 0x9c, 0x71, 0xc0, 0x1a, 0xb7, 0x63, 0x8b, 0xcd, 0xe8, 0xbd, 0xb6, 0x64, 0x15, 0x80, 0xc0, 0xcf, 0x20, 0x8f, 0xfa, 0x22, 0xef, 0x93, 0x1a, 0xaf, 0x2a, 0x51, 0xc4, 0x36, 0x24, 0x43, 0x2, 0x4, 0x31, 0x91, 0x18, 0x25, 0x8a, 0xce, 0xec, 0x44, 0x2a, 0x9c, 0x1, 0xa8, 0x22, 0x73, 0x16, 0xe5, 0xd, 0x4, 0xc8, 0x8c, 0x3b, 0x92, 0x64, 0x15, 0x49, 0x60, 0x93, 0x51, 0x47, 0x88, 0x8d, 0x35, 0x4e, 0xcb, 0x97, 0x8d, 0xf, 0x3b, 0x5a, 0x5, 0x7c, 0x4c, 0x92, 0x38, 0xd8, 0x14, 0xa9, 0x80, 0xa5, 0x6a, 0xa8, 0x1f, 0xd7, 0x8c, 0x1a, 0xaa, 0x26, 0xa6, 0x66, 0x96, 0xcc, 0x2d, 0x5b, 0xb1, 0x1a, 0x92, 0x24, 0x4d, 0x96, 0x52, 0xf2, 0x34, 0x7b, 0xae, 0xba, 0xb8, 0xba, 0x79, 0x72, 0xf7, 0xec, 0xc5, 0x6b, 0x96, 0xac, 0xd9, 0x72, 0xca, 0x9e, 0x73, 0x2e, 0xb9, 0x16, 0x2e, 0x82, 0x96, 0xb4, 0x92, 0x8a, 0x97, 0x5c, 0x4a, 0xa9, 0x15, 0x31, 0xab, 0x86, 0x6a, 0x15, 0xab, 0x2b, 0x3c, 0x6a, 0xdd, 0x78, 0x93, 0x4d, 0x37, 0xdb, 0xd2, 0xe6, 0x5b, 0xde, 0xca, 0x56, 0x1b, 0xca, 0xa7, 0x69, 0xb3, 0x96, 0x9a, 0xb7, 0xdc, 0x4a, 0xab, 0x9d, 0xbb, 0x74, 0xed, 0xd6, 0x53, 0xf7, 0x9e, 0x7b, 0xe9, 0x75, 0xa7, 0x1d, 0xa5, 0x14, 0x76, 0xdd, 0x6d, 0x4f, 0xbb, 0xef, 0x79, 0x2f, 0x7b, 0x1d, 0xa8, 0xb5, 0x21, 0x43, 0x87, 0x8d, 0x34, 0x7c, 0xe4, 0x51, 0x46, 0xbd, 0xa8, 0xd1, 0xd9, 0xb6, 0x9f, 0xe7, 0x37, 0xa8, 0xd1, 0x49, 0x8d, 0x17, 0xa9, 0xe9, 0xe7, 0x17, 0x35, 0x58, 0xdd, 0xe7, 0x46, 0x6b, 0xb, 0x9a, 0xdf, 0x19, 0x9b, 0xcc, 0x40, 0x8c, 0x95, 0x40, 0xdc, 0x27, 0x1, 0x14, 0x34, 0x4f, 0x66, 0x31, 0x93, 0x2a, 0x4f, 0x72, 0x93, 0x59, 0x2c, 0x8c, 0xae, 0x30, 0x46, 0x92, 0x36, 0xd9, 0x74, 0x8a, 0x35, 0x50, 0x2, 0x42, 0xdd, 0x89, 0x6d, 0xd0, 0xc5, 0xee, 0x9d, 0xdc, 0x6d, 0x6e, 0x1, 0x77, 0xfd, 0x8c, 0x1b, 0xdf, 0x21, 0x17, 0x26, 0xba, 0x3f, 0x40, 0x8e, 0xc3, 0x2e, 0x9f, 0xb8, 0x3d, 0xa0, 0xd6, 0xe7, 0x97, 0xb0, 0x2d, 0x62, 0x47, 0x17, 0xce, 0x3b, 0x8d, 0x82, 0xee, 0x83, 0x6f, 0xe5, 0x5c, 0x79, 0x53, 0x50, 0x5b, 0x5a, 0x7e, 0x55, 0xfe, 0x85, 0x8d, 0x68, 0x9, 0x1c, 0xe2, 0x67, 0x32, 0x3c, 0x7c, 0xa1, 0x6b, 0xe7, 0xf4, 0x1d, 0x25, 0xdc, 0x70, 0x42, 0xce, 0x65, 0x85, 0xb0, 0x2f, 0x94, 0x70, 0xc7, 0xe9, 0x37, 0xe5, 0x41, 0xd2, 0xe1, 0x47, 0xe7, 0x78, 0x90, 0x74, 0x40, 0x91, 0x8a, 0xa6, 0x74, 0x1a, 0x5b, 0xa7, 0x72, 0x86, 0xfe, 0xa6, 0x39, 0xdc, 0x72, 0x47, 0x97, 0x1b, 0xa1, 0xbf, 0xf0, 0x3f, 0x8c, 0xe, 0x7d, 0xa8, 0x84, 0x2f, 0xde, 0x7d, 0xad, 0x90, 0x1a, 0xdb, 0xc, 0xb1, 0x2d, 0x25, 0x5c, 0xda, 0x8b, 0x4a, 0x38, 0xb3, 0x2e, 0x15, 0xa7, 0x39, 0x8c, 0x38, 0x53, 0xeb, 0x67, 0xe8, 0xfb, 0xe6, 0x70, 0xdb, 0xfd, 0x49, 0xa9, 0x86, 0x17, 0xa9, 0x5f, 0x5c, 0xc2, 0x8b, 0xd4, 0x2f, 0x73, 0x78, 0x4e, 0xdd, 0x6e, 0xc9, 0x70, 0xd7, 0xf1, 0x59, 0x3d, 0x84, 0x17, 0xca, 0x88, 0x3e, 0xca, 0xf0, 0xd9, 0xf0, 0x53, 0xf9, 0xe7, 0x36, 0xf2, 0x81, 0x8f, 0x7b, 0x81, 0xf2, 0xb, 0x12, 0xea, 0xac, 0x15, 0x79, 0x70, 0x44, 0x63, 0x0, 0x0, 0x1, 0x68, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x22, 0x0, 0x0, 0x0, 0x1a, 0x19, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x21, 0x17, 0x16, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1b, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1e, 0x1c, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x0, 0x0, 0x0, 0x20, 0x20, 0x25, 0x20, 0x1d, 0x25, 0x20, 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x20, 0x1d, 0x1a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x30, 0x38, 0xe8, 0xe5, 0xf1, 0xe5, 0xe2, 0xeb, 0xe3, 0xe1, 0xe8, 0xe1, 0xdf, 0xe7, 0xe0, 0xde, 0xe6, 0xdf, 0xdd, 0xe5, 0xde, 0xdc, 0xe4, 0xdd, 0xdb, 0xe3, 0xdc, 0xda, 0xe2, 0xda, 0xd8, 0xe0, 0xd9, 0xd7, 0xdf, 0xd7, 0xd6, 0xdf, 0xd6, 0xd4, 0xdd, 0xd5, 0xd3, 0xdc, 0xd4, 0xd1, 0xdb, 0xd3, 0xd0, 0xda, 0xd1, 0xce, 0xd8, 0xd0, 0xcd, 0xd7, 0xcf, 0xcd, 0xd7, 0xe2, 0xdf, 0xeb, 0x48, 0x46, 0x51, 0x42, 0x40, 0x4b, 0x40, 0x3e, 0x48, 0x40, 0x3d, 0x48, 0x48, 0x45, 0x50, 0x42, 0x3f, 0x4a, 0x3f, 0x3d, 0x48, 0x47, 0x44, 0x50, 0x41, 0x3f, 0x4a, 0x3f, 0x3d, 0x47, 0x41, 0x3e, 0x49, 0x3f, 0x3c, 0x47, 0x46, 0x43, 0x4f, 0x3e, 0x3c, 0x46, 0x40, 0x3e, 0x49, 0x3d, 0x3b, 0x46, 0x45, 0x43, 0x4e, 0x3d, 0x3b, 0x45, 0x44, 0x42, 0x4d, 0x3d, 0x3a, 0x45, 0x3e, 0x3c, 0x47, 0x3c, 0x3a, 0x44, 0x43, 0x42, 0x4c, 0x43, 0x40, 0x4c, 0x3e, 0x3b, 0x46, 0x3b, 0x39, 0x43, 0x43, 0x3f, 0x4c, 0x43, 0x3f, 0x4b, 0x3a, 0x38, 0x42, 0x42, 0x3e, 0x4b, 0x42, 0x3e, 0x49, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3f, 0x3e, 0x48, 0x39, 0x37, 0x40, 0x38, 0x36, 0x40, 0x3e, 0x3d, 0x48, 0x38, 0x36, 0x3f, 0x3e, 0x3d, 0x47, 0x3a, 0x38, 0x41, 0x38, 0x35, 0x3f, 0x37, 0x35, 0x3e, 0x39, 0x36, 0x40, 0x37, 0x34, 0x3e, 0x3d, 0x3a, 0x46, 0x36, 0x34, 0x3d, 0x3d, 0x3a, 0x44, 0x37, 0x35, 0x3f, 0x35, 0x33, 0x3c, 0x6b, 0xff, 0x8f, 0xb1, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x88, 0x5, 0x1d, 0x48, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe5, 0x7, 0x1b, 0x17, 0x11, 0x18, 0xe6, 0xb9, 0x22, 0xac, 0x0, 0x0, 0x0, 0x3f, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0xf4, 0x64, 0xc0, 0x6, 0x98, 0x18, 0x68, 0x27, 0xcc, 0xc2, 0x88, 0x5d, 0x98, 0x99, 0x24, 0x61, 0x26, 0x52, 0x84, 0x71, 0xb9, 0x84, 0x89, 0x76, 0xc2, 0xb8, 0xac, 0x64, 0x21, 0x49, 0x35, 0x33, 0x15, 0x54, 0xe3, 0xa, 0x6f, 0x26, 0x6a, 0xa8, 0x26, 0xc9, 0x4a, 0xaa, 0x44, 0x3, 0x89, 0x89, 0x8d, 0x99, 0xee, 0xe9, 0x1b, 0x87, 0x30, 0x0, 0x20, 0x4, 0x0, 0xed, 0x48, 0xa7, 0x26, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char dropdown_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x60, 0xf8, 0xc0, 0xcc, 0x0, 0x2, 0x60, 0x16, 0x98, 0x78, 0x67, 0x8, 0x81, 0x6f, 0x4d, 0xde, 0x9a, 0x0, 0x5, 0xde, 0x3a, 0x3d, 0xfc, 0x8f, 0x80, 0xaf, 0xba, 0x18, 0xde, 0x29, 0x2, 0x19, 0xbf, 0x61, 0x2, 0x6f, 0x62, 0x18, 0x3e, 0xb0, 0xbd, 0x97, 0x4, 0x32, 0xff, 0x80, 0xb9, 0xb1, 0x20, 0x93, 0xc0, 0x42, 0x8, 0x2e, 0x54, 0xe8, 0x9d, 0xdc, 0x9b, 0x54, 0x10, 0xb, 0x21, 0xc4, 0x4, 0x63, 0x1, 0x0, 0x86, 0x1f, 0x3b, 0x1e, 0x92, 0x22, 0x3f, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char ellipsis_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc9, 0x11, 0xce, 0xcc, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x78, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x95, 0xd1, 0x31, 0xa, 0xc2, 0x50, 0x10, 0x4, 0xd0, 0xb7, 0x1f, 0xf, 0x60, 0x67, 0xa, 0xf, 0x12, 0x6f, 0x60, 0xe9, 0x51, 0x5, 0x3d, 0x44, 0xee, 0x61, 0xa1, 0xa5, 0xf6, 0x81, 0xb5, 0xc8, 0x47, 0x82, 0x84, 0x4f, 0xb2, 0xdd, 0xb0, 0x33, 0xb3, 0xb3, 0x4c, 0x40, 0x66, 0xee, 0x71, 0xc2, 0x1, 0x3b, 0xcb, 0x33, 0xe2, 0x85, 0x21, 0x22, 0xde, 0x51, 0x45, 0x97, 0x86, 0x60, 0xc9, 0xe0, 0x5a, 0xea, 0xa5, 0xb5, 0x22, 0x95, 0xdb, 0x97, 0x1a, 0xf, 0x6e, 0xb8, 0xcf, 0x8, 0x2d, 0xdc, 0x95, 0xd9, 0x22, 0xfe, 0x9c, 0x9b, 0x38, 0x32, 0xf3, 0x8c, 0xe3, 0x86, 0xa8, 0xf0, 0x28, 0x18, 0x4c, 0xf, 0xaf, 0x9d, 0x11, 0x43, 0xf0, 0xab, 0xa3, 0x47, 0xa7, 0x5d, 0xc7, 0xd3, 0x54, 0xc7, 0xe7, 0xb, 0xb9, 0xce, 0x1f, 0xc6, 0x2d, 0x99, 0x55, 0xc7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char error_icon_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0xe, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x5, 0xa3, 0x0, 0x1, 0x0, 0x2, 0x10, 0x0, 0x1, 0x14, 0xc2, 0xc0, 0x92, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char focus_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x4, 0x3, 0x0, 0x0, 0x0, 0xa4, 0x5b, 0x41, 0xd4, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0xff, 0xff, 0xff, 0xb9, 0xa2, 0x9b, 0xc9, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x10, 0x32, 0x9, 0xd, 0x75, 0x56, 0x64, 0x48, 0xef, 0x9c, 0x39, 0x73, 0x46, 0x19, 0xc3, 0x6a, 0x6, 0x20, 0xd8, 0xc5, 0x10, 0x3, 0xa2, 0x8e, 0x32, 0x44, 0x82, 0xa8, 0xa9, 0xd8, 0x29, 0xa8, 0x12, 0xb0, 0x6, 0x29, 0x86, 0xdc, 0x9d, 0x33, 0x67, 0xce, 0x2b, 0x63, 0x10, 0x3, 0x1b, 0x6, 0x0, 0xdf, 0xc6, 0x11, 0x6d, 0xb8, 0xf4, 0x9c, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x6, 0x0, 0x0, 0x0, 0x13, 0x7d, 0xf7, 0x96, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x8, 0x17, 0xd, 0x5, 0x12, 0xa1, 0x38, 0x83, 0x9b, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x2, 0x74, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x97, 0x3d, 0x6f, 0xd3, 0x60, 0x10, 0xc7, 0x7f, 0x17, 0x9b, 0x26, 0x25, 0x22, 0xad, 0xa, 0x8, 0xf1, 0x52, 0x75, 0x0, 0x16, 0x24, 0x90, 0x2a, 0x96, 0x7c, 0x1, 0x6, 0xc4, 0xce, 0xc4, 0x17, 0x0, 0x31, 0xb0, 0x30, 0x0, 0x23, 0x82, 0x85, 0x5, 0x9, 0x4, 0x5f, 0x80, 0x89, 0x1d, 0x31, 0xf0, 0x5, 0x58, 0x50, 0x25, 0x50, 0x59, 0x80, 0xa1, 0xe2, 0xad, 0x28, 0x34, 0x4a, 0x3, 0x25, 0x76, 0xea, 0xe7, 0x39, 0x6, 0x3f, 0x76, 0x6d, 0xc7, 0x49, 0x5f, 0xd8, 0x90, 0x6f, 0xb1, 0xf5, 0xe4, 0xb9, 0xdf, 0xdd, 0xfd, 0xef, 0x22, 0xf9, 0x84, 0x2d, 0x13, 0xa0, 0x6, 0x78, 0xee, 0x29, 0xe4, 0x4d, 0x1, 0xb, 0x18, 0xf7, 0x54, 0x32, 0x97, 0x6a, 0xc0, 0x7e, 0x60, 0xe, 0x38, 0xc, 0xb4, 0x80, 0x7d, 0x5, 0xc0, 0x26, 0xd0, 0x7, 0x3a, 0x40, 0x17, 0xf8, 0x3, 0xd8, 0x24, 0x6a, 0x13, 0x38, 0x35, 0x73, 0x60, 0xf6, 0x6a, 0xa3, 0xde, 0xb8, 0x38, 0x35, 0x55, 0x3f, 0x41, 0x89, 0xd, 0x87, 0xe1, 0x97, 0x20, 0xc, 0x5e, 0xae, 0xff, 0xea, 0x3d, 0x5, 0x3e, 0x2, 0x1b, 0xe2, 0x22, 0x2d, 0xcc, 0xcd, 0x1e, 0x7c, 0x78, 0x72, 0xe1, 0xf4, 0xa5, 0x7b, 0xb7, 0x1f, 0x7c, 0x9e, 0x6e, 0x35, 0xca, 0xfc, 0x19, 0xf4, 0x3, 0xee, 0xdc, 0xbf, 0x39, 0xff, 0x69, 0xe5, 0xc3, 0x8b, 0x6e, 0x6f, 0xed, 0x6, 0xb0, 0x22, 0x40, 0x3, 0x58, 0x3c, 0x7a, 0xe4, 0xf8, 0xab, 0x67, 0x4f, 0x9e, 0x77, 0xa3, 0xc8, 0x12, 0xd, 0xa3, 0x52, 0x80, 0x3f, 0xe5, 0xe3, 0xfb, 0x35, 0xae, 0x5c, 0xbb, 0x3c, 0xf7, 0xfd, 0xc7, 0xd7, 0xb, 0xc0, 0x92, 0xef, 0x74, 0x68, 0xfa, 0x9e, 0xdf, 0x1c, 0xfc, 0xe, 0xbb, 0x88, 0xc6, 0x47, 0xa, 0x2a, 0x59, 0x85, 0x95, 0x61, 0xb8, 0xc9, 0x30, 0x4, 0xdf, 0xf3, 0x9b, 0xae, 0x6c, 0x49, 0x0, 0x1e, 0x80, 0x51, 0x1b, 0xfb, 0xc7, 0x2, 0xc7, 0x5a, 0x3b, 0x88, 0x66, 0xcf, 0x63, 0xf3, 0x12, 0x80, 0x26, 0xbf, 0xa8, 0xb5, 0xa8, 0xa, 0x88, 0x22, 0x8, 0x8a, 0x22, 0x2a, 0x19, 0x37, 0x1d, 0xe9, 0xad, 0x9f, 0x6b, 0xb4, 0x55, 0x14, 0x5, 0x55, 0x44, 0xe2, 0x32, 0x6c, 0xe2, 0x24, 0x71, 0x18, 0x9d, 0x4, 0xb0, 0x6a, 0xd3, 0x1b, 0x2a, 0xe9, 0x4b, 0xfc, 0xae, 0xe4, 0x45, 0x19, 0x9f, 0x81, 0x2b, 0xdb, 0x15, 0xa6, 0xa2, 0x19, 0x2d, 0xec, 0x8, 0x24, 0x7, 0x30, 0xd6, 0x16, 0xf8, 0x82, 0xa8, 0xc6, 0x45, 0x68, 0x52, 0xbf, 0x4e, 0xce, 0x60, 0xeb, 0x42, 0x5c, 0xb4, 0x66, 0x9c, 0x6c, 0xc9, 0x1f, 0xa4, 0x0, 0xb0, 0x39, 0xc5, 0xc9, 0xa1, 0x28, 0x89, 0x5f, 0x14, 0xd1, 0x5a, 0x37, 0x32, 0x89, 0xf2, 0x82, 0xc6, 0x3, 0xe0, 0xda, 0xa, 0x22, 0x3a, 0xa9, 0xb, 0x9a, 0x26, 0xaa, 0xf1, 0x41, 0x9a, 0x42, 0x2, 0xb2, 0xb2, 0x6d, 0x6, 0x5b, 0xa3, 0xab, 0x23, 0xd5, 0x48, 0x32, 0x92, 0xe3, 0x33, 0x10, 0x37, 0xb7, 0x2a, 0xea, 0xee, 0x4a, 0x2a, 0x42, 0xe, 0x5a, 0x2a, 0xa2, 0x51, 0x37, 0x40, 0x85, 0x48, 0x96, 0xb4, 0x23, 0xdb, 0x88, 0x68, 0x5c, 0xc4, 0xcc, 0x3c, 0x14, 0xa7, 0x6f, 0xa2, 0x6, 0xaa, 0xb1, 0x7c, 0xd9, 0x30, 0xaa, 0x79, 0xc7, 0x49, 0x93, 0x78, 0xf7, 0xd1, 0xad, 0x79, 0x76, 0x69, 0x29, 0x20, 0xda, 0x34, 0x2c, 0x9e, 0x3d, 0xff, 0x7a, 0x27, 0x4e, 0x4b, 0xef, 0xde, 0xb4, 0x4b, 0x33, 0x58, 0xef, 0xf7, 0x76, 0x9b, 0x0, 0x35, 0xfe, 0xd1, 0x2a, 0x40, 0x5, 0xa8, 0x0, 0x15, 0xa0, 0x2, 0x54, 0x80, 0xa, 0xf0, 0x5f, 0x2, 0xa4, 0xe4, 0x13, 0x78, 0xd7, 0x19, 0xd8, 0x3d, 0xf8, 0xda, 0x4, 0x60, 0x81, 0xc0, 0x18, 0x13, 0xda, 0x68, 0x7, 0x5e, 0x11, 0x18, 0x63, 0x42, 0x20, 0x0, 0x6c, 0xcd, 0xad, 0xb2, 0x6b, 0x41, 0x38, 0x58, 0xee, 0x74, 0x57, 0xdb, 0x93, 0x20, 0x36, 0x82, 0x4e, 0x77, 0xb5, 0x1d, 0x84, 0x83, 0x65, 0x60, 0xd, 0x30, 0xc9, 0xe6, 0x3a, 0x3, 0x9c, 0x6b, 0x35, 0x67, 0x1f, 0x37, 0xea, 0xd3, 0x67, 0x3c, 0xcf, 0x2b, 0x15, 0xd7, 0x18, 0x63, 0x83, 0x70, 0xf0, 0xbe, 0xbf, 0xd1, 0xbb, 0xe, 0xbc, 0x5, 0xd6, 0x25, 0xb3, 0xc2, 0xb5, 0x80, 0x63, 0xc0, 0x21, 0xa0, 0x3e, 0x66, 0xf9, 0xe, 0x81, 0x9f, 0xc0, 0x37, 0xb7, 0x47, 0x1b, 0x29, 0x8, 0xea, 0x27, 0xfb, 0xe0, 0x98, 0x2a, 0xd4, 0x95, 0x1c, 0xed, 0x51, 0xf8, 0x51, 0xfb, 0xb, 0x1, 0xbe, 0x20, 0x9f, 0x90, 0x81, 0x17, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_breakpoint_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x2, 0x3, 0x0, 0x0, 0x0, 0x6e, 0x13, 0x1f, 0x5, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf4, 0xe7, 0x2c, 0xf4, 0xe7, 0x2c, 0xec, 0x5a, 0x6b, 0x42, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe4, 0xd1, 0xf4, 0xeb, 0x59, 0x0, 0x0, 0x0, 0x30, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x3, 0xb6, 0x55, 0xab, 0x26, 0x30, 0x88, 0x30, 0x0, 0x91, 0x42, 0xd6, 0x4a, 0xe, 0x6, 0x45, 0x7, 0x46, 0xf, 0x6, 0x25, 0x6, 0x86, 0xe, 0x20, 0x31, 0x4a, 0x80, 0x42, 0x3, 0x1c, 0x2e, 0xe0, 0x10, 0x82, 0x84, 0x15, 0x1c, 0x0, 0x0, 0x41, 0x2d, 0x2b, 0x21, 0xbb, 0xb7, 0x1a, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_close_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x75, 0x90, 0xb5, 0x1, 0xc0, 0x30, 0x10, 0x3, 0x55, 0x1a, 0xbe, 0xc, 0x6e, 0xe6, 0xb5, 0xc3, 0x34, 0x4b, 0x98, 0xcc, 0xee, 0x7c, 0x4f, 0x92, 0x0, 0x70, 0x45, 0x19, 0x8c, 0x47, 0x19, 0x57, 0x37, 0x66, 0x1b, 0x6b, 0x74, 0x89, 0x32, 0xd6, 0xb0, 0xed, 0x2c, 0x51, 0xca, 0x6b, 0xb6, 0xb3, 0x41, 0x94, 0x0, 0xfe, 0x9f, 0x2c, 0x0, 0xa3, 0x64, 0x61, 0xa3, 0x6f, 0x66, 0xbd, 0xc6, 0x7f, 0xe9, 0x86, 0x3b, 0x5b, 0x34, 0x76, 0xa, 0xcf, 0xad, 0xe0, 0xaa, 0xbf, 0xa4, 0x4f, 0x5a, 0xa, 0x6d, 0x25, 0xba, 0x14, 0x37, 0x18, 0x8b, 0xe4, 0x0, 0x6f, 0xe9, 0x37, 0x83, 0x22, 0x73, 0x83, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_comment_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x0, 0x78, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xd, 0x10, 0x17, 0x14, 0x18, 0x1d, 0x1a, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x10, 0x13, 0x35, 0x2f, 0x38, 0x96, 0x42, 0x2b, 0x0, 0x0, 0x0, 0x19, 0x17, 0x1b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x15, 0x1c, 0x77, 0x2f, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0xe, 0xb, 0x10, 0x24, 0x1e, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xb, 0x10, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0x85, 0xbb, 0x9b, 0xdf, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x6, 0x8, 0x9, 0x2, 0xc, 0x1e, 0x33, 0x41, 0x46, 0xd, 0x31, 0x9a, 0xe3, 0xff, 0x5, 0x24, 0xb4, 0xff, 0xe2, 0x39, 0xf4, 0x44, 0xa, 0x47, 0xff, 0x42, 0x45, 0x3d, 0xf8, 0x2a, 0xcd, 0xff, 0x11, 0x3f, 0xd3, 0xfd, 0x2b, 0x31, 0x64, 0xfe, 0xeb, 0x0, 0x0, 0x0, 0x8d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xc8, 0x35, 0x62, 0x3, 0x41, 0x14, 0x4, 0xd1, 0xee, 0x21, 0x33, 0x33, 0xdb, 0xf7, 0xbf, 0x93, 0x99, 0x49, 0xcc, 0xd2, 0x7c, 0x2d, 0x53, 0xa6, 0x58, 0x5b, 0x59, 0x3d, 0x2, 0xc, 0x3, 0x20, 0x61, 0x20, 0x5c, 0x9, 0xc6, 0x74, 0x11, 0x20, 0x2c, 0x86, 0xbd, 0xec, 0x63, 0xa1, 0xd9, 0xa7, 0x47, 0x9a, 0x92, 0x7f, 0xda, 0x83, 0xa1, 0xcd, 0x60, 0xb2, 0xfa, 0xc7, 0x33, 0xf6, 0xb, 0xb0, 0x2e, 0xb4, 0xce, 0x2e, 0x17, 0x20, 0x2f, 0x82, 0x5d, 0x66, 0x2f, 0xb2, 0x20, 0xd4, 0x50, 0xc3, 0x19, 0x59, 0x1, 0xe3, 0xb, 0xa0, 0xa6, 0x34, 0xe7, 0x9c, 0x65, 0xa0, 0xe5, 0x9d, 0x7b, 0x3b, 0xe4, 0x38, 0x79, 0x27, 0xd2, 0xa2, 0xbb, 0x22, 0xd9, 0x8b, 0x7e, 0x43, 0x44, 0x5e, 0x8, 0x75, 0x67, 0x66, 0x1f, 0x3b, 0x0, 0x5a, 0x67, 0x7a, 0xfa, 0xe0, 0x9, 0xb8, 0x99, 0x3a, 0x44, 0xd8, 0xaf, 0xd7, 0x63, 0x10, 0x95, 0xe6, 0x1e, 0x57, 0xc1, 0x90, 0xf7, 0xdc, 0x9d, 0x9f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_comment_focus_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x0, 0x6f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x96, 0x42, 0x2b, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x77, 0x2f, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x5f, 0x8c, 0x8b, 0xc7, 0x0, 0x0, 0x0, 0x25, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x6, 0x8, 0x9, 0x2, 0xc, 0x1e, 0x33, 0x41, 0x46, 0xd, 0x31, 0x9a, 0xe3, 0xff, 0x5, 0x24, 0xb4, 0xe2, 0x39, 0xf4, 0x44, 0xa, 0x47, 0x42, 0x45, 0x3d, 0xf8, 0x2a, 0xcd, 0x11, 0x3f, 0xd3, 0xfd, 0x2b, 0xb1, 0x1b, 0xa4, 0x4f, 0x0, 0x0, 0x0, 0x90, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xca, 0xb5, 0x75, 0xc4, 0x50, 0x0, 0x5, 0xd1, 0xf7, 0x49, 0xcc, 0xcc, 0xdc, 0x7f, 0x8b, 0x2b, 0xe6, 0xc8, 0xb1, 0x75, 0xc3, 0x39, 0x3, 0x80, 0x50, 0xc6, 0xc5, 0x84, 0x33, 0x4a, 0x30, 0x91, 0x64, 0x45, 0xd5, 0xf4, 0x89, 0xa6, 0x2a, 0xb2, 0x4, 0x48, 0x86, 0x69, 0xd9, 0xce, 0xc2, 0xb6, 0x4c, 0x43, 0x82, 0xeb, 0xf9, 0x4e, 0xb0, 0x71, 0x7c, 0xcf, 0x5, 0xf, 0xa3, 0xe0, 0x10, 0x85, 0x1c, 0x22, 0x76, 0xce, 0xe0, 0xc4, 0x2, 0x49, 0x7a, 0xd, 0x69, 0x2, 0xa1, 0xff, 0xb3, 0x70, 0xda, 0x42, 0x76, 0xf8, 0x73, 0xf8, 0xc2, 0x17, 0x92, 0xf4, 0x19, 0xf2, 0x6b, 0xc8, 0x13, 0xf0, 0xa2, 0x3c, 0x43, 0x59, 0x70, 0xb8, 0x55, 0x7d, 0x2c, 0x4e, 0x5d, 0xb9, 0x90, 0x9a, 0xb6, 0xeb, 0x9d, 0x45, 0xdf, 0xb5, 0x8d, 0x4, 0xd0, 0x66, 0x68, 0xf5, 0x74, 0xa2, 0xb7, 0x43, 0x43, 0x31, 0x91, 0x98, 0x48, 0x16, 0x82, 0x49, 0x78, 0x1b, 0x1, 0xf, 0xa7, 0x50, 0x68, 0x35, 0xb8, 0x84, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_default_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0xe, 0xb, 0x10, 0xe, 0xb, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0x19, 0x15, 0x1c, 0x24, 0x1e, 0x27, 0x16, 0x12, 0x19, 0x76, 0x9, 0xd2, 0x13, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x46, 0x47, 0x3f, 0x2b, 0x11, 0x3, 0xfd, 0xd3, 0xcd, 0x2a, 0x73, 0x45, 0xf8, 0x3d, 0x3f, 0x57, 0xda, 0x84, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x64, 0x2, 0x2, 0x46, 0x8, 0xc9, 0xcc, 0xc2, 0xca, 0xc6, 0xc0, 0x8f, 0x4, 0xd8, 0x39, 0x98, 0x59, 0x19, 0x50, 0x80, 0x0, 0x27, 0x17, 0x3, 0x2a, 0x10, 0xe4, 0x46, 0x13, 0xe0, 0x67, 0x1a, 0x18, 0x1, 0x1e, 0x34, 0x1, 0x5e, 0x3e, 0xc, 0xa7, 0x63, 0x78, 0xe, 0xc3, 0xfb, 0x0, 0x89, 0x4d, 0x2, 0xf2, 0xa2, 0x23, 0x3b, 0xc4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_default_focus_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x8a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x81, 0xff, 0x8c, 0xff, 0x99, 0xff, 0xb3, 0x2, 0x21, 0x3b, 0x4, 0x82, 0xd8, 0x40, 0x11, 0x46, 0x88, 0xa4, 0xf8, 0x7f, 0x49, 0x20, 0x14, 0xff, 0x2f, 0xa, 0x84, 0x8, 0xb6, 0xe0, 0x7f, 0x6e, 0xa0, 0x22, 0x90, 0x92, 0x92, 0xff, 0xd8, 0x60, 0xe5, 0x9b, 0xb6, 0x15, 0xff, 0x79, 0xc1, 0x4a, 0xb0, 0x83, 0x17, 0xc2, 0x29, 0xff, 0x1b, 0xe, 0x1, 0x4d, 0x61, 0xc6, 0xa1, 0x80, 0x5b, 0xf9, 0xf, 0x63, 0xe9, 0x77, 0xa0, 0x45, 0xac, 0xc, 0x38, 0x0, 0xd8, 0x72, 0xa0, 0x5b, 0xd8, 0xf1, 0x2b, 0x10, 0x27, 0xa4, 0x40, 0x92, 0x7c, 0x5, 0x8, 0x2b, 0xc8, 0x77, 0xe4, 0xb, 0xe1, 0xd2, 0x6f, 0x78, 0xbc, 0xf9, 0x87, 0x17, 0x18, 0x50, 0x7, 0x40, 0x1, 0x85, 0x23, 0xa8, 0x4b, 0xbf, 0x3, 0xc3, 0x91, 0x1f, 0x14, 0xd4, 0x78, 0x23, 0xb, 0x2d, 0xa0, 0xff, 0xb3, 0x23, 0x20, 0x22, 0xba, 0x1, 0x39, 0x96, 0x8a, 0xa5, 0x9b, 0x88, 0xa3, 0x56, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_position_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x2, 0x3, 0x0, 0x0, 0x0, 0x6e, 0x13, 0x1f, 0x5, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf4, 0x3f, 0x2c, 0xf4, 0x3f, 0x2c, 0x1c, 0x3e, 0x10, 0xcd, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe4, 0xd1, 0xf4, 0xeb, 0x59, 0x0, 0x0, 0x0, 0x30, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x3, 0xb6, 0x55, 0xab, 0x26, 0x30, 0x88, 0x30, 0x0, 0x91, 0x42, 0xd6, 0x4a, 0xe, 0x6, 0x45, 0x7, 0x46, 0xf, 0x6, 0x25, 0x6, 0x86, 0xe, 0x20, 0x31, 0x4a, 0x80, 0x42, 0x3, 0x1c, 0x2e, 0xe0, 0x10, 0x82, 0x84, 0x15, 0x1c, 0x0, 0x0, 0x41, 0x2d, 0x2b, 0x21, 0xbb, 0xb7, 0x1a, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_selected_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x1, 0x5f, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb2, 0xb2, 0xcc, 0xae, 0xa0, 0xbb, 0x9c, 0x92, 0xa6, 0x9a, 0x91, 0xa4, 0x95, 0x8c, 0x9e, 0xaf, 0x9f, 0xaf, 0xa0, 0x94, 0xa5, 0x8e, 0x84, 0x95, 0x7f, 0x75, 0x84, 0x73, 0x6a, 0x78, 0x6d, 0x64, 0x72, 0xa2, 0xa2, 0xb9, 0x9c, 0x90, 0xa2, 0x8b, 0x81, 0x90, 0xdc, 0xda, 0xce, 0xe2, 0xe1, 0xd2, 0x9d, 0x91, 0xa9, 0x85, 0x7c, 0x8c, 0xdb, 0xd9, 0xce, 0xdb, 0xd9, 0xcd, 0xda, 0xce, 0xe0, 0xde, 0xd5, 0xe3, 0xdf, 0xd6, 0xe4, 0x97, 0x8d, 0xa0, 0x7a, 0x70, 0x7f, 0xdb, 0xd0, 0xdf, 0xdb, 0xd0, 0xe1, 0xda, 0xd0, 0xe1, 0x70, 0x67, 0x75, 0xd8, 0xcb, 0xde, 0xda, 0xcf, 0xdf, 0xdb, 0xce, 0xe1, 0xdb, 0xcf, 0xe1, 0xdb, 0xd0, 0xe0, 0xda, 0xcf, 0xe0, 0xd8, 0xcc, 0xde, 0x90, 0x87, 0x99, 0x6d, 0x67, 0x72, 0xd7, 0xcc, 0xdf, 0xda, 0xce, 0xdf, 0xd8, 0xcb, 0xdf, 0xd7, 0xca, 0xde, 0xd9, 0xcc, 0xdf, 0xd9, 0xcd, 0xdf, 0xd6, 0xc9, 0xdd, 0xd9, 0xcd, 0xde, 0xd6, 0xc8, 0xdc, 0xd5, 0xc8, 0xdc, 0xd7, 0xcb, 0xdd, 0xd7, 0xca, 0xdd, 0xd5, 0xc7, 0xdc, 0xd3, 0xc6, 0xdb, 0xd5, 0xc9, 0xdc, 0xd5, 0xc9, 0xdd, 0xd6, 0xc9, 0xdc, 0xd4, 0xc6, 0xdb, 0xd3, 0xc5, 0xdb, 0xd5, 0xc8, 0xdb, 0xd4, 0xc8, 0xdc, 0xd3, 0xc4, 0xd9, 0xd4, 0xc6, 0xda, 0xd2, 0xc3, 0xd9, 0xd3, 0xc5, 0xda, 0xd2, 0xc5, 0xd9, 0xd3, 0xc5, 0xd9, 0xd2, 0xc5, 0xda, 0xd1, 0xc2, 0xd9, 0xd2, 0xc4, 0xd8, 0xd2, 0xc4, 0xd9, 0xd0, 0xc2, 0xd9, 0xd0, 0xc1, 0xd7, 0xd0, 0xc2, 0xd7, 0xd0, 0xc2, 0xd8, 0xd1, 0xc2, 0xd7, 0xcf, 0xc1, 0xd7, 0xd0, 0xc2, 0xd6, 0xcf, 0xc1, 0xd6, 0xcf, 0xc2, 0xd7, 0xcf, 0xc0, 0xd7, 0xce, 0xbf, 0xd6, 0xce, 0xc0, 0xd5, 0xce, 0xc0, 0xd6, 0xce, 0xbf, 0xd5, 0xcd, 0xbf, 0xd5, 0xcd, 0xbe, 0xd5, 0xcd, 0xbe, 0xd4, 0xcc, 0xbd, 0xd5, 0xcc, 0xbd, 0xd4, 0xcc, 0xbc, 0xd4, 0x47, 0x40, 0x4a, 0x1d, 0x1a, 0x1f, 0x69, 0x5f, 0x6f, 0x4a, 0x42, 0x4f, 0x5e, 0x54, 0x63, 0x3b, 0x34, 0x3f, 0x5e, 0x55, 0x63, 0x63, 0x59, 0x67, 0x77, 0x6d, 0x7b, 0x6d, 0x62, 0x73, 0x7f, 0x76, 0x85, 0xdb, 0xd9, 0xcd, 0xdb, 0xd8, 0xcd, 0x6d, 0x62, 0x74, 0x8f, 0x84, 0x94, 0x7f, 0x76, 0x83, 0xdb, 0xd8, 0xcd, 0xa4, 0x95, 0xa4, 0x7e, 0x74, 0x84, 0x74, 0x6b, 0x79, 0x6f, 0x66, 0x74, 0x96, 0x8a, 0xa2, 0x91, 0x88, 0x9b, 0x0, 0x0, 0x0, 0xaa, 0xaa, 0xaa, 0xbf, 0xbf, 0xbf, 0xf0, 0xc9, 0xec, 0x71, 0x0, 0x0, 0x0, 0x75, 0x74, 0x52, 0x4e, 0x53, 0x1, 0x3, 0xa, 0x13, 0x1a, 0x1c, 0x1d, 0x10, 0x2b, 0x4d, 0x64, 0x6e, 0x72, 0xb, 0x2c, 0x6a, 0xfc, 0xff, 0x15, 0x52, 0xfd, 0xff, 0xe2, 0xe2, 0xe2, 0x1b, 0x68, 0xe2, 0xe2, 0xe2, 0x71, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x1e, 0x72, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0x6b, 0xc7, 0x56, 0xfe, 0xff, 0xc7, 0x30, 0x74, 0xfe, 0x11, 0x57, 0x6d, 0x72, 0x16, 0x1c, 0x0, 0x3, 0x4, 0x35, 0xf5, 0x4, 0x26, 0x0, 0x0, 0x1, 0x16, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xcc, 0xc5, 0x61, 0x95, 0x61, 0x10, 0x85, 0xe1, 0xf7, 0x8c, 0x5c, 0xc3, 0xdd, 0x65, 0x4d, 0xea, 0xc8, 0x6, 0xdb, 0x63, 0xab, 0x74, 0x90, 0x42, 0xe8, 0x20, 0x5b, 0x3a, 0xa0, 0x12, 0xaa, 0xc0, 0x25, 0xd7, 0xe5, 0x1f, 0xec, 0xc3, 0xa5, 0x82, 0x9c, 0xdd, 0x79, 0x46, 0x84, 0xbe, 0x4, 0xa8, 0x2f, 0x91, 0x7a, 0xbf, 0xc0, 0x52, 0x47, 0x5a, 0xa5, 0x51, 0xe4, 0x6f, 0x27, 0xd1, 0xd7, 0x6c, 0x24, 0x15, 0xa0, 0xaa, 0xe9, 0xb0, 0xec, 0x75, 0x10, 0x51, 0x9, 0xbd, 0xde, 0x60, 0x44, 0xbc, 0xb6, 0xeb, 0x43, 0x86, 0xc, 0x56, 0x3d, 0x96, 0xfb, 0x99, 0xc, 0xaf, 0xfb, 0xfe, 0xd4, 0x8f, 0xf5, 0xf3, 0xc3, 0x72, 0xb3, 0x39, 0x67, 0xec, 0xbf, 0x3b, 0x12, 0x80, 0x97, 0x9d, 0x3, 0xc0, 0x81, 0x0, 0xec, 0x2d, 0x3d, 0x96, 0x3d, 0x96, 0x27, 0x1a, 0xe8, 0x14, 0xa8, 0x54, 0x58, 0x83, 0x10, 0x48, 0x55, 0xa6, 0x6, 0x29, 0xa6, 0x0, 0x87, 0xba, 0x6, 0x2e, 0x8e, 0x0, 0xa0, 0x6, 0x26, 0xa9, 0x13, 0xe5, 0x6b, 0xc0, 0x80, 0xcc, 0x8c, 0xea, 0xb2, 0xd6, 0x83, 0xef, 0x4f, 0x25, 0x87, 0x91, 0xba, 0xb6, 0x11, 0x74, 0x96, 0xde, 0xf3, 0xae, 0x6d, 0x90, 0x3d, 0x15, 0xea, 0x12, 0xd1, 0x0, 0x41, 0x39, 0xd0, 0xc0, 0x4, 0x88, 0xa2, 0x1a, 0xb8, 0x0, 0xe0, 0x3b, 0xd8, 0x7f, 0xc1, 0xf8, 0x25, 0x7, 0xf0, 0x4b, 0x3b, 0x80, 0xb8, 0xae, 0x17, 0x56, 0xad, 0x18, 0x97, 0xca, 0x98, 0xa1, 0xd6, 0x11, 0x33, 0x6c, 0x7f, 0x1b, 0xe3, 0xfb, 0xc6, 0xf6, 0xbe, 0xf7, 0xb6, 0xb6, 0x2c, 0x3b, 0xa, 0xb3, 0xbe, 0x6e, 0xe8, 0x59, 0xac, 0x42, 0x7a, 0xd2, 0x36, 0xee, 0x57, 0xad, 0x62, 0xec, 0x1b, 0x7f, 0xa4, 0x3d, 0x60, 0xa7, 0x6a, 0xed, 0x63, 0xa1, 0xc3, 0x3b, 0x7a, 0xa, 0xc0, 0xad, 0xda, 0x1b, 0x57, 0xec, 0xf2, 0xd8, 0x75, 0x17, 0x0, 0x6a, 0x7f, 0x97, 0x8f, 0x54, 0xa2, 0x67, 0xc8, 0xba, 0xb8, 0x46, 0x24, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_port_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x6e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0x8e, 0xc1, 0xd, 0xc2, 0x30, 0x10, 0x4, 0xb7, 0x31, 0x22, 0x2a, 0x48, 0x41, 0x88, 0x4f, 0xa, 0x73, 0x42, 0x17, 0x1e, 0xd2, 0x40, 0xde, 0x3e, 0xf3, 0xb5, 0x8c, 0x8f, 0x13, 0x8, 0x29, 0x3b, 0xbf, 0xd1, 0x9e, 0xf6, 0xe4, 0xc9, 0x57, 0x36, 0x5e, 0x83, 0x95, 0x59, 0x11, 0xee, 0x34, 0xfa, 0x97, 0xbc, 0x44, 0x6b, 0xa8, 0xa3, 0xdb, 0xe0, 0x70, 0xdd, 0xf6, 0x49, 0x3c, 0x5c, 0xd5, 0x20, 0xf4, 0x2a, 0x2a, 0xdd, 0x7e, 0xb2, 0xb8, 0x34, 0x61, 0x27, 0x59, 0xc4, 0x76, 0x3a, 0x4f, 0x62, 0xa6, 0xbb, 0x2e, 0x83, 0x18, 0x7a, 0x5e, 0x7c, 0x7f, 0xf9, 0x7b, 0xa9, 0xe5, 0x9b, 0x22, 0xfb, 0x44, 0xa2, 0x62, 0xa4, 0x4f, 0x4b, 0x6f, 0x69, 0x3b, 0x9a, 0x7e, 0xcd, 0xde, 0x94, 0x27, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hseparator_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x2, 0x3, 0x0, 0x0, 0x0, 0xb9, 0x61, 0x56, 0x18, 0x0, 0x0, 0x0, 0xc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x73, 0x9b, 0xaa, 0xce, 0xdc, 0xe1, 0xff, 0xff, 0xff, 0x64, 0x6c, 0x1, 0xd2, 0x0, 0x0, 0x0, 0x3, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xb3, 0xb3, 0x67, 0xf6, 0xdb, 0x93, 0x0, 0x0, 0x0, 0x10, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x64, 0x60, 0x60, 0xc2, 0x40, 0x8c, 0xc, 0x0, 0x0, 0xc7, 0x0, 0xf, 0xf5, 0x92, 0x2f, 0xa7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x57, 0x2e, 0xcb, 0x70, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x68, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x1a, 0x96, 0x95, 0x6b, 0xe2, 0xd5, 0x49, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x4f, 0x83, 0x11, 0x3, 0x1, 0x10, 0xcc, 0x39, 0xef, 0xfe, 0xb, 0x7d, 0xdb, 0xc6, 0x8e, 0x8e, 0xb, 0xf8, 0x9d, 0x70, 0x1d, 0x9c, 0x46, 0x2d, 0xcc, 0x18, 0xea, 0x11, 0x80, 0xda, 0xb7, 0x36, 0xe, 0xf2, 0xbe, 0x2f, 0x80, 0x1c, 0xb0, 0xe5, 0xa2, 0x1f, 0xa5, 0xc0, 0xea, 0x12, 0x2c, 0x4, 0x75, 0x52, 0x80, 0x38, 0x46, 0x2b, 0x65, 0x9d, 0xa7, 0x97, 0xc1, 0xf5, 0x25, 0x82, 0x7a, 0x47, 0x4a, 0x83, 0xac, 0x93, 0x33, 0x8f, 0x83, 0xaa, 0xb2, 0xb4, 0xb8, 0x18, 0xbb, 0x58, 0xff, 0x4e, 0xdb, 0x1, 0xc3, 0xf1, 0x34, 0x3b, 0x7e, 0xbc, 0xb3, 0xc3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_grabber_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xf3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x1e, 0x30, 0x33, 0xf0, 0x32, 0xc8, 0x30, 0xa8, 0x3, 0xa1, 0xc, 0x90, 0xc5, 0x8c, 0x29, 0x2d, 0x96, 0x96, 0xd7, 0x79, 0x70, 0xc6, 0xfb, 0x19, 0xef, 0x3b, 0xf, 0xa6, 0xe5, 0x31, 0x88, 0xa1, 0x2b, 0xe1, 0x4d, 0xcb, 0x9b, 0xf4, 0xa2, 0xef, 0x7f, 0x3b, 0x10, 0xf6, 0xfd, 0x9f, 0xf4, 0x2, 0xa8, 0x84, 0x17, 0x55, 0x81, 0x4c, 0xe7, 0xc1, 0xbe, 0xff, 0x2d, 0xff, 0x9b, 0x81, 0xb0, 0x5, 0xa8, 0xa4, 0xf3, 0x20, 0xd0, 0x22, 0x14, 0xa0, 0x3e, 0xe3, 0x7d, 0x3b, 0x50, 0x12, 0xc, 0x81, 0xa6, 0xcc, 0x78, 0xf, 0x74, 0xb, 0xa, 0xd0, 0x98, 0xf6, 0x1, 0x59, 0xc1, 0xd4, 0xf, 0xc, 0x1a, 0xa8, 0xa, 0x94, 0xfa, 0x6f, 0x77, 0xc1, 0x15, 0x74, 0xfc, 0xef, 0xbb, 0xc7, 0xa0, 0x82, 0xaa, 0x40, 0xbc, 0x71, 0x7d, 0x3f, 0x5c, 0x41, 0xef, 0xff, 0xde, 0xa3, 0xc, 0x52, 0xa8, 0xa, 0xb8, 0x82, 0x52, 0xa7, 0xfd, 0x69, 0x5, 0x4b, 0xb7, 0xfe, 0x9f, 0xf6, 0xcf, 0x37, 0x85, 0x81, 0x7, 0x2d, 0x1c, 0x14, 0xd, 0xfb, 0x1f, 0x74, 0x82, 0x15, 0x74, 0xfe, 0x9f, 0xf8, 0x80, 0x45, 0x83, 0x81, 0x9, 0x55, 0x1, 0x23, 0x83, 0x48, 0xc5, 0x9c, 0xc9, 0xff, 0x5b, 0x81, 0x70, 0xf2, 0xff, 0x92, 0xa9, 0xc, 0xc2, 0x98, 0x41, 0xcd, 0xca, 0xa3, 0x33, 0xe1, 0x76, 0xcf, 0xff, 0x9e, 0xff, 0x13, 0xef, 0xf0, 0xe8, 0x30, 0xb0, 0x62, 0x8b, 0xd, 0x6e, 0xb, 0xff, 0x39, 0x5f, 0xe6, 0x7c, 0x77, 0x8, 0x45, 0xd8, 0x8f, 0x61, 0x4d, 0x51, 0x71, 0x55, 0x2d, 0x83, 0x18, 0x90, 0x85, 0x3, 0xb0, 0x30, 0x70, 0x3, 0x75, 0xb3, 0x20, 0xb, 0x1, 0x0, 0x86, 0xe, 0x79, 0x54, 0x16, 0xbe, 0x69, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_grabber_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xe7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x1e, 0x30, 0x33, 0xf0, 0x32, 0xc8, 0x30, 0xa8, 0x3, 0xa1, 0xc, 0x90, 0xc5, 0x8c, 0x29, 0x2d, 0x66, 0x9c, 0xe7, 0x7a, 0xd0, 0xe7, 0xbd, 0xcf, 0x7b, 0xd7, 0x83, 0xc6, 0x79, 0xc, 0x62, 0xe8, 0x4a, 0x78, 0x8d, 0xf3, 0x3c, 0x5f, 0xb8, 0xff, 0x77, 0x1, 0x42, 0xf7, 0xff, 0x9e, 0x2f, 0x80, 0x4a, 0x78, 0x51, 0x15, 0xc8, 0xb8, 0x1e, 0x74, 0xff, 0xef, 0x4, 0x81, 0x40, 0x25, 0xae, 0x7, 0x81, 0x16, 0xa1, 0x0, 0x75, 0x9f, 0xf7, 0x2e, 0x30, 0x5, 0x40, 0x53, 0x7c, 0xde, 0x3, 0xdd, 0x82, 0x2, 0x34, 0xbc, 0x3f, 0x20, 0x2b, 0xf0, 0xfe, 0xc0, 0xa0, 0x81, 0xaa, 0x40, 0xc9, 0xe3, 0xb6, 0x2b, 0x92, 0x2, 0xf7, 0x7b, 0xc, 0x2a, 0xa8, 0xa, 0xc4, 0x1d, 0xd7, 0x7b, 0x20, 0xb9, 0xc1, 0xfd, 0x28, 0x83, 0x14, 0xaa, 0x2, 0x2e, 0xcd, 0x54, 0xef, 0x3f, 0xce, 0x60, 0x69, 0xe7, 0xff, 0xde, 0xff, 0xd4, 0x53, 0x18, 0x78, 0xd0, 0xc2, 0x41, 0xd0, 0xd0, 0xe3, 0x1, 0xc4, 0x12, 0xd7, 0xff, 0x9e, 0xf, 0x98, 0x34, 0x18, 0x98, 0x50, 0x15, 0x30, 0x32, 0x88, 0xd8, 0xcc, 0xf1, 0xfa, 0xef, 0xc, 0x84, 0x5e, 0xff, 0xad, 0xa6, 0x32, 0x8, 0x63, 0x6, 0x35, 0x2b, 0x9b, 0x8e, 0xc7, 0x6d, 0xb7, 0xff, 0x6e, 0xff, 0x3d, 0xef, 0xb0, 0xe9, 0x30, 0xb0, 0x62, 0x8b, 0xd, 0x6e, 0x19, 0x7f, 0xbf, 0x2f, 0x7e, 0xdf, 0x15, 0x42, 0x11, 0xf6, 0x63, 0x58, 0x63, 0x59, 0x6c, 0x5b, 0xcb, 0x20, 0x6, 0x64, 0xe1, 0x0, 0x2c, 0xc, 0xdc, 0x40, 0xdd, 0x2c, 0xc8, 0x42, 0x0, 0x4, 0x5c, 0x63, 0x9b, 0xfc, 0xae, 0x1b, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_grabber_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xc3, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x48, 0x83, 0x83, 0x60, 0xaf, 0xb1, 0x65, 0xbb, 0xca, 0x61, 0xb3, 0xc2, 0x0, 0x0, 0x0, 0x63, 0xb7, 0xc8, 0x63, 0xb7, 0xc7, 0x0, 0x0, 0x0, 0x61, 0xb3, 0xbc, 0x60, 0xb1, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0xa6, 0xa5, 0x63, 0xb4, 0xb6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x69, 0x69, 0x5e, 0xb1, 0xcd, 0x5e, 0xb0, 0xcd, 0x36, 0x63, 0x63, 0x0, 0x0, 0x0, 0x17, 0x2a, 0x29, 0x60, 0xb2, 0xbd, 0x62, 0xb3, 0xbf, 0x3, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x9b, 0x9a, 0x52, 0x96, 0x95, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf, 0xf, 0x62, 0xb4, 0xbd, 0x63, 0xb7, 0xbf, 0x0, 0x0, 0x0, 0x27, 0x48, 0x47, 0x68, 0xc0, 0xcf, 0x68, 0xc1, 0xcf, 0x2d, 0x52, 0x52, 0x51, 0x93, 0x92, 0x56, 0x9d, 0x9c, 0x0, 0x0, 0x0, 0x54, 0xa2, 0xc8, 0x4c, 0x94, 0xc2, 0x48, 0x8e, 0xc0, 0x47, 0x8c, 0xbf, 0x4b, 0x93, 0xc2, 0x4b, 0x92, 0xc2, 0x4f, 0x98, 0xc4, 0x4d, 0x96, 0xc3, 0x55, 0xa3, 0xc8, 0x53, 0x9f, 0xc7, 0x49, 0x8f, 0xc0, 0x4e, 0x97, 0xc4, 0x5a, 0xab, 0xcb, 0x5a, 0xac, 0xcc, 0x52, 0x9e, 0xc6, 0x51, 0x9d, 0xc6, 0xd4, 0xd, 0x1d, 0x1c, 0x0, 0x0, 0x0, 0x31, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x3, 0xd, 0x1c, 0x27, 0x16, 0x6e, 0xc1, 0xef, 0xe8, 0x28, 0xf0, 0xf0, 0x22, 0xdb, 0xde, 0x24, 0x17, 0xaf, 0xc5, 0x1a, 0xa, 0x65, 0xfc, 0xfe, 0x64, 0xc, 0x31, 0xe0, 0xe0, 0x28, 0x2, 0x1, 0x14, 0x9c, 0x95, 0x13, 0x5, 0x2c, 0xdb, 0xdc, 0xb, 0x4f, 0xf4, 0xf7, 0x55, 0x73, 0x7d, 0x4, 0x28, 0xf1, 0xfd, 0xa1, 0x0, 0x0, 0x0, 0x7d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xcc, 0x81, 0x6, 0xc3, 0x40, 0x10, 0x84, 0xe1, 0xdd, 0x9d, 0xd9, 0x3d, 0x40, 0x1f, 0xa1, 0x0, 0xd1, 0xbe, 0xff, 0xbb, 0x94, 0x10, 0x29, 0x40, 0x1a, 0x40, 0x9, 0xa, 0xbd, 0x34, 0x7b, 0x39, 0xfa, 0x63, 0xf0, 0x61, 0x4c, 0xfe, 0xfa, 0x81, 0x1a, 0x48, 0x98, 0x36, 0x50, 0x66, 0xda, 0x40, 0x71, 0xab, 0x41, 0x3b, 0xc, 0x56, 0x1b, 0x3a, 0xd4, 0x8e, 0x4d, 0x8, 0x40, 0x6a, 0x64, 0x24, 0x0, 0xb6, 0x83, 0xa1, 0x1, 0x79, 0x0, 0x79, 0xc2, 0x54, 0x44, 0xca, 0x14, 0x91, 0xb0, 0xba, 0xef, 0xa7, 0xee, 0x9e, 0x70, 0x8d, 0xd0, 0x52, 0x2c, 0xe2, 0x99, 0x30, 0x93, 0xb, 0x7d, 0x81, 0x4a, 0x82, 0x8c, 0xc0, 0xba, 0xfa, 0x7b, 0x3c, 0x41, 0xb6, 0xd, 0x78, 0x84, 0x74, 0x98, 0x2f, 0x9f, 0xd7, 0x7d, 0x96, 0xbd, 0x2f, 0xa5, 0x6b, 0x13, 0xc4, 0x35, 0x90, 0x18, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_tick_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xc3, 0x98, 0xc3, 0xc0, 0x0, 0x0, 0x0, 0x1b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x38, 0x55, 0x5f, 0x8c, 0xac, 0xb8, 0x81, 0xa2, 0xad, 0x98, 0x98, 0x98, 0x4e, 0x4e, 0x4e, 0x8d, 0x8d, 0x8d, 0x82, 0x82, 0x82, 0x4e, 0x4e, 0x4e, 0xc9, 0xf6, 0x7, 0x31, 0x0, 0x0, 0x0, 0x9, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x79, 0x31, 0x79, 0x79, 0x1c, 0x7e, 0xed, 0x4b, 0xf4, 0x0, 0x0, 0x0, 0x20, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x9, 0x60, 0x60, 0xb, 0x60, 0x60, 0xf, 0x60, 0x60, 0x6d, 0x60, 0x60, 0x14, 0x60, 0xc0, 0x4, 0x4c, 0x2, 0xc, 0xcc, 0x2, 0x70, 0x65, 0x0, 0x46, 0x9d, 0x2, 0xbe, 0xb7, 0xdf, 0x14, 0x38, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hsplit_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0xff, 0xff, 0xff, 0x11, 0xab, 0xb9, 0xf3, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hsplitter_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x40, 0x8, 0x0, 0x0, 0x0, 0x0, 0x2, 0x6f, 0x69, 0x56, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x1a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x80, 0x5, 0xa9, 0x10, 0xcc, 0x90, 0x6a, 0xd, 0xc6, 0x70, 0x80, 0x4b, 0xcd, 0xa8, 0x9a, 0x51, 0x35, 0x0, 0x78, 0xd5, 0x34, 0xa1, 0x54, 0x8c, 0xd5, 0x84, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_add_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x3c, 0xf8, 0xff, 0xe0, 0xff, 0xd0, 0x52, 0x80, 0x10, 0xc4, 0xd, 0x9, 0x2a, 0x18, 0x26, 0xe1, 0x40, 0x18, 0x0, 0x0, 0x5b, 0x26, 0x61, 0x4d, 0xc9, 0xc1, 0x48, 0x81, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_close_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_color_pick_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xaa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x9d, 0x8e, 0x35, 0x82, 0x2, 0x41, 0x10, 0x45, 0x3b, 0xda, 0x3d, 0xca, 0xba, 0x44, 0x2b, 0x70, 0x9, 0xdc, 0xe1, 0x20, 0xe8, 0x91, 0x90, 0x78, 0x6e, 0x40, 0x4c, 0x82, 0x74, 0xff, 0xc2, 0x9d, 0x18, 0xa7, 0x6, 0x77, 0x7b, 0x23, 0x2d, 0xaf, 0x4c, 0xdc, 0xc, 0xbd, 0x65, 0x1e, 0x84, 0x80, 0x19, 0x55, 0x34, 0x60, 0x3e, 0xd0, 0xea, 0x17, 0x3d, 0x4a, 0xc8, 0x80, 0x1a, 0x60, 0xc2, 0x4f, 0xfd, 0x30, 0xe0, 0x1b, 0x2d, 0x16, 0xab, 0xa7, 0x2c, 0xe, 0x41, 0x68, 0xa5, 0xb9, 0xca, 0x91, 0x16, 0x2e, 0x54, 0xe0, 0x59, 0x54, 0x91, 0xfe, 0xa3, 0x3a, 0xff, 0xce, 0xab, 0x5b, 0xf, 0xa0, 0x4, 0x8f, 0x7b, 0x4c, 0xd3, 0x1b, 0xca, 0x32, 0xcc, 0x55, 0x7a, 0xf4, 0x76, 0x42, 0x2b, 0x97, 0x3e, 0xae, 0xfa, 0xdd, 0xd2, 0xd2, 0x8e, 0x72, 0xe1, 0x83, 0xaf, 0x9f, 0xa9, 0x28, 0x7d, 0x5b, 0xe2, 0x2a, 0xd, 0xc3, 0xa2, 0x78, 0xfe, 0x7d, 0x51, 0xfc, 0x0, 0x8a, 0x41, 0xcb, 0x3d, 0xb2, 0xae, 0x1c, 0xd3, 0xc, 0xa5, 0x30, 0x81, 0xc6, 0xda, 0x29, 0x8e, 0x83, 0x34, 0x25, 0x29, 0x4a, 0x46, 0x71, 0x1f, 0x33, 0xbe, 0x51, 0x89, 0xaf, 0x78, 0xe3, 0x97, 0x7e, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_file_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x2, 0x3, 0x0, 0x0, 0x0, 0x62, 0x9d, 0x17, 0xf2, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0x42, 0xf, 0xc7, 0x49, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x88, 0x95, 0xf0, 0xc6, 0x2a, 0x0, 0x0, 0x0, 0x21, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x0, 0x1, 0xae, 0x55, 0x2d, 0x20, 0xa2, 0x13, 0x44, 0x74, 0x39, 0x80, 0x88, 0x9, 0x40, 0xa2, 0x1, 0xc4, 0x5d, 0xb5, 0x80, 0x68, 0x2, 0x4, 0x0, 0x95, 0x34, 0x18, 0xe4, 0x5e, 0x46, 0xf7, 0x27, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_folder_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_grid_layout_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x5, 0x52, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0xa, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2e, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x23, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3d, 0x22, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3d, 0x22, 0x32, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x3d, 0x22, 0x33, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x49, 0x43, 0x43, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x3d, 0x22, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x20, 0x78, 0x6d, 0x6c, 0x3a, 0x6c, 0x61, 0x6e, 0x67, 0x3d, 0x22, 0x78, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x3e, 0x47, 0x72, 0x69, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x20, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x20, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x77, 0x68, 0x65, 0x6e, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x2f, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x10, 0xfa, 0x51, 0xae, 0x0, 0x0, 0x1, 0x82, 0x69, 0x43, 0x43, 0x50, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x0, 0x0, 0x28, 0x91, 0x75, 0x91, 0xbf, 0x4b, 0x42, 0x51, 0x14, 0xc7, 0x3f, 0x5a, 0xa1, 0x94, 0x61, 0x50, 0x43, 0x43, 0x83, 0x84, 0x45, 0x83, 0x85, 0x15, 0x84, 0x2d, 0xd, 0x4a, 0xbf, 0xa0, 0x1a, 0xd4, 0x20, 0xab, 0x45, 0x9f, 0xbf, 0x2, 0xb5, 0xc7, 0x7b, 0x4a, 0x48, 0x6b, 0xd0, 0x2a, 0x14, 0x44, 0x2d, 0xfd, 0x1a, 0xea, 0x2f, 0xa8, 0x35, 0x68, 0xe, 0x82, 0xa2, 0x8, 0xa2, 0x2d, 0x68, 0x2e, 0x6a, 0xa9, 0x78, 0x9d, 0xa7, 0x82, 0x11, 0x79, 0x2e, 0xe7, 0x9e, 0xcf, 0xfd, 0xde, 0x7b, 0xe, 0xf7, 0x9e, 0xb, 0xd6, 0x70, 0x46, 0xc9, 0xea, 0x8d, 0x5e, 0xc8, 0xe6, 0xf2, 0x5a, 0x70, 0xd2, 0xef, 0x5a, 0x88, 0x2c, 0xba, 0x6c, 0xcf, 0xd8, 0x71, 0x62, 0xa3, 0xf, 0x5f, 0x54, 0xd1, 0xd5, 0xd9, 0xd0, 0x44, 0x98, 0xba, 0xf6, 0x71, 0x87, 0xc5, 0x8c, 0x37, 0xfd, 0x66, 0xad, 0xfa, 0xe7, 0xfe, 0xb5, 0x96, 0x78, 0x42, 0x57, 0xc0, 0x62, 0x17, 0x1e, 0x53, 0x54, 0x2d, 0x2f, 0x3c, 0x25, 0x3c, 0xb3, 0x96, 0x57, 0x4d, 0xde, 0x16, 0xee, 0x50, 0xd2, 0xd1, 0xb8, 0xf0, 0xa9, 0xb0, 0x47, 0x93, 0xb, 0xa, 0xdf, 0x9a, 0x7a, 0xac, 0xc2, 0x2f, 0x26, 0xa7, 0x2a, 0xfc, 0x65, 0xb2, 0x16, 0xe, 0x6, 0xc0, 0xda, 0x26, 0xec, 0x4a, 0xfd, 0xe2, 0xd8, 0x2f, 0x56, 0xd2, 0x5a, 0x56, 0x58, 0x5e, 0x8e, 0x3b, 0x9b, 0x29, 0x28, 0xd5, 0xfb, 0x98, 0x2f, 0x71, 0x24, 0x72, 0xf3, 0x21, 0x89, 0xdd, 0xe2, 0x5d, 0xe8, 0x4, 0x99, 0xc4, 0x8f, 0x8b, 0x69, 0xc6, 0x9, 0x30, 0xc2, 0x20, 0xa3, 0x32, 0x8f, 0xd0, 0xcf, 0x10, 0x3, 0xb2, 0xa2, 0x4e, 0xbe, 0xb7, 0x9c, 0x3f, 0xc7, 0xaa, 0xe4, 0x2a, 0x32, 0xab, 0x14, 0xd1, 0x58, 0x21, 0x45, 0x9a, 0x3c, 0x1e, 0x51, 0xb, 0x52, 0x3d, 0x21, 0x31, 0x29, 0x7a, 0x42, 0x46, 0x86, 0xa2, 0xd9, 0xff, 0xbf, 0x7d, 0xd5, 0x93, 0xc3, 0x43, 0x95, 0xea, 0xe, 0x3f, 0x34, 0x3d, 0x19, 0xc6, 0x5b, 0xf, 0xd8, 0xb6, 0xe0, 0xbb, 0x64, 0x18, 0x9f, 0x87, 0x86, 0xf1, 0x7d, 0x4, 0xd, 0x8f, 0x70, 0x91, 0xab, 0xe5, 0xaf, 0x1e, 0x80, 0xef, 0x5d, 0xf4, 0x52, 0x4d, 0x73, 0xef, 0x83, 0x73, 0x3, 0xce, 0x2e, 0x6b, 0x5a, 0x6c, 0x7, 0xce, 0x37, 0xa1, 0xf3, 0x41, 0x8d, 0x6a, 0xd1, 0xb2, 0xd4, 0x20, 0x6e, 0x4d, 0x26, 0xe1, 0xf5, 0x4, 0x5a, 0x23, 0xd0, 0x7e, 0xd, 0xcd, 0x4b, 0x95, 0x9e, 0x55, 0xf7, 0x39, 0xbe, 0x87, 0xf0, 0xba, 0x7c, 0xd5, 0x15, 0xec, 0xee, 0x41, 0xaf, 0x9c, 0x77, 0x2e, 0xff, 0x0, 0xa6, 0xc4, 0x68, 0x3, 0x1f, 0xd7, 0x32, 0xd8, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x1, 0x40, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x9d, 0xd2, 0xbd, 0x4a, 0x5d, 0x41, 0x14, 0x86, 0xe1, 0xe7, 0x84, 0xa3, 0x51, 0x22, 0x82, 0x41, 0x8c, 0x12, 0x2, 0xeb, 0x1a, 0x2c, 0x84, 0x4, 0x52, 0x88, 0xb1, 0xd2, 0x26, 0x76, 0x62, 0x67, 0xeb, 0xd, 0x78, 0x19, 0x56, 0x69, 0x82, 0x62, 0x67, 0x25, 0x82, 0x60, 0x97, 0xc2, 0x52, 0x8, 0x24, 0x95, 0x16, 0xa9, 0x6, 0x8c, 0x18, 0xb5, 0x30, 0x88, 0xa7, 0x90, 0x88, 0x5a, 0xec, 0x39, 0x66, 0xd8, 0x7a, 0x54, 0x5c, 0xb0, 0xd8, 0xcc, 0xbb, 0xe7, 0xfb, 0x58, 0x3f, 0xd3, 0x50, 0x8b, 0x94, 0xd2, 0x6b, 0xcc, 0x60, 0x25, 0x22, 0xae, 0xea, 0xff, 0xeb, 0xd1, 0xbc, 0x47, 0xfc, 0xd, 0xbd, 0x58, 0x7e, 0x4c, 0xc, 0x8d, 0x42, 0x3c, 0x82, 0x9f, 0xe8, 0xc1, 0x26, 0xce, 0x3b, 0x68, 0xae, 0xf1, 0x25, 0x22, 0x76, 0xeb, 0x15, 0xf4, 0xa3, 0xf, 0x97, 0x18, 0xce, 0xdf, 0x4e, 0x6, 0xfd, 0x77, 0x2a, 0xc8, 0x55, 0x4, 0xb6, 0xf1, 0x1b, 0x1f, 0x23, 0xe2, 0xfa, 0x49, 0x2d, 0xa4, 0x94, 0x7a, 0x30, 0x98, 0xd9, 0x5b, 0x4c, 0x63, 0x49, 0x35, 0xb, 0x68, 0xe1, 0x2f, 0x46, 0xf0, 0x22, 0xb3, 0x93, 0x88, 0xb8, 0x68, 0x1f, 0x36, 0xb0, 0x9f, 0x73, 0x7, 0x87, 0xf8, 0x53, 0xb0, 0x23, 0x2c, 0xe0, 0xa0, 0x60, 0xeb, 0xa, 0xb7, 0x1, 0xac, 0x60, 0x34, 0x5f, 0x1a, 0x52, 0xcd, 0x67, 0x16, 0x73, 0xe8, 0xca, 0x6c, 0x3f, 0xdf, 0x59, 0xcd, 0x9a, 0x5b, 0x83, 0x67, 0x47, 0x7b, 0xb, 0xa7, 0x98, 0xcf, 0x9, 0xc7, 0xaa, 0x2d, 0xac, 0xe5, 0xf3, 0xbf, 0xcc, 0xde, 0xe1, 0x47, 0x66, 0x5b, 0xa5, 0xc1, 0x67, 0xff, 0x87, 0x78, 0xa5, 0x9a, 0xc1, 0x1a, 0x5e, 0x65, 0xd6, 0xc2, 0x24, 0x3e, 0xe4, 0x36, 0xe0, 0x84, 0xda, 0x1a, 0x1f, 0x8a, 0x94, 0xd2, 0x6, 0xc6, 0x30, 0x1e, 0x11, 0xbf, 0xda, 0xbc, 0x7c, 0x89, 0xc3, 0x58, 0x44, 0x77, 0x7, 0x8f, 0x2e, 0x4c, 0xa9, 0x1e, 0xd1, 0xa7, 0x88, 0xd8, 0x29, 0x5b, 0xa0, 0x7a, 0xc2, 0xf1, 0x80, 0x41, 0x23, 0xe7, 0x4b, 0xbc, 0x79, 0x6a, 0xe5, 0x65, 0xb, 0x5f, 0x53, 0x4a, 0x67, 0x29, 0xa5, 0xf7, 0x25, 0x6f, 0x76, 0x12, 0xdc, 0x13, 0x7b, 0x98, 0x88, 0x88, 0xef, 0x25, 0xbc, 0x1, 0x6c, 0x4d, 0x56, 0x9e, 0x2a, 0x4e, 0x48, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_grid_minimap_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_parent_folder_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x33, 0xb8, 0x27, 0xfe, 0xe0, 0xfc, 0x83, 0x73, 0xf7, 0xc4, 0x71, 0x48, 0xdf, 0x11, 0x7b, 0x78, 0xe9, 0xc1, 0x3f, 0x20, 0xbc, 0xfe, 0x40, 0x12, 0x8f, 0x34, 0x4c, 0x9, 0xa6, 0xe1, 0x57, 0x80, 0x12, 0x17, 0x81, 0xf8, 0x2f, 0x58, 0xe1, 0x15, 0x34, 0x8b, 0x1e, 0x9c, 0x5, 0xa, 0x5e, 0xb8, 0x23, 0x6, 0x52, 0x70, 0x5b, 0x14, 0xac, 0xf0, 0xc, 0xaa, 0x82, 0x7d, 0xf, 0x8e, 0xde, 0x14, 0xf9, 0xcf, 0x8, 0x52, 0xc0, 0xc0, 0x70, 0x5b, 0xf4, 0xe1, 0xc9, 0x7, 0x47, 0xb1, 0xb8, 0x3, 0xaa, 0x0, 0xa, 0x48, 0x52, 0x80, 0xb0, 0xea, 0xc8, 0xc3, 0x83, 0xc, 0x83, 0xe, 0x0, 0x0, 0xb8, 0x27, 0x55, 0x4c, 0xbe, 0xc0, 0xd2, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_reload_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xb1, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x50, 0x35, 0xba, 0xc2, 0x40, 0x10, 0x7e, 0xcd, 0x33, 0x5c, 0xca, 0x1c, 0x21, 0x87, 0xc1, 0xa9, 0xd3, 0x23, 0x47, 0xca, 0x69, 0xb0, 0x6, 0x77, 0xdb, 0xfd, 0x17, 0x2f, 0x91, 0x3e, 0xee, 0xd2, 0xc1, 0x4e, 0xb5, 0xdf, 0xcc, 0xaf, 0x5f, 0x9f, 0x7c, 0xdb, 0x5f, 0xda, 0x44, 0x17, 0x2f, 0x75, 0xba, 0xa4, 0xb1, 0xfd, 0xf5, 0xad, 0x8f, 0x1c, 0x86, 0x90, 0x5c, 0x33, 0x38, 0x72, 0x1e, 0xb4, 0xbe, 0x66, 0x28, 0xad, 0xe2, 0xab, 0x38, 0xcd, 0x63, 0xa9, 0x9d, 0xb8, 0x58, 0x68, 0x53, 0x5b, 0x1f, 0x33, 0xd6, 0x9f, 0xa5, 0xc1, 0x20, 0x91, 0xba, 0x7d, 0x80, 0x1e, 0x24, 0x94, 0xdc, 0x92, 0xa4, 0x2, 0x9, 0x1d, 0xe7, 0xe0, 0x9, 0x69, 0x15, 0xf7, 0x58, 0x4e, 0x40, 0xc2, 0xc3, 0x58, 0x8a, 0xb6, 0x31, 0xd1, 0x39, 0xd8, 0x27, 0xed, 0x83, 0x5b, 0x14, 0x33, 0x7d, 0x3d, 0xbb, 0x45, 0x5d, 0x12, 0x55, 0x97, 0x4, 0xe3, 0xb5, 0xf4, 0x8c, 0x77, 0xd6, 0xa7, 0x2c, 0x3b, 0x78, 0x4c, 0x52, 0x81, 0xa, 0x8e, 0x3a, 0xa9, 0x6a, 0x6b, 0xc, 0xe6, 0x3f, 0xa1, 0x8d, 0x86, 0x16, 0xe5, 0x39, 0x78, 0xa2, 0x4d, 0xea, 0xe, 0xfa, 0xdd, 0xa7, 0x0, 0x90, 0x4f, 0x8b, 0xd0, 0xe1, 0x9e, 0x1b, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_snap_grid_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf3, 0xf3, 0xf3, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x85, 0x85, 0xff, 0x83, 0x83, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x80, 0x80, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0xff, 0xff, 0xa, 0xa5, 0x43, 0x1, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xff, 0x1d, 0xac, 0xf2, 0xaf, 0x27, 0xed, 0xff, 0xee, 0xb4, 0x1b, 0x1c, 0xb6, 0xaa, 0xf1, 0x50, 0xa6, 0xdd, 0x5f, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x2, 0x40, 0x23, 0xd3, 0x60, 0x0, 0x40, 0xc, 0x3, 0xaf, 0x76, 0x93, 0xfd, 0x97, 0x7d, 0x9b, 0x55, 0x70, 0x12, 0x62, 0xaf, 0x68, 0x0, 0xc4, 0xe9, 0x3c, 0x1, 0x67, 0xf7, 0x17, 0x20, 0x95, 0xd6, 0xc6, 0xee, 0x80, 0x74, 0xde, 0x7b, 0x1f, 0x24, 0xb0, 0x64, 0x29, 0x1f, 0x53, 0x2e, 0xbe, 0x6e, 0x80, 0xf6, 0x19, 0x90, 0x9e, 0x36, 0x8b, 0xf7, 0xc0, 0x5c, 0xdf, 0x0, 0x66, 0x60, 0xae, 0xf3, 0xb9, 0x1, 0xfb, 0xe9, 0x1, 0xa6, 0x26, 0x1, 0xcd, 0x30, 0x66, 0x63, 0x6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_visibility_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x3, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0xdb, 0xe1, 0x4f, 0xe0, 0x0, 0x0, 0x0, 0x96, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xdf, 0xdf, 0xdf, 0xe3, 0xe3, 0xe3, 0xe6, 0xe6, 0xe6, 0xd5, 0xd5, 0xd5, 0xd8, 0xd8, 0xd8, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xe1, 0xe1, 0xe1, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xde, 0xde, 0xde, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xb7, 0x7e, 0xd, 0xb6, 0x0, 0x0, 0x0, 0x32, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x8, 0x9, 0xa, 0xc, 0xd, 0xe, 0xf, 0x11, 0x12, 0x13, 0x2e, 0x2f, 0x32, 0x33, 0x36, 0x37, 0x38, 0x48, 0x49, 0x4b, 0x50, 0x53, 0x55, 0x56, 0x6c, 0x6d, 0x6e, 0x70, 0x77, 0x79, 0x7b, 0x7c, 0xc5, 0xd7, 0xd8, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe1, 0xe2, 0xe3, 0xf0, 0xf2, 0xf3, 0xf4, 0xfe, 0x5e, 0x62, 0x1a, 0x26, 0x0, 0x0, 0x0, 0x83, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x8d, 0xc8, 0x45, 0x42, 0x3, 0x1, 0x0, 0xc0, 0xc0, 0xd4, 0x8b, 0x43, 0x1d, 0x5f, 0x77, 0xcb, 0xff, 0x1f, 0x87, 0xd3, 0xf3, 0xe6, 0x36, 0x61, 0x5c, 0x93, 0xc7, 0x70, 0xe8, 0xc3, 0xa7, 0xe9, 0xbf, 0xaf, 0x62, 0x55, 0x4d, 0x6e, 0x7e, 0x7d, 0xdd, 0x5a, 0x1d, 0x16, 0x8b, 0x7d, 0x64, 0x77, 0xfb, 0xed, 0x69, 0x66, 0x75, 0x1, 0xb0, 0xae, 0x2c, 0x67, 0xc0, 0xab, 0x1e, 0xd8, 0x35, 0xf5, 0x96, 0xa3, 0xbe, 0x1, 0x89, 0x2e, 0xa8, 0xb5, 0x62, 0xa9, 0x9, 0x90, 0x9e, 0xc7, 0x4a, 0x53, 0x20, 0xd0, 0x3d, 0xdb, 0xba, 0xda, 0x70, 0xd2, 0x77, 0x60, 0xde, 0x18, 0xad, 0x1, 0x2e, 0x6b, 0xab, 0x39, 0xc0, 0xc3, 0x60, 0x75, 0x5c, 0x2e, 0x4f, 0xb5, 0xc3, 0x1d, 0x3f, 0xdd, 0x17, 0xaa, 0x9a, 0xff, 0x19, 0x66, 0x2f, 0x61, 0xdf, 0x87, 0xcf, 0x33, 0x46, 0xf5, 0x9, 0xdb, 0xf3, 0x10, 0xc5, 0x55, 0x93, 0xc, 0xea, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_zoom_less_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x13, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0x31, 0xe0, 0xc1, 0x7f, 0x3c, 0x90, 0xb0, 0x82, 0x11, 0x2, 0x0, 0xbf, 0x57, 0x36, 0x25, 0x52, 0x24, 0x7b, 0x26, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_zoom_more_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x3c, 0xf8, 0xf, 0x84, 0x43, 0x4a, 0x1, 0x42, 0x10, 0xf, 0x24, 0xa8, 0x60, 0x78, 0x84, 0x3, 0x61, 0x0, 0x0, 0xca, 0x3a, 0x6d, 0x8d, 0x50, 0x1e, 0x9a, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_zoom_reset_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0xa, 0x3c, 0xc, 0x7b, 0xf0, 0xff, 0xc1, 0x7f, 0x9c, 0x22, 0xcf, 0x44, 0x1e, 0xbc, 0x84, 0x72, 0xb1, 0x8b, 0x3c, 0x58, 0x5, 0xe4, 0x40, 0xb8, 0x38, 0x45, 0x18, 0x60, 0x5c, 0x84, 0x30, 0x59, 0xa, 0xa0, 0x80, 0x6e, 0xa, 0x86, 0x92, 0x2f, 0x8, 0x3, 0x0, 0x69, 0xc8, 0x86, 0x87, 0x72, 0xca, 0x85, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char indeterminate_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xfe, 0xfe, 0xfe, 0x98, 0x4d, 0x2d, 0x9a, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x77, 0x5e, 0x7f, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xc1, 0x12, 0x80, 0x20, 0x8, 0x45, 0xd1, 0x4, 0x14, 0xd, 0xc5, 0xfa, 0xff, 0x9f, 0x8d, 0x9c, 0x6c, 0x9a, 0xb4, 0xe9, 0x2e, 0xcf, 0x42, 0x9e, 0xcb, 0x32, 0xe4, 0x0, 0xc9, 0xb7, 0x8, 0xc1, 0x19, 0x40, 0x60, 0xc9, 0x2d, 0xe1, 0x0, 0x6, 0xc8, 0x45, 0x6b, 0x4b, 0xb, 0xa3, 0x1, 0x89, 0x6e, 0x57, 0x2a, 0x64, 0xe0, 0x73, 0xed, 0x50, 0xb3, 0x9f, 0xc3, 0x7e, 0xf7, 0x5, 0x7f, 0x6f, 0xc, 0x67, 0x9f, 0xc3, 0xe2, 0x39, 0xc, 0x52, 0xec, 0xd3, 0xd7, 0x4, 0xb3, 0xcf, 0xbd, 0x3a, 0x0, 0xa0, 0xa2, 0x8, 0xbc, 0xf6, 0x84, 0x3a, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char line_edit_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char line_edit_clear_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x90, 0x1, 0x6, 0xc0, 0x30, 0xc, 0x45, 0x77, 0x89, 0xd5, 0x76, 0xb3, 0x9e, 0x7b, 0x65, 0x63, 0xd, 0xf9, 0xbb, 0x48, 0x3b, 0xb3, 0x92, 0x54, 0x42, 0xb1, 0x5, 0x88, 0xf7, 0xc8, 0xcf, 0x9f, 0xfe, 0x1a, 0x8e, 0x14, 0xf4, 0x4e, 0x81, 0x63, 0x87, 0x51, 0x90, 0x28, 0x8, 0x46, 0x42, 0x51, 0x4a, 0x9e, 0x79, 0x43, 0xc5, 0x81, 0x55, 0x6f, 0xbc, 0x34, 0xdc, 0x2b, 0x2e, 0x16, 0xe5, 0x3a, 0xb1, 0xb, 0xb6, 0xca, 0x3, 0x2b, 0xb2, 0xc2, 0xbe, 0xf0, 0x66, 0x71, 0x4f, 0x70, 0x3b, 0x61, 0x14, 0x89, 0x26, 0x71, 0x5d, 0x6c, 0x9f, 0x1e, 0x17, 0x35, 0xae, 0xfa, 0xeb, 0xdc, 0x62, 0xc3, 0x84, 0x2d, 0x77, 0x22, 0xda, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char line_edit_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x94, 0xc8, 0x67, 0x6b, 0x60, 0xe6, 0x60, 0x64, 0x80, 0x80, 0xff, 0xc, 0x7f, 0x7f, 0xfc, 0x6a, 0x60, 0x94, 0xfb, 0xc0, 0xce, 0xcf, 0xc2, 0x80, 0x10, 0xfc, 0xc3, 0xf0, 0xf3, 0x23, 0xa3, 0xe2, 0x4f, 0xe, 0x36, 0x54, 0xc1, 0x1f, 0xbf, 0x18, 0x95, 0xbe, 0x73, 0x70, 0xb0, 0x30, 0xc0, 0x1, 0x48, 0xf0, 0x7, 0x85, 0x82, 0x58, 0x2d, 0xc2, 0xe6, 0xa4, 0x4f, 0x20, 0xc7, 0x37, 0x32, 0xb3, 0x23, 0x39, 0xfe, 0xfb, 0xaf, 0x46, 0x0, 0xee, 0x2a, 0x2f, 0xce, 0x4c, 0x47, 0x66, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char mini_checkerboard_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x98, 0xa0, 0xbd, 0x0, 0x0, 0x0, 0x17, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0x0, 0x5, 0xff, 0xa1, 0x60, 0xa0, 0x4, 0x60, 0xc, 0x98, 0xc4, 0x0, 0x9, 0x0, 0x0, 0x44, 0x81, 0xef, 0x81, 0xc1, 0x26, 0x8e, 0x8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_arrow_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x2, 0xdc, 0x4f, 0xb8, 0x9f, 0x80, 0x45, 0xf8, 0xa1, 0xf6, 0x83, 0x2f, 0xf, 0xbe, 0x3c, 0xd4, 0x46, 0x13, 0x7e, 0xc1, 0xfd, 0xe0, 0xea, 0x83, 0xff, 0x40, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xfa, 0xf9, 0x40, 0x41, 0x30, 0x7c, 0x38, 0x9f, 0x81, 0x12, 0x80, 0x69, 0x14, 0x61, 0xcb, 0x11, 0xce, 0xc5, 0xe5, 0x41, 0xc2, 0x0, 0x0, 0x95, 0x48, 0x37, 0x91, 0x1f, 0xec, 0x77, 0x5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3f, 0x3f, 0x5a, 0x5a, 0x5a, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x59, 0x59, 0x59, 0x2a, 0x2a, 0x30, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x27, 0x35, 0x35, 0x35, 0x4a, 0x4a, 0x4a, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x56, 0x56, 0x56, 0x62, 0x62, 0x62, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x48, 0x48, 0x48, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x54, 0x54, 0x54, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x49, 0x49, 0x49, 0x3a, 0x3a, 0x3a, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x39, 0x39, 0x39, 0x47, 0x47, 0x47, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x46, 0x46, 0x46, 0xd3, 0xa7, 0xd4, 0x88, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xec, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0x8e, 0x5, 0x4e, 0x0, 0x31, 0x10, 0x45, 0xff, 0xef, 0x56, 0xc2, 0xe2, 0xee, 0x4e, 0x3c, 0xc8, 0xd, 0xb8, 0x38, 0xa7, 0xc0, 0xdd, 0xdd, 0x5d, 0xbb, 0x94, 0x4c, 0xd2, 0xc1, 0xdf, 0x4a, 0x47, 0x5e, 0x27, 0xc3, 0xc, 0x80, 0x64, 0x24, 0xb8, 0xc7, 0x4f, 0x2c, 0x6b, 0xd5, 0x90, 0xff, 0xdd, 0x23, 0x7e, 0xc1, 0xa2, 0xb6, 0x64, 0xad, 0x26, 0x82, 0xc6, 0xef, 0x45, 0xbc, 0xe3, 0x1, 0x2c, 0x69, 0x9b, 0xc8, 0x7f, 0x5, 0x36, 0x1e, 0x41, 0x84, 0xd6, 0x4, 0x25, 0x90, 0xb7, 0x39, 0x6c, 0x4c, 0x2f, 0xbe, 0x68, 0xdf, 0x13, 0xa1, 0x9e, 0xc8, 0xa4, 0xb7, 0xe7, 0x47, 0x93, 0x63, 0x1b, 0xf9, 0xdc, 0x8, 0x88, 0x60, 0xbf, 0x4, 0xff, 0xd2, 0x56, 0x93, 0xe3, 0xb7, 0xb2, 0xf6, 0x2c, 0x36, 0x1, 0x16, 0xf4, 0x5f, 0x42, 0xc4, 0x17, 0x8f, 0xb5, 0xc0, 0xa5, 0x8, 0x30, 0x5f, 0xc2, 0x5d, 0xcf, 0xc9, 0xd, 0x74, 0x87, 0x8b, 0x5e, 0x56, 0x22, 0x24, 0xf7, 0x6d, 0xc2, 0xd1, 0x80, 0x26, 0x27, 0x5d, 0x5b, 0x67, 0x7d, 0x2f, 0x80, 0x4d, 0xc9, 0x7f, 0x9, 0x63, 0xa7, 0x61, 0x23, 0xc7, 0x74, 0x3d, 0xf, 0xae, 0x41, 0x84, 0x6f, 0x13, 0xe6, 0x26, 0xfa, 0x36, 0x46, 0x73, 0xbc, 0xba, 0x3b, 0xd6, 0xca, 0x8, 0xd0, 0xd6, 0x36, 0x4e, 0x35, 0xeb, 0xad, 0xd7, 0xb0, 0x60, 0x72, 0xdc, 0xda, 0x9c, 0x2, 0x67, 0x76, 0x64, 0x82, 0x99, 0x9b, 0xb6, 0x80, 0xb0, 0x7e, 0x8d, 0xf6, 0x5a, 0xdd, 0xe1, 0xb5, 0xba, 0xba, 0xb1, 0x0, 0xcd, 0xc7, 0x50, 0x23, 0xeb, 0xfb, 0x7f, 0xb4, 0xc8, 0x22, 0x18, 0xdd, 0x0, 0xd5, 0xec, 0x4e, 0x53, 0xc6, 0x18, 0x44, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_disabled_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5a, 0x5a, 0x5a, 0x3e, 0x3e, 0x3e, 0x2a, 0x2a, 0x30, 0x59, 0x59, 0x59, 0x22, 0x22, 0x27, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x4a, 0x4a, 0x4a, 0x36, 0x36, 0x36, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x62, 0x62, 0x62, 0x56, 0x56, 0x56, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x54, 0x54, 0x54, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x48, 0x48, 0x48, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x3a, 0x3a, 0x3a, 0x49, 0x49, 0x49, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x47, 0x47, 0x47, 0x39, 0x39, 0x39, 0x46, 0x46, 0x46, 0xda, 0x9d, 0x96, 0x5c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0xf, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0xef, 0x50, 0x45, 0x4b, 0x6d, 0xe, 0x95, 0xa6, 0xa5, 0x40, 0xab, 0x15, 0x5, 0xdc, 0xc0, 0x8a, 0xa, 0xb8, 0xa1, 0xef, 0xff, 0xc, 0x86, 0x90, 0x30, 0xc, 0x17, 0xfa, 0x5d, 0x26, 0xff, 0x4c, 0x4e, 0x12, 0x41, 0x98, 0x32, 0x33, 0x1b, 0x98, 0x9b, 0x17, 0x83, 0x84, 0x18, 0xa, 0x47, 0xa2, 0xb1, 0xe9, 0x7d, 0x21, 0x16, 0x58, 0x58, 0x5c, 0x5a, 0x96, 0x24, 0x29, 0x9e, 0x48, 0xa6, 0xd2, 0x2b, 0x51, 0xb2, 0xb4, 0xba, 0x96, 0xc9, 0xca, 0x87, 0x47, 0x8c, 0x9c, 0xc9, 0x1d, 0x9f, 0x30, 0xeb, 0x1b, 0xe9, 0x8, 0x9, 0x36, 0x15, 0x25, 0xaf, 0x9e, 0x6a, 0x8c, 0x8a, 0xa0, 0xa0, 0x8f, 0x9c, 0x15, 0xb7, 0x52, 0x61, 0x12, 0xa8, 0x6, 0x56, 0x4d, 0x2b, 0xcb, 0x98, 0xb8, 0xc4, 0x3, 0x5d, 0x2f, 0x24, 0x43, 0xc3, 0xc0, 0x6, 0xcd, 0x2a, 0xcb, 0x4c, 0xe, 0x4a, 0x95, 0x2a, 0x57, 0x49, 0x88, 0x24, 0x70, 0xc, 0x70, 0xcf, 0xcb, 0x17, 0x8c, 0x5, 0x8e, 0x77, 0xc9, 0x79, 0xf1, 0x20, 0x9, 0x80, 0x4, 0xd6, 0x64, 0x50, 0xbb, 0xe2, 0x6a, 0xd2, 0x30, 0xc0, 0xd7, 0xf5, 0x89, 0x19, 0xb4, 0x46, 0xbe, 0x79, 0xc3, 0x35, 0x69, 0x80, 0x6e, 0x15, 0xb8, 0x33, 0xef, 0x99, 0x7, 0x84, 0x5a, 0x6d, 0xae, 0x45, 0x8f, 0xb0, 0x1f, 0xed, 0x86, 0x3f, 0xbe, 0xa6, 0x6f, 0x3f, 0x75, 0x9e, 0xb9, 0xe, 0x1d, 0xd2, 0x78, 0x79, 0xed, 0x62, 0xf0, 0x19, 0xdc, 0xeb, 0x17, 0xdf, 0xb8, 0x77, 0x7a, 0xcd, 0xed, 0x8f, 0xcf, 0x5e, 0xb7, 0x8e, 0x19, 0xe5, 0xab, 0x3f, 0xf8, 0x66, 0xda, 0x3b, 0xf4, 0xa1, 0x76, 0xf7, 0x90, 0xe3, 0x8e, 0x67, 0x70, 0x1, 0xbc, 0x9f, 0x91, 0xc1, 0xfe, 0x1, 0x7d, 0xea, 0x7f, 0x3f, 0xeb, 0xef, 0xef, 0xfe, 0x5, 0xd6, 0xe3, 0x56, 0x89, 0xd8, 0x62, 0xb6, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_hover_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x5f, 0x5a, 0x6b, 0x2a, 0x2a, 0x30, 0x56, 0x53, 0x64, 0x22, 0x22, 0x27, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x67, 0x63, 0x76, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5a, 0x56, 0x65, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x5b, 0x57, 0x66, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x43, 0x40, 0x4c, 0x54, 0x50, 0x5f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x4f, 0x5f, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x5f, 0x5a, 0x6c, 0xd3, 0x26, 0x54, 0x35, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe5, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x91, 0x43, 0x62, 0xc5, 0x60, 0x18, 0x45, 0xef, 0x8d, 0x51, 0xdb, 0xee, 0x46, 0xca, 0x6d, 0x77, 0x58, 0xce, 0x6b, 0xdb, 0x7c, 0x8a, 0xad, 0xea, 0x44, 0x1f, 0x4e, 0x92, 0x1f, 0x4c, 0x0, 0xe0, 0x9, 0x61, 0xf0, 0x89, 0x32, 0x12, 0xcd, 0xd4, 0x8, 0xef, 0x1f, 0x35, 0x54, 0xa0, 0x68, 0x1a, 0x34, 0x89, 0x8, 0x86, 0xa4, 0xd, 0x57, 0xb4, 0xdf, 0x79, 0x5, 0x89, 0x94, 0xba, 0xf9, 0xb3, 0xc0, 0xce, 0xeb, 0xf0, 0x17, 0x1c, 0xab, 0x11, 0x9, 0x1a, 0xf9, 0x96, 0x84, 0x5d, 0x5e, 0x43, 0x15, 0x7, 0x2e, 0x42, 0x41, 0x51, 0xd3, 0xbe, 0x67, 0xd5, 0x6b, 0x42, 0x3a, 0x38, 0x9b, 0xf5, 0x2e, 0x20, 0xfa, 0x5, 0x33, 0x41, 0x69, 0xf4, 0xeb, 0x49, 0x6c, 0x19, 0xe6, 0xbd, 0xdd, 0xd, 0x48, 0xa0, 0x92, 0xb, 0x36, 0x72, 0x6a, 0x26, 0xf0, 0x14, 0xa, 0x10, 0x72, 0xe1, 0x7d, 0xf4, 0xf6, 0x35, 0x1b, 0xc3, 0xe3, 0x18, 0x9d, 0x50, 0xf0, 0xe4, 0xc2, 0x17, 0xae, 0x27, 0xd3, 0xe4, 0x6e, 0xe8, 0xf8, 0x7e, 0xbc, 0x1, 0x48, 0x9e, 0x57, 0xf8, 0xc5, 0xfc, 0xbd, 0x7a, 0x98, 0xc4, 0x94, 0x47, 0xbf, 0xe4, 0xce, 0x48, 0x8, 0x6e, 0x69, 0x91, 0x63, 0x47, 0x73, 0x49, 0xbc, 0x77, 0x3e, 0xd7, 0x4b, 0x3b, 0x12, 0x36, 0x16, 0xb3, 0x85, 0x6a, 0x68, 0xce, 0x39, 0x62, 0xc6, 0xba, 0x3d, 0x8d, 0xe7, 0x91, 0x20, 0xac, 0xad, 0xa6, 0xc2, 0xe, 0x6, 0xcc, 0x74, 0xc, 0x2d, 0xe7, 0xf9, 0x55, 0x2, 0x28, 0x94, 0x37, 0xab, 0xee, 0xa1, 0xcc, 0xbf, 0xdb, 0xed, 0x3, 0x70, 0xe6, 0x4f, 0x4a, 0xc3, 0xed, 0xed, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_hover_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5f, 0x5a, 0x6c, 0x42, 0x40, 0x4b, 0x2a, 0x2a, 0x30, 0x5f, 0x5a, 0x6b, 0x22, 0x22, 0x27, 0x56, 0x53, 0x64, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x57, 0x53, 0x63, 0x3e, 0x3c, 0x47, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x67, 0x63, 0x76, 0x5a, 0x56, 0x67, 0x5b, 0x57, 0x68, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x5a, 0x56, 0x65, 0x48, 0x45, 0x51, 0x49, 0x46, 0x52, 0x4d, 0x4a, 0x57, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x5b, 0x57, 0x66, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x54, 0x50, 0x5f, 0x43, 0x40, 0x4c, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x53, 0x4f, 0x5f, 0x42, 0x3f, 0x4c, 0x43, 0x3f, 0x4d, 0x47, 0x43, 0x51, 0x5f, 0x5a, 0x6c, 0x53, 0x4f, 0x5e, 0x53, 0x50, 0x5f, 0x68, 0x6, 0xa3, 0x65, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x15, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0x8f, 0x60, 0xd3, 0x62, 0xc1, 0x56, 0x11, 0x5, 0xdc, 0xd0, 0x5a, 0x14, 0x70, 0x5f, 0xdf, 0xff, 0x1, 0xc, 0x69, 0xc2, 0x30, 0x5c, 0xe8, 0x77, 0x99, 0xfc, 0x33, 0x39, 0x49, 0x38, 0x6e, 0xc8, 0xc8, 0xa8, 0x6f, 0x6c, 0x9c, 0xf7, 0x63, 0x7c, 0x20, 0x18, 0xa, 0x47, 0x86, 0xf7, 0xb9, 0x88, 0x6f, 0x62, 0x72, 0x6a, 0x5a, 0x10, 0x84, 0x68, 0x2c, 0x9e, 0x48, 0xce, 0x84, 0xf1, 0xd2, 0xec, 0x5c, 0x3a, 0x23, 0x6e, 0x6c, 0x52, 0x62, 0x3a, 0xbb, 0xb5, 0xed, 0xd9, 0x99, 0x5f, 0x48, 0x86, 0x70, 0xb0, 0x28, 0x49, 0x39, 0x79, 0x57, 0xa1, 0x64, 0x15, 0xf6, 0xf2, 0x9e, 0xc2, 0xfe, 0x52, 0x22, 0x88, 0x3, 0x59, 0x43, 0xb2, 0x6e, 0x64, 0x28, 0x1d, 0x15, 0x59, 0x90, 0x2f, 0x1c, 0xc4, 0x3, 0xbd, 0xc0, 0x4, 0xc5, 0x28, 0x89, 0x54, 0x16, 0x8a, 0xe5, 0x43, 0xa6, 0x1c, 0xe3, 0x71, 0x60, 0x69, 0x60, 0x1f, 0x95, 0x8e, 0x29, 0x3, 0xac, 0xca, 0x9, 0x53, 0x89, 0xfa, 0x71, 0x0, 0x38, 0x30, 0x6, 0x83, 0xea, 0x29, 0x53, 0x15, 0x7a, 0x1, 0x3a, 0xab, 0xd, 0xcc, 0xa0, 0xd4, 0x73, 0x8d, 0x73, 0xa6, 0x41, 0x2, 0xf5, 0x42, 0x82, 0x4b, 0xfd, 0x8a, 0xba, 0x56, 0xd5, 0xe6, 0xd, 0xd3, 0x24, 0x47, 0x98, 0xb7, 0x66, 0xdd, 0xe9, 0x5f, 0xd3, 0x31, 0xef, 0xdc, 0x16, 0xe3, 0x92, 0x21, 0xb5, 0xfb, 0x87, 0x36, 0x2, 0x87, 0x42, 0x9d, 0xee, 0xe3, 0x13, 0xd5, 0x72, 0xc9, 0x35, 0x97, 0x9f, 0x5f, 0x3a, 0xed, 0x1a, 0xa2, 0xa4, 0xd7, 0xee, 0xdb, 0xbb, 0xe7, 0xe3, 0x33, 0x45, 0x1e, 0x6a, 0x65, 0x55, 0xb5, 0xec, 0xfe, 0xc, 0x36, 0xc0, 0xd7, 0xb7, 0xe7, 0x67, 0x6d, 0x9d, 0x3c, 0xf5, 0xbf, 0x9f, 0xf5, 0xf7, 0x77, 0xff, 0x2, 0xa7, 0xc5, 0x58, 0xc8, 0x6e, 0x59, 0x8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_normal_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x56, 0x52, 0x60, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x52, 0x22, 0x22, 0x27, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x5d, 0x5a, 0x6a, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x46, 0x42, 0x4f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x4e, 0x4b, 0x58, 0x8, 0xd9, 0x10, 0xcb, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd1, 0x55, 0x5a, 0xc4, 0x30, 0x14, 0x5, 0xe0, 0x73, 0xea, 0x82, 0xbb, 0x3b, 0x6c, 0x80, 0x45, 0xb0, 0x70, 0x5e, 0xb1, 0x37, 0xdc, 0x75, 0xdc, 0x3d, 0x43, 0x2a, 0x5f, 0x3a, 0x7e, 0x6a, 0xc9, 0xbd, 0x7f, 0x9d, 0x2a, 0x0, 0xa4, 0x16, 0xd, 0xaa, 0x18, 0x8e, 0x41, 0x3f, 0x11, 0xd1, 0xbe, 0x52, 0xc7, 0x48, 0xa8, 0xfb, 0x5e, 0x68, 0xd4, 0x24, 0x96, 0x6a, 0xfc, 0xaf, 0x8b, 0x32, 0xbf, 0x61, 0x90, 0xc6, 0x3c, 0x27, 0x3, 0xce, 0xfe, 0x20, 0x2, 0x4b, 0xd2, 0x45, 0x1f, 0x14, 0xd5, 0x78, 0x5e, 0x36, 0x1c, 0x7d, 0xe5, 0x33, 0x2, 0xb3, 0x84, 0x8a, 0xec, 0xd4, 0xeb, 0x9a, 0x1a, 0x1b, 0x82, 0xf5, 0x79, 0x20, 0x2, 0x46, 0x1f, 0x58, 0x8d, 0x95, 0xe4, 0x6a, 0x1d, 0xcf, 0x4f, 0x89, 0x85, 0x10, 0x80, 0x56, 0x1f, 0x8, 0xf4, 0x53, 0xf7, 0x81, 0x6c, 0x4, 0xa0, 0xf5, 0x41, 0x69, 0xeb, 0xb7, 0xd0, 0x7b, 0x86, 0xcc, 0x36, 0xbb, 0x11, 0x90, 0x66, 0x1f, 0x88, 0xef, 0xbd, 0x64, 0x92, 0x5a, 0x7b, 0x4e, 0xed, 0x34, 0x42, 0x20, 0xa5, 0xd5, 0x7, 0x27, 0x69, 0xfb, 0x51, 0x8d, 0x69, 0x6e, 0xd5, 0xcc, 0xb9, 0x18, 0xf4, 0xaf, 0x40, 0x6e, 0x3f, 0x1d, 0xab, 0xf1, 0xdd, 0xc7, 0xf1, 0x12, 0x45, 0xc, 0xce, 0x4f, 0x17, 0x12, 0xd0, 0xb6, 0xc4, 0x87, 0x1a, 0x6f, 0x2f, 0x48, 0x8b, 0xef, 0x31, 0xd0, 0x6e, 0xce, 0xa8, 0xc0, 0x25, 0x56, 0x7d, 0x5, 0x52, 0xed, 0x6e, 0xae, 0x68, 0x0, 0xd4, 0x86, 0x7f, 0x56, 0x43, 0x62, 0x38, 0xc, 0x46, 0x28, 0xba, 0x1, 0x7a, 0xad, 0x4f, 0x59, 0x90, 0xab, 0xbf, 0xa4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_normal_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x56, 0x53, 0x61, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x56, 0x52, 0x60, 0x22, 0x22, 0x27, 0x47, 0x44, 0x52, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x47, 0x44, 0x50, 0x33, 0x31, 0x3a, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x5d, 0x5a, 0x6a, 0x51, 0x4f, 0x5d, 0x52, 0x50, 0x5d, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x51, 0x4e, 0x5b, 0x41, 0x3e, 0x49, 0x42, 0x3e, 0x4a, 0x46, 0x42, 0x4e, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x47, 0x43, 0x50, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x4e, 0x4b, 0x58, 0x44, 0x41, 0x4c, 0x44, 0x42, 0x4d, 0x7d, 0x2e, 0xcf, 0xc5, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x13, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0xf, 0x68, 0xd3, 0xa2, 0x5, 0x5b, 0x40, 0x14, 0x70, 0x3, 0x2b, 0x2a, 0xe0, 0xbe, 0xbd, 0xff, 0x3, 0x18, 0xd2, 0x84, 0x61, 0xb8, 0xd0, 0xef, 0x32, 0xf9, 0x67, 0x72, 0x92, 0x70, 0xdc, 0x88, 0xb1, 0x71, 0xcf, 0xc4, 0x24, 0xef, 0xc5, 0x78, 0x9f, 0x3f, 0x10, 0xc, 0x8d, 0xee, 0x73, 0x21, 0xcf, 0xd4, 0xf4, 0xcc, 0xac, 0x20, 0x8, 0xe1, 0x48, 0x34, 0x16, 0x9f, 0xb, 0xe2, 0xa5, 0xf9, 0x85, 0x64, 0x4a, 0xdc, 0xda, 0xa6, 0xc4, 0x64, 0x7a, 0x67, 0xd7, 0xb5, 0xb7, 0xb8, 0x14, 0xf, 0xe0, 0x60, 0x59, 0x92, 0x32, 0xf2, 0xbe, 0x42, 0xc9, 0x2a, 0x64, 0x73, 0xae, 0x83, 0xc3, 0x95, 0x98, 0x1f, 0x7, 0xb2, 0x86, 0x64, 0xdd, 0x48, 0x51, 0x3a, 0xca, 0x1f, 0x1d, 0x53, 0xb9, 0x6c, 0xd4, 0xd7, 0xf, 0x4c, 0x50, 0x8c, 0x82, 0x48, 0xa5, 0x21, 0x5f, 0x3c, 0x61, 0x8a, 0x11, 0x1e, 0x7, 0x96, 0x6, 0x76, 0xa9, 0x50, 0xa6, 0xc, 0xb0, 0x2a, 0xa7, 0x4c, 0x25, 0xec, 0xc5, 0x1, 0xe0, 0xc0, 0x18, 0xe, 0xaa, 0x67, 0x4c, 0x55, 0xe8, 0x7, 0xe8, 0xbc, 0x36, 0x34, 0x83, 0x52, 0xcf, 0x34, 0x2e, 0x98, 0x6, 0x9, 0xd4, 0x4b, 0x9, 0xae, 0xf4, 0x6b, 0xea, 0x46, 0x55, 0x9b, 0x2d, 0xa6, 0x49, 0x8e, 0x30, 0x6f, 0xcd, 0xba, 0x33, 0xb8, 0xa6, 0x63, 0xde, 0xb5, 0xef, 0x99, 0x36, 0x19, 0x52, 0x7b, 0x78, 0xec, 0x20, 0x70, 0x28, 0xd4, 0xed, 0x3d, 0x3d, 0x33, 0x2f, 0xe4, 0x9a, 0xab, 0xaf, 0x6f, 0xdd, 0x4e, 0xd, 0x51, 0xd2, 0x7b, 0xef, 0xe3, 0x93, 0x6a, 0x25, 0xc8, 0x43, 0xad, 0xad, 0xab, 0x96, 0x3d, 0x98, 0xc1, 0x6, 0xf8, 0xfa, 0x76, 0xfd, 0x6c, 0x6c, 0x92, 0xa7, 0xfe, 0xf7, 0xb3, 0xfe, 0xfe, 0xee, 0x5f, 0xa1, 0x5f, 0x59, 0xbd, 0x75, 0x41, 0x2b, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_pressed_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x2f, 0x37, 0x46, 0x43, 0x4f, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x44, 0x50, 0x2a, 0x2a, 0x30, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x27, 0x3d, 0x3a, 0x45, 0x56, 0x52, 0x60, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x43, 0x40, 0x4c, 0x42, 0x40, 0x4b, 0x4c, 0x49, 0x56, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x3a, 0x38, 0x41, 0x36, 0x34, 0x3d, 0x44, 0x41, 0x4c, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x44, 0x42, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x50, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x3f, 0x3d, 0x47, 0x4f, 0x4c, 0x59, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x45, 0x42, 0x4d, 0x41, 0x3e, 0x49, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x52, 0x4e, 0x5c, 0x51, 0x4e, 0x5b, 0x5d, 0x59, 0x69, 0x10, 0x9d, 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xcf, 0x3, 0x62, 0x4, 0x51, 0x10, 0x4, 0xd0, 0xaa, 0x31, 0x62, 0xdb, 0xb8, 0x49, 0x2e, 0x9e, 0x3b, 0xc4, 0xb6, 0x9d, 0xc5, 0x58, 0x1f, 0xc1, 0xd6, 0xe8, 0x77, 0xf7, 0x1b, 0x59, 0x6c, 0x2, 0x20, 0x37, 0xaa, 0xc5, 0x17, 0x7e, 0xc7, 0x62, 0x28, 0x45, 0x75, 0xfe, 0x6c, 0xe1, 0x4f, 0x68, 0x86, 0x41, 0x69, 0x44, 0x51, 0x4b, 0xb1, 0xce, 0xcc, 0xe4, 0x83, 0xd7, 0xb0, 0x48, 0x6b, 0x98, 0xe8, 0x9, 0x38, 0x70, 0x8b, 0xa, 0xcc, 0x12, 0x1a, 0xf0, 0x4d, 0xac, 0x87, 0xf3, 0x96, 0x6f, 0x8e, 0x5f, 0x56, 0xc0, 0x53, 0x20, 0x8f, 0xbf, 0xdb, 0x86, 0x58, 0x5b, 0x9, 0xbf, 0x47, 0x80, 0xa, 0x58, 0x1a, 0x38, 0xad, 0x9, 0x5f, 0xac, 0xe3, 0x20, 0xbc, 0x4b, 0x46, 0x4b, 0x0, 0x3a, 0x1a, 0x24, 0xd0, 0x69, 0x85, 0xc0, 0x63, 0x5, 0x60, 0x68, 0xf0, 0x36, 0x7f, 0xf3, 0xaa, 0xbe, 0xe1, 0x61, 0x81, 0x69, 0x5, 0x72, 0x5b, 0x83, 0xe4, 0x6a, 0x59, 0x16, 0xf7, 0x53, 0x47, 0x77, 0x8b, 0xad, 0x12, 0xe4, 0xb9, 0xa3, 0xc1, 0xe6, 0x83, 0x7b, 0x20, 0xd6, 0xb4, 0xe7, 0xbf, 0xed, 0xe1, 0x1a, 0xd8, 0xfa, 0xdf, 0xb9, 0x70, 0xb8, 0x21, 0xd6, 0xbb, 0x17, 0x1b, 0xe3, 0x4c, 0x6a, 0xb0, 0xbd, 0x25, 0x5, 0x3b, 0x5e, 0x7c, 0x21, 0xc0, 0xc2, 0x68, 0xee, 0xf1, 0xbc, 0x6, 0x46, 0xb1, 0xbd, 0x5e, 0x30, 0x5, 0x27, 0x19, 0x24, 0xb8, 0x61, 0x6e, 0xf8, 0xf5, 0xf7, 0xcd, 0x47, 0x16, 0xa0, 0x18, 0x13, 0x6a, 0x64, 0x7d, 0xff, 0x8f, 0x1e, 0x59, 0x84, 0xa2, 0x1b, 0x0, 0xe5, 0xe0, 0x4e, 0x46, 0x1d, 0x98, 0x92, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_pressed_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x46, 0x43, 0x4f, 0x31, 0x2f, 0x38, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x50, 0x22, 0x22, 0x27, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x56, 0x52, 0x60, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x4c, 0x49, 0x56, 0x42, 0x40, 0x4b, 0x43, 0x40, 0x4c, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x44, 0x41, 0x4c, 0x36, 0x34, 0x3d, 0x3a, 0x38, 0x41, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4e, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x3f, 0x3d, 0x48, 0x50, 0x4d, 0x5a, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x41, 0x3e, 0x49, 0x45, 0x42, 0x4d, 0x5d, 0x59, 0x69, 0x51, 0x4e, 0x5b, 0x52, 0x4e, 0x5c, 0x49, 0x7e, 0x80, 0x9, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x14, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x40, 0x3, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x40, 0xc0, 0xc6, 0xc1, 0xc9, 0xc5, 0xcd, 0x83, 0x2e, 0xcf, 0xc0, 0xc3, 0xcc, 0xcb, 0xc7, 0x2f, 0x20, 0x28, 0x28, 0x28, 0x24, 0x2c, 0x22, 0x2a, 0x26, 0xce, 0xd, 0x14, 0x92, 0x90, 0x54, 0x51, 0x55, 0x53, 0xd7, 0x80, 0x2, 0x35, 0x15, 0x4d, 0x2d, 0x6d, 0x8, 0xd0, 0x91, 0x92, 0x16, 0xe3, 0x2, 0x2a, 0x90, 0xd1, 0xd5, 0xd5, 0xd3, 0x37, 0x30, 0x84, 0x2, 0x7d, 0x23, 0x63, 0x13, 0x53, 0x28, 0x30, 0x93, 0x15, 0xe5, 0x4, 0x2a, 0xd0, 0x37, 0xb7, 0xd0, 0xb7, 0xb4, 0x52, 0x85, 0x2, 0x4b, 0xb, 0x6b, 0x1b, 0x5b, 0x18, 0xb0, 0x13, 0xe1, 0x0, 0x29, 0xb0, 0x37, 0x36, 0xb4, 0x72, 0x50, 0x83, 0x2, 0x4d, 0x63, 0x6b, 0x47, 0x27, 0x18, 0x70, 0x14, 0x66, 0x3, 0x2a, 0x70, 0x36, 0x37, 0x76, 0x71, 0x75, 0x70, 0x83, 0x2, 0x2b, 0x63, 0x67, 0x77, 0xf, 0x18, 0x70, 0x17, 0x62, 0x7, 0x2a, 0x30, 0x6, 0x2a, 0xb0, 0x42, 0x56, 0xe0, 0xe9, 0x5, 0x3, 0x9e, 0x82, 0x20, 0x5, 0x16, 0xde, 0x3e, 0x48, 0x6e, 0x30, 0xf4, 0xd5, 0xf3, 0xf3, 0x87, 0x1, 0x3f, 0xb0, 0x2, 0xa3, 0x0, 0x5d, 0xe3, 0x40, 0xcb, 0x20, 0x28, 0x8, 0x36, 0x32, 0xa, 0x9, 0x85, 0x81, 0x10, 0xb0, 0x15, 0xf6, 0x61, 0xf6, 0xbe, 0xe1, 0x70, 0x6f, 0x86, 0xdb, 0x47, 0x44, 0x46, 0xc1, 0x40, 0x24, 0xd8, 0x91, 0xe6, 0xd1, 0x31, 0xb1, 0x16, 0xc6, 0xe1, 0x50, 0x60, 0x11, 0x17, 0x9f, 0x90, 0x8, 0x5, 0x49, 0xc9, 0x60, 0x6f, 0xca, 0xa5, 0xa4, 0xc6, 0xc5, 0xfa, 0x58, 0x40, 0x81, 0x6e, 0x5a, 0x7c, 0x7a, 0x6, 0x4, 0x64, 0x66, 0xc9, 0x83, 0x3, 0x4a, 0x41, 0xd1, 0xc8, 0xd9, 0x5, 0xee, 0x6, 0x17, 0x63, 0xe3, 0xec, 0x1c, 0x8, 0xc8, 0x55, 0x52, 0x6, 0x7, 0x35, 0xc1, 0xc8, 0xc2, 0x1f, 0xdd, 0x0, 0xa5, 0xe, 0x59, 0xe5, 0x7f, 0xe9, 0xa4, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char overbright_indicator_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0xcd, 0x4b, 0xe, 0x80, 0x20, 0xc, 0x45, 0xd1, 0x9b, 0xea, 0xbe, 0x40, 0x97, 0x87, 0x8b, 0xae, 0x13, 0x34, 0x88, 0x1f, 0xa, 0x9d, 0xf8, 0x92, 0xe, 0xcf, 0x2d, 0x80, 0xda, 0x4f, 0x14, 0x26, 0x5, 0x49, 0x14, 0x53, 0xcb, 0x42, 0x58, 0xe, 0xbc, 0x51, 0xcd, 0x85, 0x9b, 0x81, 0x16, 0xfe, 0xc, 0x58, 0xf0, 0x6b, 0xc0, 0x8a, 0x1f, 0x3, 0x3d, 0xf8, 0x16, 0xe8, 0xc5, 0x97, 0x40, 0x81, 0x53, 0x9b, 0x55, 0x81, 0x91, 0xcf, 0x67, 0x20, 0xc6, 0x75, 0xe8, 0x73, 0x9e, 0xc, 0x7f, 0xce, 0x73, 0x61, 0x80, 0xd9, 0x83, 0x7f, 0xb0, 0x1d, 0xec, 0x30, 0xf0, 0x78, 0x5a, 0x7, 0xa8, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char panel_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x25, 0x2a, 0x35, 0x32, 0x3b, 0x4a, 0x73, 0x58, 0x4a, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char picker_cursor_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0xb9, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x6d, 0x8f, 0x3d, 0x8a, 0xc2, 0x50, 0x18, 0x45, 0xcf, 0x6b, 0x92, 0x2a, 0x19, 0xd4, 0xa4, 0x72, 0x47, 0x3, 0x42, 0xc0, 0x9f, 0x55, 0x44, 0x17, 0x24, 0x88, 0xee, 0x24, 0x53, 0x4d, 0x7e, 0xa, 0xbf, 0x94, 0xd6, 0x71, 0x5, 0xf2, 0x5e, 0x7f, 0x2d, 0xa2, 0xa2, 0xe0, 0x29, 0xef, 0xb9, 0xcd, 0x1, 0x40, 0xb1, 0x76, 0x6a, 0x14, 0x14, 0xd4, 0x68, 0xab, 0x98, 0x11, 0xcd, 0xd5, 0xef, 0x9b, 0xac, 0x27, 0x10, 0x32, 0x3b, 0xb4, 0x32, 0xcd, 0xc7, 0x77, 0xff, 0xfb, 0xc7, 0xc0, 0x92, 0x84, 0x84, 0x82, 0xcb, 0xa2, 0x92, 0x29, 0x46, 0xbb, 0x7d, 0xc3, 0xc0, 0x94, 0x27, 0x13, 0x86, 0x63, 0xa7, 0x12, 0xb5, 0x59, 0xcf, 0x8a, 0x77, 0xd6, 0xb9, 0xa9, 0x46, 0xde, 0x5, 0x92, 0xf, 0x91, 0x3a, 0x2f, 0xff, 0x4d, 0xfc, 0x38, 0xaf, 0x1b, 0x6a, 0x33, 0xa3, 0xf8, 0x10, 0x9b, 0xfc, 0xac, 0x1a, 0x6d, 0xf, 0x2d, 0x17, 0x26, 0xaf, 0x79, 0xc6, 0xf5, 0xd4, 0xa9, 0x44, 0xb1, 0x6c, 0x51, 0x31, 0xb0, 0x26, 0x25, 0x65, 0xc3, 0xb5, 0xa8, 0x64, 0x8a, 0xc6, 0x40, 0x3b, 0x76, 0xb9, 0xb9, 0xe0, 0x42, 0x7e, 0x3e, 0x75, 0x8f, 0x40, 0x0, 0x45, 0x2a, 0x55, 0xcb, 0xcb, 0xeb, 0x5f, 0xa5, 0x22, 0x80, 0x3b, 0xa0, 0x2c, 0x6c, 0xa1, 0x40, 0x2f, 0xda, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char popup_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xa2, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3b, 0x3b, 0x43, 0x42, 0x42, 0x4b, 0x3e, 0x3e, 0x47, 0x3e, 0x3e, 0x46, 0x41, 0x41, 0x4a, 0x0, 0x0, 0x0, 0x3d, 0x3d, 0x45, 0x3b, 0x3b, 0x43, 0x3a, 0x3a, 0x42, 0x38, 0x38, 0x41, 0x37, 0x37, 0x3e, 0x36, 0x36, 0x3d, 0x35, 0x35, 0x3c, 0x0, 0x0, 0x0, 0x38, 0x38, 0x40, 0x38, 0x38, 0x40, 0x31, 0x31, 0x38, 0x34, 0x34, 0x3b, 0x34, 0x34, 0x3b, 0x39, 0x39, 0x3f, 0x31, 0x31, 0x38, 0x2f, 0x2f, 0x36, 0x2d, 0x2d, 0x33, 0x2c, 0x2c, 0x32, 0x2b, 0x2b, 0x31, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x29, 0x29, 0x30, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x28, 0x28, 0x2d, 0x27, 0x27, 0x2d, 0x27, 0x27, 0x2c, 0x29, 0x29, 0x2e, 0x26, 0x26, 0x2c, 0x36, 0xc6, 0xc8, 0x93, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0x4, 0x13, 0x19, 0x1f, 0x22, 0x23, 0x16, 0x27, 0x35, 0x3f, 0x45, 0x46, 0x94, 0xf5, 0xfa, 0xfb, 0xf5, 0x40, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0x1a, 0xf5, 0xf6, 0x95, 0xfa, 0xfb, 0xf4, 0x94, 0x71, 0xda, 0xac, 0x92, 0x0, 0x0, 0x0, 0x7f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0x8f, 0x35, 0x82, 0xc3, 0x0, 0xc, 0x4, 0x77, 0x24, 0x85, 0xba, 0xe3, 0xff, 0xff, 0xee, 0xca, 0x74, 0x41, 0xdb, 0x32, 0xf3, 0x94, 0x82, 0x85, 0x10, 0x1d, 0x92, 0xb2, 0x3, 0x8e, 0x95, 0x77, 0x93, 0x6c, 0x28, 0xed, 0x15, 0x54, 0x67, 0xa6, 0x41, 0x3e, 0x8, 0x9c, 0xc3, 0xf4, 0xf2, 0xf6, 0x2a, 0x80, 0xf8, 0x44, 0x2d, 0x79, 0x2d, 0x20, 0xe0, 0x2, 0xa8, 0xc3, 0x2e, 0x6f, 0xc, 0x9e, 0x4c, 0x3c, 0x21, 0x4, 0xd8, 0xf0, 0x2, 0x28, 0x24, 0xcd, 0x3, 0xa9, 0x19, 0x64, 0xce, 0x83, 0x4c, 0x45, 0xe6, 0x69, 0x1a, 0xd8, 0xe9, 0x99, 0x96, 0x7f, 0x77, 0x37, 0x59, 0x83, 0xcc, 0xef, 0x7f, 0x89, 0x1f, 0x8e, 0xbf, 0x95, 0xd3, 0x1d, 0xf0, 0xff, 0x7a, 0x63, 0x7e, 0x86, 0xcb, 0x73, 0x8c, 0x5e, 0xee, 0xca, 0xb1, 0xad, 0x5f, 0x3, 0xaf, 0xdb, 0x49, 0x94, 0x4b, 0x90, 0x40, 0xdf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char popup_bg_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x78, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x0, 0xff, 0x67, 0x7a, 0x85, 0x66, 0x7a, 0x86, 0x68, 0x7b, 0x86, 0x57, 0x51, 0x51, 0x4c, 0x42, 0x40, 0x4d, 0x43, 0x41, 0x56, 0x4c, 0x4b, 0x4d, 0x44, 0x41, 0x4e, 0x44, 0x42, 0x4f, 0x45, 0x43, 0x67, 0x7b, 0x87, 0x4f, 0x44, 0x43, 0x50, 0x45, 0x44, 0x52, 0x46, 0x44, 0x51, 0x46, 0x45, 0x4b, 0x40, 0x3f, 0x51, 0x47, 0x45, 0x52, 0x48, 0x46, 0x53, 0x48, 0x47, 0x4b, 0x41, 0x3f, 0x54, 0x49, 0x46, 0x55, 0x4a, 0x47, 0x55, 0x49, 0x47, 0x68, 0x7c, 0x88, 0x4a, 0x40, 0x3e, 0x55, 0x4b, 0x49, 0x56, 0x4d, 0x4b, 0x53, 0x49, 0x47, 0x50, 0x46, 0x44, 0x4a, 0x41, 0x3e, 0x48, 0x3e, 0x3c, 0x4b, 0x42, 0x3f, 0x49, 0x3f, 0x3d, 0x46, 0x3d, 0x3c, 0x47, 0x3d, 0x3b, 0x47, 0x3e, 0x3b, 0x49, 0x40, 0x3d, 0x45, 0x3c, 0x3b, 0x46, 0x3c, 0x3a, 0xdd, 0x63, 0x56, 0x8d, 0x0, 0x0, 0x0, 0xad, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x2d, 0x88, 0x35, 0x42, 0x4, 0x0, 0xc, 0x4, 0x77, 0x93, 0x9c, 0xbb, 0xf4, 0xc8, 0xff, 0xdf, 0x84, 0xb5, 0xb8, 0x5b, 0x84, 0x0, 0x37, 0xc5, 0xca, 0x10, 0xd, 0xc9, 0xce, 0xaa, 0xea, 0x24, 0x40, 0xca, 0x41, 0x64, 0x2b, 0x5, 0x87, 0x34, 0xa8, 0x88, 0x54, 0xef, 0x82, 0x80, 0x89, 0x34, 0x36, 0x96, 0xe0, 0x14, 0xa4, 0x12, 0xa6, 0x24, 0x18, 0xe1, 0xa8, 0x50, 0x59, 0x7c, 0x73, 0x30, 0x50, 0x55, 0x4a, 0x31, 0xd7, 0xb4, 0x89, 0xa1, 0x51, 0xb2, 0x9c, 0x1, 0x2c, 0x4, 0x83, 0x15, 0x12, 0x30, 0xab, 0xe9, 0x5a, 0x1, 0xb4, 0x40, 0xa1, 0x29, 0xbe, 0x75, 0xe, 0x5a, 0x70, 0xbe, 0x2a, 0xff, 0x12, 0xf1, 0xef, 0x1b, 0x5f, 0x8d, 0x5b, 0x68, 0xd, 0xdc, 0xe3, 0xf1, 0x71, 0x16, 0x3e, 0x5b, 0xc8, 0x33, 0xa9, 0xc7, 0xbc, 0x7f, 0xa4, 0x22, 0x6a, 0xb5, 0x90, 0xcb, 0xb2, 0x1a, 0x25, 0x67, 0x8b, 0x8f, 0x6f, 0xf8, 0x64, 0xa8, 0x35, 0x7a, 0x25, 0xa8, 0xa7, 0x1, 0x38, 0xc, 0xcc, 0xab, 0x4c, 0x5, 0xea, 0xe3, 0x76, 0x2f, 0x54, 0x93, 0xf3, 0xf, 0x4f, 0x10, 0x8d, 0x4c, 0x16, 0x9d, 0xcf, 0x1f, 0xd1, 0xf9, 0x3, 0x34, 0xc8, 0x4a, 0xb4, 0x1d, 0xb, 0xcd, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char popup_window_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x48, 0x8, 0x3, 0x0, 0x0, 0x0, 0xb7, 0x21, 0x97, 0x38, 0x0, 0x0, 0x1, 0x71, 0x69, 0x43, 0x43, 0x50, 0x69, 0x63, 0x63, 0x0, 0x0, 0x28, 0x91, 0x75, 0x91, 0x3d, 0x4b, 0xc3, 0x50, 0x14, 0x86, 0xdf, 0xb6, 0x96, 0x8a, 0xad, 0x74, 0xd0, 0x41, 0xa4, 0x43, 0x86, 0x2a, 0xe, 0x2d, 0x14, 0x5, 0x71, 0xd4, 0x3a, 0x74, 0x29, 0x52, 0x6a, 0x5, 0xab, 0x2e, 0xc9, 0x6d, 0xd2, 0xa, 0x49, 0x1a, 0x6e, 0x52, 0xa4, 0xb8, 0xa, 0x2e, 0xe, 0x5, 0x7, 0xd1, 0xc5, 0xaf, 0xc1, 0x7f, 0xa0, 0xab, 0xe0, 0xaa, 0x20, 0x8, 0x8a, 0x20, 0xe2, 0xe2, 0x1f, 0xf0, 0x6b, 0x91, 0x12, 0xcf, 0x6d, 0xa, 0x2d, 0xd2, 0x9e, 0x70, 0x73, 0x1e, 0xde, 0x7b, 0xde, 0xc3, 0xbd, 0xe7, 0x2, 0xfe, 0xac, 0xce, 0xc, 0x7b, 0x20, 0x5, 0x18, 0xa6, 0xc3, 0xf3, 0x99, 0xb4, 0xb4, 0x5a, 0x5c, 0x93, 0x42, 0xef, 0x8, 0x21, 0x6, 0x1f, 0x82, 0x8, 0xcb, 0xcc, 0xb6, 0x16, 0x72, 0xb9, 0x2c, 0xfa, 0xc6, 0xcf, 0x23, 0x55, 0x52, 0x3c, 0x24, 0x45, 0xaf, 0xfe, 0x75, 0x3d, 0x23, 0x5c, 0x52, 0x6d, 0x6, 0xf8, 0x6, 0x89, 0x67, 0x99, 0xc5, 0x1d, 0xe2, 0x79, 0xe2, 0xec, 0x96, 0x63, 0x9, 0xde, 0x23, 0x1e, 0x65, 0x15, 0xb9, 0x44, 0x7c, 0x42, 0x9c, 0xe0, 0x74, 0x40, 0xe2, 0x5b, 0xa1, 0x2b, 0x1e, 0xbf, 0x9, 0x2e, 0x7b, 0xfc, 0x25, 0x98, 0x17, 0xf2, 0x8b, 0x80, 0x5f, 0xf4, 0x94, 0xca, 0x5d, 0xac, 0x74, 0x31, 0xab, 0x70, 0x83, 0x78, 0x8a, 0x38, 0x6e, 0xe8, 0x35, 0xd6, 0x3e, 0x8f, 0xb8, 0x49, 0x44, 0x35, 0x57, 0x96, 0x29, 0x8f, 0xd3, 0x8a, 0xc1, 0x46, 0x1e, 0x19, 0xa4, 0x21, 0x41, 0x41, 0xd, 0x9b, 0xd0, 0xe1, 0x20, 0x49, 0xd9, 0xa4, 0x99, 0xf5, 0xf6, 0xa5, 0x5a, 0xbe, 0x25, 0x54, 0xc9, 0xc3, 0xe8, 0x6f, 0xa1, 0xe, 0x4e, 0x8e, 0x32, 0x2a, 0xe4, 0x4d, 0x90, 0x5a, 0xa3, 0xae, 0x2a, 0x65, 0x8d, 0x74, 0x95, 0x3e, 0x1d, 0x75, 0x31, 0xf7, 0xff, 0xf3, 0xb4, 0xb5, 0x99, 0x69, 0xaf, 0x7b, 0x24, 0xd, 0x4, 0x5f, 0x5d, 0xf7, 0x73, 0x2, 0x8, 0xed, 0x3, 0xcd, 0x86, 0xeb, 0xfe, 0x9e, 0xba, 0x6e, 0xf3, 0xc, 0x8, 0xbc, 0x0, 0xd7, 0x66, 0xc7, 0x5f, 0xa5, 0x39, 0xcd, 0x7d, 0x93, 0xde, 0xe8, 0x68, 0xf1, 0x63, 0x20, 0xba, 0x3, 0x5c, 0xde, 0x74, 0x34, 0xe5, 0x0, 0xb8, 0xda, 0x5, 0xc6, 0x9e, 0x2d, 0x99, 0xcb, 0x2d, 0x29, 0x40, 0xcb, 0xaf, 0x69, 0xc0, 0xc7, 0x5, 0x30, 0x5c, 0x4, 0x46, 0xee, 0x81, 0xa1, 0x75, 0x6f, 0x56, 0xed, 0x7d, 0x9c, 0x3f, 0x1, 0x85, 0x6d, 0x7a, 0xa2, 0x3b, 0xe0, 0xf0, 0x8, 0x98, 0xa4, 0xfa, 0xe8, 0xc6, 0x1f, 0x7b, 0xde, 0x67, 0xcd, 0xda, 0x7, 0x3a, 0xfb, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x12, 0x0, 0x0, 0xb, 0x12, 0x1, 0xd2, 0xdd, 0x7e, 0xfc, 0x0, 0x0, 0x0, 0xa8, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x20, 0x1e, 0x23, 0x21, 0x1f, 0x24, 0x21, 0x1f, 0x24, 0x22, 0x20, 0x25, 0x1d, 0x1a, 0x20, 0x1d, 0x1d, 0x20, 0x1d, 0x1d, 0x22, 0x20, 0x1d, 0x22, 0x20, 0x1d, 0x25, 0x20, 0x20, 0x25, 0x22, 0x20, 0x25, 0xcf, 0xcd, 0xd7, 0xd0, 0xcd, 0xd7, 0xd1, 0xce, 0xd8, 0xd3, 0xd0, 0xda, 0xd4, 0xd1, 0xdb, 0xd5, 0xd3, 0xdc, 0xd6, 0xd4, 0xdd, 0xd7, 0xd6, 0xdf, 0xd9, 0xd7, 0xdf, 0xda, 0xd8, 0xe0, 0xdc, 0xda, 0xe2, 0xdd, 0xdb, 0xe3, 0xde, 0xdc, 0xe4, 0xdf, 0xdd, 0xe5, 0xe0, 0xde, 0xe6, 0xe1, 0xdf, 0xe7, 0xe2, 0xdf, 0xeb, 0xe3, 0xe1, 0xe8, 0xe5, 0xe2, 0xeb, 0xe8, 0xe5, 0xf1, 0xe8, 0x4e, 0x48, 0xd9, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x3, 0x9, 0xc, 0xf, 0x15, 0x1d, 0x20, 0x22, 0x25, 0x30, 0x3a, 0x44, 0x52, 0x57, 0x59, 0x5b, 0x5e, 0x60, 0x7e, 0x92, 0x9e, 0x9f, 0xa1, 0xa2, 0xb8, 0xba, 0xbd, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf5, 0xb3, 0x97, 0xfb, 0x0, 0x0, 0x0, 0xea, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x96, 0x31, 0xe, 0x83, 0x30, 0xc, 0x45, 0x6d, 0x93, 0xc2, 0xda, 0xb5, 0x13, 0xf7, 0x3f, 0x4a, 0xef, 0xd0, 0xa5, 0xc7, 0x28, 0x10, 0xff, 0xe, 0x4, 0x35, 0x88, 0xc6, 0x25, 0x48, 0x34, 0x2a, 0x6a, 0x16, 0x86, 0x3c, 0x7d, 0xbe, 0xfd, 0x49, 0x30, 0x51, 0xe1, 0xc5, 0xe1, 0xc1, 0xc2, 0xcc, 0xbc, 0xd8, 0x6, 0x0, 0x5, 0x22, 0x94, 0xa5, 0x72, 0xc2, 0x22, 0xb, 0x54, 0x15, 0x3a, 0x78, 0x5, 0x11, 0x91, 0x1b, 0xd1, 0xea, 0xd4, 0xf0, 0x1b, 0x59, 0x0, 0xc0, 0x83, 0x10, 0xa1, 0xe2, 0x9a, 0xba, 0x4d, 0x58, 0xbc, 0x13, 0x54, 0x23, 0x55, 0xe1, 0x36, 0x38, 0x5a, 0x14, 0xd3, 0xde, 0x64, 0x7c, 0x59, 0x40, 0x99, 0xc9, 0x27, 0x54, 0xdd, 0xe4, 0x4b, 0x26, 0x55, 0xa3, 0x49, 0x73, 0x55, 0x61, 0xc2, 0x3a, 0x94, 0xc8, 0x42, 0x27, 0x27, 0xaf, 0xc6, 0x7c, 0x4a, 0xcb, 0x85, 0xaa, 0xea, 0x34, 0x5a, 0xf7, 0xcc, 0xd8, 0xa0, 0x7a, 0x5c, 0x34, 0x23, 0x82, 0x1d, 0xd2, 0xda, 0x11, 0x3d, 0xaf, 0x45, 0xdd, 0x35, 0x89, 0x5c, 0x6, 0x1f, 0x9d, 0x2, 0xfb, 0xaa, 0x98, 0x1d, 0x98, 0x2, 0x68, 0x13, 0x32, 0x90, 0xf5, 0xd7, 0xdb, 0x6, 0xd4, 0x8, 0x0, 0xf4, 0xd, 0x3, 0xc5, 0xcb, 0xfa, 0xa5, 0xe, 0x1c, 0x34, 0xad, 0xff, 0xe7, 0x72, 0x44, 0x14, 0x40, 0x97, 0x46, 0x3a, 0x8c, 0x63, 0xcd, 0x9e, 0x69, 0x41, 0x2d, 0x34, 0x6c, 0xe6, 0xa3, 0xb0, 0x50, 0x64, 0xab, 0x86, 0x3f, 0x82, 0xb3, 0xc6, 0xc7, 0x7e, 0x88, 0x66, 0x42, 0x78, 0x82, 0x31, 0x94, 0x22, 0x77, 0xd4, 0x2d, 0xbe, 0x9e, 0x25, 0x9d, 0x7b, 0xb0, 0x59, 0x6d, 0x9f, 0xd7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char progress_bar_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0x43, 0x65, 0x7d, 0x95, 0x0, 0x0, 0x0, 0xc, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xb7, 0x4a, 0xbe, 0x33, 0x0, 0x0, 0x0, 0x35, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x9f, 0xe, 0x66, 0xb4, 0x33, 0x7a, 0xb4, 0x1b, 0x0, 0x19, 0x7f, 0x3b, 0x28, 0x64, 0xc0, 0xd, 0x84, 0x5b, 0x1, 0xb7, 0x14, 0xee, 0xc, 0x0, 0xcf, 0x9d, 0x26, 0xff, 0xba, 0xcb, 0x90, 0x39, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char progress_fill_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0x4c, 0xc0, 0xc1, 0xc2, 0xf8, 0xa5, 0xfa, 0x7f, 0x8, 0x34, 0x7e, 0xe9, 0x60, 0x81, 0xa1, 0xc0, 0xf8, 0xa5, 0xca, 0x17, 0x85, 0xff, 0x10, 0xa8, 0xf2, 0xc5, 0xf8, 0x25, 0x86, 0x2, 0x75, 0xa0, 0x4, 0x1c, 0x2, 0x4d, 0xa1, 0xbf, 0x2, 0x4c, 0x47, 0x12, 0xf6, 0xe6, 0x20, 0x2, 0x0, 0x78, 0x21, 0x45, 0x61, 0x7f, 0xe2, 0xad, 0xaf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char radio_checked_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x42, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0xd1, 0xa7, 0xf5, 0xaa, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0x4f, 0x55, 0x2, 0x43, 0x31, 0x8, 0x4b, 0xe8, 0x6a, 0xf7, 0xbf, 0xeb, 0xc, 0x9b, 0x6b, 0x1f, 0x9f, 0xc4, 0x89, 0xbf, 0xbb, 0x3f, 0x2a, 0x49, 0x64, 0xa6, 0x3e, 0x1e, 0x1c, 0x7c, 0x3e, 0xf2, 0x14, 0xb7, 0xc7, 0xac, 0xf1, 0x10, 0xa6, 0xe8, 0x1, 0x44, 0xad, 0x42, 0xb9, 0x33, 0x22, 0x43, 0x95, 0x68, 0x55, 0xa4, 0xdc, 0x1f, 0x1e, 0xa1, 0x67, 0xa2, 0x57, 0x96, 0x22, 0x0, 0xc2, 0x3d, 0xf5, 0x44, 0x8c, 0x8a, 0x5d, 0x21, 0x80, 0x74, 0x83, 0x1e, 0x97, 0xc7, 0x22, 0x59, 0x4c, 0xd7, 0xd8, 0xb5, 0x18, 0x4a, 0x7b, 0x57, 0x57, 0xdb, 0x1a, 0xf7, 0x77, 0x17, 0x3a, 0x56, 0x4e, 0x11, 0x6f, 0x82, 0x20, 0xde, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char radio_checked_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x83, 0xac, 0xe9, 0xaf, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x3f, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0x20, 0xe, 0xf0, 0x8, 0x2, 0x1, 0xf, 0x9c, 0x2b, 0x2c, 0x8, 0x5, 0xc2, 0x50, 0x1, 0xb0, 0x34, 0x58, 0x11, 0x5c, 0xbd, 0x10, 0x3f, 0x10, 0x8, 0xc1, 0x74, 0x1, 0x65, 0xf8, 0xc1, 0x0, 0xa6, 0x4, 0x28, 0x1, 0x11, 0xe0, 0xc1, 0x2d, 0x80, 0xa6, 0x5, 0xc3, 0x50, 0xc, 0x6b, 0x31, 0x1d, 0x86, 0xe1, 0x74, 0xfc, 0x0, 0x0, 0x1b, 0xc, 0x7, 0xb9, 0xa, 0x5e, 0x2e, 0x12, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char radio_unchecked_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0x2b, 0x6e, 0xf2, 0xbf, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x2, 0x61, 0x15, 0xed, 0xa9, 0x20, 0x5a, 0x72, 0xf5, 0x99, 0x33, 0xbb, 0x26, 0x1, 0x19, 0x73, 0xcf, 0x0, 0xc1, 0x4d, 0x6, 0x6, 0xd6, 0x35, 0x20, 0xc6, 0xa9, 0x0, 0x6, 0xb6, 0x3d, 0x20, 0xc6, 0xe9, 0x4, 0x6, 0xf6, 0x33, 0x60, 0x50, 0xc0, 0xc0, 0x1, 0x61, 0x34, 0xc0, 0x19, 0x70, 0x29, 0xb8, 0x62, 0xb8, 0x76, 0x84, 0x81, 0xc, 0x96, 0x20, 0x2b, 0xa6, 0xc0, 0x2d, 0x45, 0x0, 0x0, 0x37, 0xca, 0x3d, 0x81, 0xb4, 0x84, 0xb6, 0x80, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char radio_unchecked_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0xf, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x54, 0x54, 0x54, 0xad, 0xad, 0xad, 0x80, 0x80, 0x81, 0x64, 0x64, 0x64, 0xf4, 0x17, 0x36, 0x28, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1b, 0xfc, 0xf6, 0x4, 0xd4, 0x0, 0x0, 0x0, 0x40, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x7d, 0x8d, 0xb1, 0x11, 0x80, 0x40, 0xc, 0xc3, 0x94, 0xd, 0x12, 0x60, 0x1, 0xc4, 0x4, 0xb0, 0x1, 0xfb, 0x2f, 0x45, 0xe5, 0x3b, 0xaa, 0x57, 0xe5, 0xc2, 0xb6, 0x20, 0xc0, 0xcc, 0xc, 0x40, 0xed, 0x7a, 0x37, 0x70, 0xa8, 0xbe, 0x50, 0x9b, 0xea, 0xd9, 0xd4, 0xa3, 0x7a, 0x35, 0xa5, 0xaa, 0xeb, 0x90, 0x72, 0xe6, 0x39, 0xfc, 0x2b, 0x22, 0xd, 0x1f, 0xe7, 0x64, 0xa, 0x5d, 0x6c, 0xfe, 0xc1, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x14, 0xee, 0x69, 0x20, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x67, 0x1a, 0x96, 0x95, 0x1c, 0xf0, 0x43, 0x52, 0x0, 0x0, 0x0, 0x55, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0x8e, 0x45, 0x2, 0x80, 0x50, 0x10, 0x42, 0xc1, 0xee, 0xfb, 0x5f, 0xd4, 0xd6, 0xdf, 0xfd, 0x36, 0xd3, 0x3, 0x4, 0xd, 0x90, 0x6, 0xb2, 0x25, 0x39, 0xe0, 0xd2, 0xf9, 0xcb, 0x6a, 0x60, 0x6f, 0x27, 0xb7, 0xbc, 0x58, 0xb7, 0x53, 0x4d, 0x0, 0xf2, 0x3f, 0x5e, 0x36, 0x43, 0x5f, 0xc3, 0xf0, 0xdf, 0x17, 0xd7, 0xa6, 0xae, 0x60, 0x10, 0xff, 0x57, 0x16, 0xc5, 0x5a, 0xf1, 0x60, 0xe3, 0xe7, 0x5f, 0x37, 0x46, 0x74, 0xba, 0x9a, 0x16, 0xef, 0x37, 0x1c, 0x6f, 0x61, 0x47, 0x1, 0xa5, 0xc7, 0x32, 0x47, 0x38, 0x12, 0x92, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_button_left_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0x59, 0x59, 0x59, 0x8e, 0x47, 0x76, 0xf1, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x19, 0x33, 0xa1, 0x8c, 0xae, 0x55, 0x50, 0xc6, 0x2e, 0x28, 0xa3, 0x7b, 0xf7, 0x6e, 0x8, 0xa3, 0xe7, 0xcc, 0x19, 0xa8, 0x14, 0x9c, 0xd1, 0x7b, 0x17, 0xa6, 0xfd, 0x1d, 0x86, 0x81, 0x60, 0x6, 0xdc, 0x52, 0x43, 0x88, 0x33, 0x44, 0x0, 0xcc, 0x4e, 0x3f, 0xd1, 0x4, 0x90, 0xbf, 0x60, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_button_left_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0x58, 0xf9, 0x63, 0x6a, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x42, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x19, 0x33, 0xa1, 0x8c, 0xae, 0x55, 0x50, 0xc6, 0x9e, 0x3d, 0x10, 0x46, 0xf7, 0xee, 0xdb, 0x10, 0x46, 0xef, 0xdd, 0xbb, 0x50, 0xa9, 0x77, 0xef, 0xa0, 0x8c, 0xfe, 0x7f, 0x30, 0xed, 0xff, 0x81, 0xc, 0x4c, 0x93, 0x11, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0xfe, 0x97, 0x40, 0xa0, 0xa6, 0x84, 0xb1, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_button_right_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0x59, 0x59, 0x59, 0x8e, 0x47, 0x76, 0xf1, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x40, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x18, 0x33, 0x61, 0x8c, 0x59, 0x2b, 0xa0, 0x8c, 0x5d, 0xab, 0xa0, 0x8c, 0xdd, 0xbb, 0x77, 0x40, 0x18, 0x67, 0xce, 0x9c, 0x80, 0x31, 0xa0, 0x52, 0x77, 0x6f, 0x40, 0x19, 0xef, 0x30, 0xc, 0x84, 0x30, 0xe0, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0xfd, 0x36, 0x40, 0x93, 0xf1, 0x83, 0x5f, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_button_right_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0x2e, 0x3d, 0xb1, 0x1e, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0xc8, 0x31, 0x16, 0x2, 0x20, 0x10, 0x43, 0xc1, 0xe4, 0x83, 0xdc, 0xff, 0xb8, 0x88, 0xf, 0x57, 0xb, 0x8b, 0x80, 0x53, 0xe, 0xf2, 0x23, 0x18, 0xc6, 0x68, 0x61, 0x74, 0xca, 0xa, 0x2e, 0x74, 0xf9, 0x85, 0xfd, 0x17, 0x5c, 0x81, 0xfb, 0x11, 0x2a, 0xaa, 0x65, 0x80, 0x20, 0xc3, 0x5f, 0xaf, 0x2b, 0x96, 0xce, 0x78, 0xea, 0x88, 0x39, 0x95, 0x91, 0x70, 0x29, 0x94, 0xd9, 0x6b, 0x87, 0xf5, 0xfe, 0x0, 0xc6, 0xa7, 0x1b, 0x66, 0x7b, 0x42, 0xf1, 0x14, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_grabber_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x5d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x5b, 0x59, 0x61, 0x5b, 0x59, 0x61, 0x5a, 0x58, 0x60, 0x59, 0x57, 0x5f, 0x5a, 0x58, 0x60, 0x5a, 0x58, 0x60, 0x57, 0x56, 0x5e, 0x58, 0x56, 0x5e, 0x56, 0x55, 0x5d, 0x57, 0x55, 0x5d, 0x57, 0x55, 0x5d, 0x55, 0x53, 0x5b, 0x55, 0x53, 0x5b, 0x54, 0x53, 0x5b, 0x55, 0x54, 0x5c, 0x54, 0x52, 0x5a, 0x55, 0x53, 0x5b, 0x5a, 0x58, 0x60, 0x56, 0x54, 0x5c, 0x54, 0x53, 0x5a, 0x55, 0x53, 0x5b, 0x53, 0x51, 0x59, 0x52, 0x51, 0x59, 0x52, 0x50, 0x58, 0x51, 0x50, 0x58, 0x51, 0x4f, 0x57, 0x50, 0x4e, 0x56, 0x4f, 0x4d, 0x55, 0x50, 0x4f, 0x57, 0x54, 0x52, 0x5a, 0xae, 0x55, 0xff, 0xf7, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2c, 0xb8, 0xf4, 0x2e, 0xf2, 0xb8, 0xf4, 0xf5, 0xf4, 0xf5, 0xb8, 0x2f, 0xf2, 0x2e, 0xb8, 0xf4, 0xb8, 0x66, 0xf6, 0xf7, 0x12, 0x0, 0x0, 0x0, 0x48, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x64, 0xc7, 0xb1, 0x11, 0x80, 0x40, 0x8, 0x45, 0x41, 0xff, 0x83, 0x2, 0xe, 0xfb, 0x2f, 0x13, 0xe2, 0xf3, 0x6, 0x12, 0x1d, 0x37, 0x5b, 0xae, 0x97, 0x5f, 0x84, 0xdd, 0x68, 0xe2, 0x1e, 0x41, 0xb8, 0x77, 0x58, 0x6, 0xb6, 0xe8, 0x88, 0xd1, 0xe1, 0x93, 0xad, 0x63, 0xab, 0xa3, 0x3a, 0xa3, 0x26, 0xa9, 0x4a, 0x52, 0xf9, 0xc, 0x62, 0xcf, 0xa7, 0x8f, 0x1f, 0x1e, 0xbd, 0xff, 0x84, 0xee, 0x2, 0x0, 0x54, 0x76, 0x10, 0x19, 0x1e, 0xd7, 0x1d, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_grabber_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x69, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x97, 0xd0, 0xdf, 0x92, 0xcb, 0xdc, 0x84, 0xbb, 0xd4, 0x92, 0xca, 0xdc, 0x95, 0xd0, 0xdd, 0x83, 0xbb, 0xd3, 0x8b, 0xc8, 0xd7, 0x79, 0xb5, 0xcb, 0x78, 0xb4, 0xca, 0x73, 0xb0, 0xc7, 0x73, 0xb0, 0xc7, 0x7b, 0xc0, 0xcf, 0x79, 0xc5, 0xd1, 0x6b, 0xae, 0xc1, 0x75, 0xc6, 0xcf, 0x70, 0xbc, 0xca, 0x64, 0xa6, 0xbc, 0x71, 0xbc, 0xc9, 0x82, 0xba, 0xd4, 0x6a, 0xa2, 0xc6, 0x62, 0x9a, 0xc2, 0x61, 0x9a, 0xc1, 0x68, 0x9f, 0xc2, 0x5d, 0x92, 0xbb, 0x5c, 0x92, 0xb8, 0x58, 0x8d, 0xb6, 0x59, 0x8e, 0xb3, 0x56, 0x89, 0xb0, 0x5c, 0x91, 0xb2, 0x53, 0x84, 0xa9, 0x58, 0x8f, 0xae, 0x54, 0x83, 0xa4, 0x57, 0x8e, 0xad, 0x64, 0xa5, 0xba, 0x17, 0x3b, 0xfc, 0x67, 0x0, 0x0, 0x0, 0x13, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x25, 0xad, 0xf1, 0xad, 0x27, 0xef, 0xad, 0xf1, 0xf3, 0xf1, 0xf3, 0xad, 0x28, 0xef, 0x27, 0xad, 0xf2, 0xad, 0xcd, 0x8a, 0x27, 0xfe, 0x0, 0x0, 0x0, 0x46, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x7d, 0x8a, 0x25, 0x2, 0xc4, 0x40, 0x0, 0x3, 0x27, 0x39, 0x52, 0x57, 0xfa, 0xff, 0x1f, 0x17, 0x54, 0x19, 0x6d, 0x61, 0x4c, 0x90, 0x5b, 0x5e, 0x80, 0xec, 0x66, 0x58, 0x76, 0x3, 0x1f, 0x15, 0xd2, 0x9c, 0x97, 0x61, 0xf, 0xbf, 0x4a, 0xc0, 0x12, 0x3b, 0xde, 0xfb, 0xeb, 0x8, 0x66, 0xf, 0xbe, 0x8, 0x2, 0x83, 0x92, 0xec, 0x57, 0x12, 0x8, 0x28, 0xa5, 0x3a, 0x4e, 0x89, 0x7, 0x56, 0x51, 0x36, 0x11, 0x22, 0x4, 0xb, 0xcc, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_grabber_pressed_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x3a, 0xd8, 0xcf, 0xb2, 0xc1, 0x7d, 0xc3, 0x14, 0x20, 0x74, 0xdf, 0xcf, 0x82, 0x22, 0xb1, 0xc1, 0x7d, 0xfd, 0x2e, 0x8, 0xdc, 0xe0, 0x8e, 0x2a, 0x31, 0x5, 0x2e, 0x31, 0x85, 0x90, 0x4, 0xa6, 0x51, 0xb8, 0x2d, 0xa7, 0x36, 0x0, 0x0, 0x7b, 0xcd, 0x2b, 0x75, 0x45, 0x5e, 0xf8, 0x88, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char selection_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfd, 0xfb, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf6, 0xff, 0xf6, 0xf4, 0xff, 0x15, 0x15, 0x17, 0xff, 0x70, 0xc0, 0x21, 0x0, 0x0, 0x0, 0xe, 0x74, 0x52, 0x4e, 0x53, 0x6, 0xf, 0x16, 0x18, 0x2a, 0x3b, 0x40, 0x3c, 0x6, 0x3d, 0x44, 0x3e, 0x31, 0x25, 0x8, 0x3d, 0x16, 0xb4, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x6, 0x2, 0x23, 0x1, 0x6, 0x91, 0xb0, 0x34, 0x20, 0x48, 0x75, 0x64, 0x50, 0xef, 0x5c, 0x5, 0x4, 0x33, 0x8a, 0x18, 0xcc, 0xf6, 0xdc, 0x5, 0x82, 0xd3, 0xc9, 0xc, 0x66, 0x6b, 0x41, 0x8c, 0x5b, 0x94, 0x33, 0x60, 0x6, 0xc2, 0xad, 0x80, 0x5b, 0xa, 0x73, 0x6, 0x0, 0x45, 0x34, 0x48, 0x41, 0xa3, 0xc5, 0x91, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char selection_oof_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x2, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xaf, 0xdf, 0x90, 0xa5, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0x3, 0x20, 0x25, 0x16, 0xc, 0x1f, 0x74, 0xbf, 0x74, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x2f, 0x33, 0x4, 0x32, 0xde, 0xce, 0x64, 0xf4, 0x68, 0x53, 0x0, 0x32, 0xfe, 0xcd, 0xa0, 0x90, 0x1, 0x37, 0x10, 0x6e, 0x5, 0xdc, 0x52, 0xb8, 0x33, 0x0, 0xcc, 0x7, 0x26, 0xff, 0x1f, 0x38, 0x23, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char space_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x1c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x16, 0xfc, 0x17, 0xfc, 0x2f, 0x88, 0xcc, 0x15, 0xfa, 0x6f, 0x4, 0x84, 0x82, 0x18, 0x2, 0xe8, 0x5a, 0x88, 0x4, 0x0, 0x8c, 0xa4, 0xd, 0x47, 0x8, 0xea, 0xcc, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char spinbox_updown_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0xdc, 0x4f, 0x7f, 0x98, 0x86, 0x47, 0xfa, 0x81, 0xe5, 0x83, 0x1f, 0xf, 0x7e, 0x3d, 0xb2, 0xc5, 0xa5, 0x5b, 0xe2, 0xc1, 0x93, 0x7, 0xff, 0x81, 0xf0, 0xf9, 0x63, 0x69, 0x2c, 0xd2, 0x67, 0x58, 0xef, 0x1f, 0x2, 0x4a, 0x42, 0xe0, 0xf1, 0xdb, 0xec, 0x98, 0xfa, 0x67, 0x2, 0x25, 0xe0, 0xf0, 0xe1, 0x2, 0x86, 0x41, 0x7, 0x30, 0x1d, 0x39, 0x3, 0xbf, 0x37, 0x8f, 0xdd, 0x66, 0x27, 0x29, 0xa0, 0x10, 0x4a, 0x2c, 0xa0, 0x41, 0x8d, 0x1b, 0x3c, 0x4c, 0x3, 0x46, 0x16, 0x69, 0x0, 0x0, 0x87, 0x2a, 0x58, 0xb5, 0x18, 0xe9, 0x80, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char submenu_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0xc0, 0xf0, 0xe0, 0x3f, 0x8, 0xde, 0x4f, 0x60, 0x0, 0x3, 0xb8, 0xc0, 0x83, 0x2f, 0xf, 0xb5, 0xe1, 0x2, 0x50, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xc0, 0xff, 0x87, 0xf3, 0x31, 0x4, 0x30, 0xb5, 0x60, 0x1a, 0x8a, 0x61, 0x2d, 0x0, 0xa6, 0x55, 0x4f, 0xb1, 0x91, 0xd6, 0xa7, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char submenu_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x55, 0xcb, 0xa1, 0x11, 0x83, 0x40, 0x14, 0x45, 0xd1, 0xb3, 0x6b, 0x10, 0x34, 0x10, 0x47, 0x34, 0xf4, 0x13, 0x9d, 0x2, 0x28, 0x87, 0x2a, 0xe8, 0x84, 0x2, 0x82, 0x65, 0x71, 0x69, 0x0, 0x11, 0xf5, 0x11, 0x4c, 0x66, 0xd8, 0xe7, 0xee, 0x99, 0x79, 0x80, 0xed, 0x5d, 0xa2, 0x44, 0x9, 0x12, 0xec, 0x43, 0x2c, 0x5a, 0x78, 0xa6, 0xcc, 0xb7, 0x8d, 0xf9, 0x4a, 0xc8, 0xfc, 0x26, 0x3d, 0x37, 0xa8, 0x97, 0x69, 0x46, 0x6b, 0x5, 0x8f, 0x23, 0xbd, 0x1c, 0xd5, 0xa5, 0xfb, 0xc4, 0xf8, 0x87, 0x13, 0xd2, 0x2f, 0x14, 0x49, 0x6f, 0xb1, 0x11, 0xe1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x19, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0xfe, 0x47, 0xfe, 0x17, 0x1, 0xc2, 0x48, 0xd2, 0x84, 0x10, 0x2, 0x84, 0xb9, 0x98, 0x0, 0x0, 0xbf, 0x67, 0x1d, 0x5, 0x89, 0x9b, 0x48, 0x90, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_behind_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x57, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x2e, 0x36, 0x43, 0x40, 0x4d, 0x0, 0x0, 0x0, 0x43, 0x40, 0x4c, 0x3e, 0x3c, 0x47, 0x3e, 0x3b, 0x46, 0x31, 0x2f, 0x38, 0x2d, 0x2b, 0x33, 0x3f, 0x3c, 0x47, 0x91, 0xf8, 0xc4, 0xb2, 0x0, 0x0, 0x0, 0x18, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0xc, 0x4, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x77, 0xf3, 0x7, 0xef, 0xd3, 0x51, 0x5e, 0xca, 0x0, 0x0, 0x0, 0x5a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xb5, 0x8c, 0x35, 0x2, 0x80, 0x30, 0x10, 0x4, 0x6f, 0x73, 0xc4, 0x53, 0xf3, 0xff, 0x3f, 0xe2, 0xee, 0x4e, 0x9d, 0xe9, 0x56, 0x41, 0xd8, 0xa1, 0x69, 0x7, 0xd0, 0x43, 0x72, 0x19, 0x3d, 0x37, 0x10, 0x6c, 0x1f, 0x8d, 0x6a, 0x0, 0x2b, 0x25, 0xc1, 0x20, 0xa2, 0x69, 0x98, 0xba, 0xb6, 0x45, 0x9a, 0x2b, 0xbd, 0xe9, 0xd5, 0x69, 0xda, 0x0, 0xa9, 0x94, 0x9f, 0x68, 0x7, 0xc5, 0xd2, 0x50, 0x4a, 0x69, 0x71, 0x18, 0x63, 0xb3, 0x18, 0x7a, 0x71, 0x2e, 0xa3, 0xfd, 0x1b, 0xff, 0xc9, 0xff, 0x34, 0x86, 0x31, 0x3, 0x12, 0xb2, 0x4c, 0x6a, 0xfb, 0x60, 0xc7, 0xdc, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_close_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x90, 0x1, 0x6, 0xc0, 0x30, 0xc, 0x45, 0x77, 0x89, 0xd5, 0x76, 0xb3, 0x9e, 0x7b, 0x65, 0x63, 0xd, 0xf9, 0xbb, 0x48, 0x3b, 0xb3, 0x92, 0x54, 0x42, 0xb1, 0x5, 0x88, 0xf7, 0xc8, 0xcf, 0x9f, 0xfe, 0x1a, 0x8e, 0x14, 0xf4, 0x4e, 0x81, 0x63, 0x87, 0x51, 0x90, 0x28, 0x8, 0x46, 0x42, 0x51, 0x4a, 0x9e, 0x79, 0x43, 0xc5, 0x81, 0x55, 0x6f, 0xbc, 0x34, 0xdc, 0x2b, 0x2e, 0x16, 0xe5, 0x3a, 0xb1, 0xb, 0xb6, 0xca, 0x3, 0x2b, 0xb2, 0xc2, 0xbe, 0xf0, 0x66, 0x71, 0x4f, 0x70, 0x3b, 0x61, 0x14, 0x89, 0x26, 0x71, 0x5d, 0x6c, 0x9f, 0x1e, 0x17, 0x35, 0xae, 0xfa, 0xeb, 0xdc, 0x62, 0xc3, 0x84, 0x2d, 0x77, 0x22, 0xda, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_container_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x87, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x56, 0x52, 0x60, 0x47, 0x44, 0x52, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x47, 0x44, 0x51, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x40, 0x3e, 0x48, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x3f, 0x38, 0xaa, 0x5e, 0x0, 0x0, 0x0, 0x15, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0xe8, 0xff, 0x76, 0xed, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0xcf, 0x45, 0x2, 0x84, 0x30, 0x0, 0x4, 0xc1, 0x69, 0x1c, 0xfe, 0xff, 0xca, 0xd5, 0xeb, 0x2a, 0x83, 0x6b, 0xd2, 0xb7, 0x54, 0x1c, 0x31, 0x26, 0xc9, 0x63, 0x50, 0xcc, 0x32, 0x8d, 0x3f, 0xd9, 0x20, 0x5, 0x1a, 0xf2, 0xc7, 0x1f, 0x7a, 0x48, 0x4a, 0x66, 0xa8, 0xde, 0xc, 0xd0, 0x38, 0xd1, 0x54, 0xdb, 0x4c, 0x2b, 0xd0, 0x5c, 0x62, 0x8e, 0xa0, 0x1, 0x74, 0x4, 0x65, 0xd2, 0x1e, 0xd4, 0x83, 0xf7, 0x60, 0x65, 0x3e, 0x82, 0x3, 0x48, 0x49, 0xdf, 0x55, 0xca, 0xd4, 0xef, 0xfa, 0xfb, 0x27, 0x36, 0xf, 0x92, 0x31, 0x9e, 0x44, 0x3e, 0x17, 0x7c, 0xbf, 0x3, 0xef, 0x34, 0x3f, 0x3e, 0xe0, 0x24, 0x67, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_current_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3d, 0x48, 0x5b, 0x58, 0x66, 0x5b, 0x57, 0x65, 0x57, 0x54, 0x62, 0x55, 0x53, 0x62, 0x4a, 0x46, 0x52, 0x46, 0x41, 0x4e, 0x45, 0x41, 0x4d, 0x55, 0x52, 0x60, 0x44, 0x41, 0x4c, 0x53, 0x50, 0x5e, 0x43, 0x40, 0x4b, 0x52, 0x4e, 0x5d, 0x41, 0x3e, 0x4a, 0x4f, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x4e, 0x4b, 0x59, 0x3e, 0x3c, 0x47, 0x4d, 0x4a, 0x58, 0x3d, 0x3b, 0x46, 0x4b, 0x49, 0x54, 0x3c, 0x3a, 0x44, 0x4b, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x3b, 0x39, 0x42, 0x3b, 0x38, 0x43, 0x3b, 0x38, 0x42, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3a, 0x38, 0x41, 0x39, 0x36, 0x3f, 0x38, 0x36, 0x3f, 0x39, 0x36, 0x40, 0x38, 0x36, 0x40, 0x37, 0x35, 0x3e, 0x37, 0x34, 0x3e, 0x36, 0x35, 0x3d, 0xd7, 0x41, 0xa4, 0x19, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xa3, 0x31, 0x6b, 0xc2, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0xca, 0x85, 0xd, 0xc0, 0x40, 0x14, 0xc3, 0x50, 0x27, 0xf7, 0xd5, 0xfd, 0xd7, 0x2d, 0xa6, 0x4c, 0x16, 0x3f, 0x59, 0xe8, 0x8, 0xc8, 0x91, 0xd4, 0x5d, 0x92, 0xa3, 0xa1, 0x76, 0xb1, 0xd8, 0xcb, 0x92, 0x41, 0x1b, 0xb8, 0xe9, 0x2, 0xcf, 0xd2, 0x7e, 0xc4, 0x9c, 0x2d, 0xed, 0x3c, 0xc4, 0x95, 0xa3, 0x3f, 0xb0, 0x3, 0x7f, 0xa0, 0xe0, 0xb, 0x50, 0xe4, 0xb, 0xa1, 0xf2, 0x87, 0x38, 0x31, 0x4f, 0x4e, 0xa, 0x30, 0x22, 0x58, 0x33, 0x81, 0x1d, 0x4a, 0x44, 0x5a, 0xec, 0x8c, 0x27, 0x34, 0x10, 0x58, 0xf1, 0xf8, 0x39, 0xe0, 0x60, 0x56, 0x63, 0x63, 0x30, 0x69, 0xf8, 0x3c, 0xb4, 0xd1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0x2e, 0x23, 0x0, 0x0, 0x2e, 0x23, 0x1, 0x78, 0xa5, 0x3f, 0x76, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0xa6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x7c, 0xcd, 0xc5, 0x41, 0x4, 0x41, 0x10, 0x0, 0xc0, 0xea, 0xbd, 0xc1, 0x6d, 0xde, 0x47, 0x30, 0xe4, 0x40, 0x34, 0x1b, 0x2, 0xd1, 0x10, 0x1, 0x1a, 0xc, 0x4f, 0xdc, 0x6d, 0x1b, 0x77, 0xe6, 0xea, 0xd5, 0xde, 0xa1, 0x98, 0x36, 0x63, 0xd6, 0xb4, 0x91, 0xe, 0x30, 0x78, 0x74, 0xe7, 0xc6, 0xad, 0xbb, 0x97, 0xf6, 0x82, 0x6a, 0xc5, 0x82, 0x19, 0x21, 0x90, 0xd2, 0xad, 0x4b, 0xa7, 0x4e, 0x28, 0x66, 0xd4, 0xb5, 0xf5, 0xd5, 0xfe, 0xa1, 0xfa, 0xa1, 0x9c, 0x1c, 0x6c, 0xec, 0x6f, 0x7a, 0x28, 0x66, 0xad, 0x8c, 0xfb, 0xa3, 0xfa, 0x20, 0x7d, 0x9, 0xa5, 0x8e, 0x7b, 0xdb, 0x2e, 0x5e, 0x5f, 0xdc, 0xd7, 0x7b, 0x3f, 0xa5, 0x7b, 0xf7, 0xd5, 0x82, 0xe9, 0x62, 0x64, 0x66, 0x90, 0xfe, 0x1a, 0x98, 0x31, 0x2a, 0x3a, 0x91, 0x6, 0x7f, 0x25, 0xa1, 0x2b, 0x88, 0xe6, 0x85, 0xa0, 0x40, 0x73, 0x0, 0x5, 0x99, 0xf2, 0xff, 0x17, 0xf9, 0x79, 0x61, 0x98, 0x78, 0x21, 0x9a, 0x17, 0x82, 0xf2, 0x99, 0x34, 0x74, 0x13, 0xbb, 0x49, 0x87, 0xd0, 0x12, 0x4f, 0x3, 0x29, 0x20, 0x0, 0x0, 0xe, 0x38, 0x38, 0x83, 0xd2, 0xe8, 0x36, 0x36, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_menu_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x5, 0xa3, 0xe0, 0xc1, 0x7f, 0x54, 0x48, 0x3, 0x5, 0xf, 0xe3, 0x1e, 0x7c, 0x81, 0x4b, 0x7f, 0x7b, 0x98, 0x86, 0xc5, 0x15, 0xf7, 0x35, 0xee, 0x5f, 0x2, 0x4b, 0x5f, 0x7f, 0xac, 0x8b, 0xc3, 0xa1, 0x2f, 0xb8, 0x1f, 0xce, 0x7f, 0x38, 0xff, 0x5, 0x37, 0x75, 0xbd, 0xf, 0x0, 0x52, 0xd4, 0x48, 0xb8, 0x2d, 0x78, 0x5a, 0x91, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_menu_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x5, 0xa3, 0xe0, 0xc1, 0x7f, 0x54, 0x48, 0x3, 0x5, 0xf, 0xe3, 0x1e, 0x7c, 0x81, 0x4b, 0x7f, 0x7b, 0x98, 0x86, 0xc5, 0x15, 0xf7, 0x35, 0xee, 0x5f, 0x2, 0x4b, 0x5f, 0x7f, 0xac, 0x8b, 0xc3, 0xa1, 0x2f, 0xb8, 0x1f, 0xce, 0x7f, 0x38, 0xff, 0x5, 0x37, 0x75, 0xbd, 0xf, 0x0, 0x52, 0xd4, 0x48, 0xb8, 0x2d, 0x78, 0x5a, 0x91, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_off_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x7a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x2d, 0x26, 0x2c, 0x4d, 0x2b, 0x37, 0x63, 0x2f, 0x3f, 0x6e, 0x31, 0x43, 0x71, 0x32, 0x44, 0x6c, 0x31, 0x42, 0x51, 0x2c, 0x39, 0x47, 0x2a, 0x35, 0x66, 0x30, 0x40, 0x4d, 0x2b, 0x38, 0x32, 0x26, 0x2e, 0x26, 0x25, 0x2a, 0x2e, 0x25, 0x2c, 0x3c, 0x28, 0x31, 0x52, 0x2c, 0x39, 0x68, 0x30, 0x40, 0x27, 0x25, 0x2a, 0x50, 0x2c, 0x38, 0x5f, 0x2e, 0x3d, 0x35, 0x27, 0x2f, 0x38, 0x27, 0x30, 0x5e, 0x2e, 0x3d, 0x43, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x44, 0x2a, 0x34, 0x2b, 0x26, 0x2c, 0x64, 0x2f, 0x3f, 0x36, 0x27, 0x30, 0x37, 0x27, 0x30, 0x66, 0x2f, 0x40, 0x2c, 0x26, 0x2c, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x97, 0xb0, 0x86, 0xb4, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0x8, 0x17, 0x35, 0x86, 0xf3, 0x7, 0x3a, 0xb4, 0xb9, 0xb, 0x28, 0x8a, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xe6, 0x37, 0xf, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xe5, 0x7c, 0xf3, 0x66, 0x0, 0x0, 0x2, 0x29, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x3, 0x93, 0x24, 0x4b, 0x14, 0x85, 0x5f, 0xb9, 0xaa, 0x6d, 0x8e, 0x6d, 0xdb, 0x73, 0x2b, 0xb3, 0xe7, 0xad, 0x6d, 0xab, 0xdd, 0x63, 0xe3, 0xbf, 0x6f, 0x67, 0x65, 0xbb, 0xb4, 0xbb, 0x81, 0xc5, 0x97, 0x11, 0x27, 0x78, 0x4f, 0xea, 0xe2, 0xbf, 0x3f, 0x9, 0x86, 0xe5, 0x38, 0xde, 0x16, 0x8e, 0x63, 0x19, 0xc3, 0x70, 0x96, 0x17, 0x44, 0x49, 0x56, 0x1c, 0x36, 0x28, 0xb2, 0x24, 0xa, 0x3c, 0xab, 0xdf, 0x9d, 0x77, 0xca, 0x2e, 0xb7, 0xc7, 0xeb, 0xf3, 0x7, 0x2c, 0xf1, 0xfb, 0xbc, 0x1e, 0xb7, 0x4b, 0x76, 0xf2, 0x2d, 0xa7, 0x60, 0x83, 0xa1, 0x70, 0x24, 0x1a, 0x8b, 0x27, 0xb6, 0xc1, 0x86, 0xed, 0x44, 0x3c, 0x16, 0x8d, 0x84, 0x43, 0x41, 0xb6, 0x29, 0x3e, 0xd9, 0xd6, 0xde, 0xd1, 0xa9, 0x22, 0x84, 0x31, 0x6, 0xd, 0x4c, 0x40, 0x38, 0x85, 0xd, 0x4d, 0x3a, 0x3b, 0xda, 0xdb, 0x92, 0xd, 0xe, 0x4c, 0x97, 0xd2, 0xdd, 0xa3, 0xa2, 0x9d, 0xff, 0x6f, 0xdd, 0xbe, 0x43, 0xb9, 0x7b, 0x6f, 0x47, 0x73, 0x49, 0x21, 0x93, 0x73, 0xf4, 0x74, 0x2b, 0x5d, 0x4c, 0xfd, 0xfe, 0x92, 0xbb, 0x57, 0xc5, 0xf7, 0x1f, 0x3c, 0x7c, 0xf4, 0x18, 0x8, 0x8f, 0x9f, 0x3c, 0x7d, 0xf6, 0xfc, 0xfe, 0xb, 0xed, 0x24, 0x66, 0x37, 0xe9, 0x75, 0x4b, 0xb5, 0x77, 0x60, 0xfb, 0xfa, 0x7, 0x54, 0xfc, 0xf2, 0xd5, 0x6b, 0x68, 0xe0, 0xcd, 0xdb, 0x97, 0x2f, 0x2c, 0xdf, 0x62, 0xb0, 0xbf, 0xaf, 0x7a, 0x9, 0xbe, 0xcd, 0x33, 0x84, 0xde, 0xbd, 0x7f, 0x2, 0x4d, 0x7c, 0x78, 0xff, 0xf1, 0x31, 0x58, 0x30, 0x34, 0xdc, 0x36, 0x42, 0x8f, 0xc0, 0x8, 0xae, 0x51, 0xf4, 0xe9, 0xf3, 0x17, 0x68, 0xe1, 0xeb, 0xb7, 0x34, 0x58, 0x31, 0xea, 0x12, 0xa8, 0x1, 0x2b, 0xba, 0xc7, 0x50, 0xe6, 0x19, 0xe8, 0xc8, 0x66, 0x80, 0x92, 0xcb, 0x17, 0x8a, 0x25, 0xd8, 0x2d, 0x94, 0xd1, 0x64, 0xf, 0x8, 0xe3, 0x13, 0x93, 0xf4, 0xe, 0x9c, 0xe4, 0x89, 0xe3, 0xfd, 0x3, 0xd0, 0x71, 0xb0, 0xf, 0x94, 0xc3, 0xa3, 0xdc, 0x71, 0x21, 0xb7, 0x4b, 0x2, 0x35, 0xa1, 0x4c, 0xd, 0x4b, 0x1c, 0x35, 0x90, 0xa7, 0x67, 0xf0, 0xc9, 0x29, 0xe8, 0x38, 0x3d, 0xa9, 0x1c, 0x80, 0x4, 0x1d, 0x9d, 0xed, 0x9e, 0x13, 0x3, 0x22, 0x94, 0xed, 0x59, 0x99, 0xa7, 0x6, 0x6d, 0x73, 0x2a, 0xbe, 0x6d, 0xf0, 0x60, 0x8f, 0x6f, 0x13, 0x25, 0x41, 0x65, 0xb9, 0xc8, 0xef, 0x1e, 0x1e, 0x1e, 0xee, 0x69, 0x2, 0x94, 0xf9, 0xb6, 0x8a, 0x81, 0xec, 0x55, 0x2d, 0x4f, 0xb0, 0x47, 0x4e, 0x90, 0xbf, 0xdc, 0x25, 0x91, 0x44, 0x74, 0x27, 0x90, 0x3c, 0xb, 0xc8, 0xf2, 0xd, 0xce, 0xcf, 0x60, 0xaf, 0x58, 0xaa, 0x18, 0xe8, 0xdf, 0x80, 0x15, 0x27, 0x6c, 0x7e, 0x61, 0xb7, 0x78, 0x5e, 0xcc, 0x43, 0xab, 0x1, 0xfd, 0x5, 0x9a, 0x7, 0x8b, 0x36, 0x79, 0xb0, 0x77, 0x76, 0x5, 0x90, 0x2b, 0xed, 0x55, 0x84, 0xb2, 0x18, 0x16, 0x98, 0x5a, 0x26, 0x2e, 0xe1, 0x5f, 0xce, 0x44, 0x5a, 0xb, 0x83, 0x3f, 0x5f, 0xb, 0xcb, 0xb4, 0x16, 0xcc, 0xab, 0xf1, 0x9a, 0xc6, 0xdb, 0x57, 0x23, 0xed, 0x7, 0x2b, 0xab, 0x2a, 0xba, 0x69, 0xed, 0x7, 0xe6, 0x6c, 0xaf, 0xae, 0xd5, 0xfa, 0x1, 0xed, 0x48, 0x8a, 0x7b, 0x7d, 0x3, 0xa5, 0x10, 0x6d, 0x48, 0xb8, 0x2, 0x2a, 0x2f, 0x30, 0xa2, 0x73, 0xdd, 0xad, 0x24, 0x9b, 0x7b, 0x5a, 0x97, 0x14, 0xf6, 0x44, 0x63, 0x53, 0xdb, 0x60, 0xcb, 0xf6, 0x54, 0x2c, 0xea, 0x9, 0x4b, 0x5d, 0x6c, 0x6b, 0x57, 0xee, 0x93, 0x5d, 0x13, 0xc3, 0xb3, 0x9b, 0x1, 0x1b, 0x36, 0x67, 0x87, 0x27, 0x5c, 0x72, 0x1f, 0xcf, 0xe8, 0xa7, 0xca, 0x88, 0x30, 0xb9, 0xd5, 0x66, 0x3f, 0x17, 0xda, 0xb6, 0x26, 0x85, 0x11, 0x96, 0x31, 0x99, 0x4c, 0xfc, 0xf, 0x40, 0x27, 0xd3, 0xbf, 0xc4, 0x77, 0x82, 0xde, 0x40, 0xde, 0x4b, 0x3f, 0xe2, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_off_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0xfc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x69, 0x69, 0x6c, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71, 0xb, 0x1b, 0xbb, 0x0, 0x0, 0x0, 0x54, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0xff, 0xff, 0x8, 0x17, 0x35, 0x86, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x7, 0x3a, 0xb4, 0xff, 0xff, 0xff, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x8a, 0xff, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x37, 0xf, 0xff, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xd4, 0x6d, 0x6c, 0xf9, 0x0, 0x0, 0x2, 0x3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x55, 0x85, 0x9a, 0xe2, 0x30, 0x18, 0xbc, 0x7a, 0x8b, 0xbb, 0x7b, 0x2, 0xbb, 0x4d, 0x36, 0xb8, 0x3b, 0xeb, 0xae, 0xef, 0xff, 0x2e, 0x47, 0x48, 0x3f, 0xa0, 0x7a, 0xae, 0x83, 0x56, 0xfe, 0xe9, 0xfc, 0xfe, 0xe9, 0x6f, 0x2, 0xc7, 0xb, 0x82, 0xf8, 0x45, 0x8, 0x2, 0xcf, 0x39, 0x9a, 0xf3, 0xa2, 0x24, 0x2b, 0xaa, 0xe6, 0xfb, 0x2, 0x34, 0x55, 0x91, 0x25, 0x91, 0xb7, 0x3f, 0x5d, 0xf4, 0xab, 0x81, 0x60, 0x28, 0x1c, 0x89, 0xc6, 0x3c, 0x11, 0x8d, 0x84, 0x43, 0xc1, 0x80, 0xea, 0x17, 0x2d, 0x2a, 0xf8, 0x78, 0x22, 0x99, 0x4a, 0x67, 0xb2, 0xb9, 0x7c, 0xe1, 0xb, 0xc8, 0xe7, 0xb2, 0x99, 0x74, 0x2a, 0x99, 0x88, 0xf3, 0x26, 0xfb, 0x62, 0xa9, 0x5c, 0xa9, 0xd6, 0x0, 0x80, 0x10, 0xb2, 0xfb, 0x20, 0x5, 0x80, 0x75, 0xe8, 0x48, 0x52, 0xad, 0x94, 0x4b, 0xc5, 0x23, 0x6, 0xae, 0xa1, 0x9d, 0x9c, 0xd6, 0x80, 0x8e, 0xf0, 0x1e, 0x48, 0xdf, 0xb1, 0xd4, 0x81, 0x8b, 0x8e, 0xd3, 0x13, 0xad, 0xc1, 0x1d, 0xfc, 0x57, 0x82, 0x67, 0x35, 0x48, 0x30, 0x6a, 0xb6, 0x76, 0x97, 0x5b, 0x3a, 0x41, 0x98, 0xb4, 0x77, 0x4a, 0xdc, 0x3c, 0x39, 0xb, 0x2a, 0xfb, 0x38, 0xf0, 0x9d, 0x6e, 0xaf, 0x6, 0x11, 0xea, 0x1f, 0xdf, 0x41, 0x10, 0x6a, 0x7b, 0xc6, 0x62, 0xd0, 0xed, 0xf0, 0x6, 0x81, 0x58, 0xa, 0xd, 0x1, 0xa1, 0xa2, 0x8f, 0xa1, 0x23, 0xd2, 0xf2, 0x62, 0x18, 0x8e, 0x4a, 0x63, 0x26, 0x81, 0x93, 0x2, 0x13, 0xa0, 0xe3, 0xbe, 0xf5, 0xe, 0x82, 0x3d, 0x25, 0x14, 0x26, 0x1, 0x89, 0x11, 0xf0, 0x72, 0x70, 0xba, 0x15, 0x60, 0xbf, 0x3, 0x11, 0xe3, 0xcf, 0x6c, 0xbe, 0x58, 0xa2, 0x42, 0x7f, 0xb1, 0xc5, 0xee, 0x8b, 0x9d, 0x5f, 0xad, 0x37, 0xcc, 0x7, 0x41, 0x9, 0x65, 0x21, 0xbd, 0xd9, 0x8a, 0x3d, 0xe9, 0xf9, 0x7c, 0x46, 0x16, 0xb3, 0x3e, 0x35, 0xa4, 0x5f, 0x6, 0x2e, 0x46, 0x8a, 0xc0, 0x8, 0xd4, 0xcb, 0x2b, 0x88, 0x75, 0x3b, 0x81, 0x8e, 0xd, 0x1, 0xd4, 0x68, 0x8e, 0xfa, 0xe7, 0x94, 0xe0, 0x7c, 0x4f, 0x90, 0xbf, 0x56, 0x45, 0x46, 0x50, 0xba, 0xa9, 0x41, 0xec, 0x10, 0xb0, 0x96, 0x41, 0xd0, 0x3f, 0xdf, 0x7e, 0xe1, 0x79, 0xff, 0xfc, 0xfc, 0x9c, 0x6c, 0xbf, 0xf6, 0x14, 0xb7, 0x25, 0x83, 0x40, 0xd, 0xd7, 0x3c, 0x15, 0x90, 0x9d, 0x2, 0xec, 0xae, 0x40, 0x9, 0xdd, 0x1, 0xef, 0x18, 0xa0, 0x2, 0x59, 0xda, 0x5c, 0xd8, 0xc7, 0x80, 0x97, 0xd7, 0x5f, 0xc8, 0x42, 0x7f, 0x79, 0xbe, 0x9c, 0x17, 0x2c, 0x4, 0xfb, 0x2c, 0xd0, 0x3a, 0xb8, 0xff, 0x42, 0x1d, 0x10, 0x5a, 0x95, 0x33, 0x44, 0xd8, 0x97, 0x81, 0xfb, 0xa4, 0xc4, 0xed, 0x2b, 0xf1, 0x1, 0xfe, 0x40, 0x25, 0xd2, 0x5e, 0x18, 0x7c, 0x7b, 0x2f, 0x3c, 0xd2, 0x5e, 0xf8, 0x72, 0x37, 0x16, 0xbe, 0xdc, 0x8d, 0x6c, 0x1e, 0x3c, 0x3d, 0xd7, 0x40, 0xdb, 0x32, 0xf, 0xbc, 0xec, 0x9f, 0x5f, 0xf6, 0xf3, 0x80, 0x4d, 0x24, 0x2d, 0xf8, 0xfa, 0x6, 0xea, 0x80, 0xd, 0x24, 0x68, 0x0, 0x6c, 0x5f, 0x8e, 0xf6, 0xd5, 0xd7, 0xa0, 0x56, 0x34, 0xcf, 0xb4, 0x86, 0x92, 0xc, 0xa5, 0x33, 0x17, 0xf9, 0xc2, 0x17, 0x91, 0xbf, 0xc8, 0xa4, 0x43, 0x49, 0xa5, 0xc1, 0x5b, 0xa7, 0x72, 0x47, 0xd, 0xac, 0x47, 0xd7, 0xef, 0xb1, 0x2f, 0xe0, 0xfd, 0x7a, 0xb4, 0xe, 0xa8, 0x1d, 0x91, 0xb3, 0x6f, 0x95, 0xb1, 0xb4, 0xf9, 0x28, 0x7d, 0x79, 0x2f, 0x94, 0x3e, 0x36, 0xd2, 0x98, 0xe7, 0x5c, 0x36, 0x93, 0xf8, 0x15, 0xa0, 0x9b, 0xe9, 0xff, 0xc2, 0x67, 0x14, 0xf4, 0xa5, 0xb3, 0x35, 0x5e, 0x63, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_off_disabled_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x9c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x5d, 0x5d, 0x5f, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x40, 0x40, 0x44, 0x32, 0x32, 0x37, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x6a, 0x6a, 0x6d, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x50, 0x50, 0x51, 0x13, 0x13, 0x15, 0x2b, 0xcd, 0x4, 0x96, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x29, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xdb, 0x76, 0x83, 0x20, 0x10, 0x45, 0x8d, 0xe8, 0x24, 0x98, 0xda, 0x1a, 0x93, 0xda, 0x5b, 0xac, 0xb9, 0x50, 0x14, 0x52, 0x72, 0xfd, 0xff, 0x7f, 0xab, 0x68, 0x49, 0x28, 0x5a, 0x35, 0xf5, 0xa5, 0xf, 0x39, 0xf, 0xb3, 0x96, 0xe3, 0xda, 0x7, 0x98, 0x81, 0xb1, 0xac, 0x9b, 0xfe, 0x97, 0x6, 0x36, 0x72, 0x5c, 0x68, 0x91, 0xeb, 0x20, 0x7b, 0x50, 0xcf, 0xf, 0x5b, 0xe1, 0xb3, 0xc9, 0xb0, 0x6, 0x1f, 0xe1, 0x46, 0xc6, 0x1b, 0xdf, 0xf9, 0xf7, 0xa5, 0x1e, 0x2, 0xf, 0xf0, 0xc8, 0xe4, 0x27, 0x8d, 0xcb, 0x87, 0xd3, 0xd9, 0xf8, 0x31, 0x7a, 0x92, 0x7a, 0xf6, 0x5e, 0x5e, 0xdf, 0xa6, 0xa1, 0x3b, 0x31, 0xc, 0x1a, 0xd7, 0xf, 0xe7, 0xf1, 0xbb, 0xfe, 0x9d, 0xc4, 0xf3, 0x10, 0xff, 0xe4, 0x17, 0x4d, 0xfc, 0x72, 0x15, 0x7b, 0xc6, 0x81, 0xe2, 0xd5, 0x72, 0xa1, 0xf3, 0xeb, 0xc6, 0xf3, 0x93, 0x8f, 0xc4, 0x4c, 0x25, 0x33, 0x2, 0x6b, 0xcd, 0xc0, 0x56, 0x3f, 0x10, 0x4d, 0x33, 0x6, 0x24, 0xcd, 0x55, 0x4, 0x2e, 0x93, 0x9b, 0xa0, 0x6a, 0x1a, 0x6c, 0xe0, 0x53, 0x33, 0x40, 0x2a, 0x2f, 0x28, 0xe2, 0x29, 0x22, 0x12, 0x24, 0x25, 0x9d, 0x6b, 0xbb, 0xab, 0x1a, 0xec, 0xb6, 0x80, 0x34, 0x3, 0x47, 0x6d, 0x40, 0x42, 0x94, 0x11, 0x21, 0xd, 0x84, 0x32, 0xd8, 0x1f, 0xaa, 0x6, 0x87, 0x3d, 0xe8, 0x65, 0x54, 0x3d, 0x24, 0x22, 0xf, 0x47, 0x4a, 0x84, 0x10, 0xbc, 0x8, 0x45, 0xd6, 0x8f, 0xaa, 0x6, 0x91, 0xf, 0x50, 0xd3, 0x44, 0x5e, 0xec, 0xe0, 0x78, 0xfd, 0xe, 0x2e, 0x35, 0x60, 0xc0, 0x33, 0xf3, 0x8, 0x1d, 0x6a, 0x70, 0xee, 0x2, 0xc9, 0x44, 0x46, 0xc1, 0x30, 0xe8, 0xd0, 0x85, 0xcb, 0x3d, 0xe0, 0x4c, 0xd6, 0x92, 0xf1, 0xef, 0xd0, 0xf5, 0x1e, 0xf4, 0xbe, 0x89, 0xfd, 0xdf, 0x42, 0xff, 0xd7, 0x68, 0x9d, 0xae, 0x9b, 0x7, 0xa7, 0xba, 0x89, 0x4, 0x9d, 0x55, 0x37, 0x91, 0xca, 0x99, 0x88, 0xdb, 0x61, 0xfc, 0xeb, 0x4c, 0xbc, 0xe9, 0xaf, 0xfa, 0x2, 0xdc, 0x1a, 0x30, 0x60, 0x4e, 0xef, 0xb8, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_off_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0xaa, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x11, 0x11, 0x14, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x25, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x26, 0x11, 0x11, 0x13, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x24, 0x24, 0x28, 0x2d, 0x26, 0x2c, 0x51, 0x2c, 0x39, 0x6c, 0x31, 0x42, 0x71, 0x32, 0x44, 0x6e, 0x31, 0x43, 0x63, 0x2f, 0x3f, 0x4d, 0x2b, 0x37, 0x27, 0x25, 0x2a, 0x47, 0x2a, 0x35, 0x68, 0x30, 0x40, 0x52, 0x2c, 0x39, 0x3c, 0x28, 0x31, 0x2e, 0x25, 0x2c, 0x26, 0x25, 0x2a, 0x32, 0x26, 0x2e, 0x4d, 0x2b, 0x38, 0x66, 0x30, 0x40, 0x50, 0x2c, 0x38, 0x5e, 0x2e, 0x3d, 0x38, 0x27, 0x30, 0x35, 0x27, 0x2f, 0x5f, 0x2e, 0x3d, 0x44, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x43, 0x2a, 0x34, 0x2c, 0x26, 0x2c, 0x66, 0x2f, 0x40, 0x37, 0x27, 0x30, 0x36, 0x27, 0x30, 0x64, 0x2f, 0x3f, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x25, 0x25, 0x27, 0xea, 0xac, 0x78, 0x5d, 0x0, 0x0, 0x0, 0x51, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x76, 0xfb, 0x8a, 0xde, 0xf6, 0x82, 0x9b, 0xc6, 0xe6, 0x4c, 0xfe, 0x4f, 0xe9, 0xfb, 0x37, 0x83, 0x9c, 0xf6, 0x77, 0x8b, 0x3b, 0x9c, 0xbb, 0x74, 0xda, 0xf3, 0x87, 0xfb, 0x45, 0x4e, 0x53, 0x5, 0xf, 0x14, 0xc7, 0x22, 0x44, 0x61, 0x0, 0x0, 0x2, 0x45, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0x55, 0xe7, 0x5f, 0xd3, 0x40, 0x18, 0x26, 0x3b, 0x91, 0x24, 0x65, 0x95, 0xd, 0xd, 0x8e, 0x22, 0x6a, 0x71, 0x2b, 0x82, 0x75, 0xe1, 0x66, 0xa6, 0x97, 0xa0, 0x56, 0x45, 0xa4, 0x2a, 0x2e, 0xdc, 0x76, 0x25, 0x18, 0x84, 0x6, 0x9c, 0xff, 0xb3, 0xb9, 0x26, 0x17, 0xda, 0xa4, 0x49, 0xd4, 0x4f, 0xfa, 0x7c, 0xb8, 0xdf, 0xe5, 0xcd, 0xef, 0x79, 0xee, 0xbd, 0xf7, 0xde, 0xd1, 0xd4, 0xf4, 0xf, 0x1, 0xc3, 0x9, 0x82, 0x8c, 0x4, 0x41, 0xe0, 0x58, 0x43, 0x3a, 0x4e, 0x52, 0x34, 0xc3, 0x72, 0xbb, 0x22, 0xc0, 0xb1, 0xc, 0x4d, 0x91, 0xb8, 0xff, 0x74, 0xb2, 0x99, 0xe5, 0x5, 0x31, 0xd6, 0xd2, 0xda, 0x16, 0x8a, 0xd6, 0x96, 0x98, 0x28, 0xf0, 0x6c, 0x33, 0xe9, 0xf1, 0x2, 0x6f, 0xef, 0x88, 0x77, 0x76, 0x75, 0xf7, 0xf4, 0xf6, 0xc9, 0x11, 0xc8, 0xf4, 0xf5, 0xf, 0xc, 0x76, 0xc6, 0x3b, 0xda, 0xeb, 0x9c, 0xc0, 0x13, 0xd2, 0xd0, 0xee, 0x3d, 0x99, 0x0, 0xe, 0x50, 0x20, 0xec, 0xbd, 0xb5, 0x1, 0x40, 0xdd, 0xbb, 0x6f, 0x48, 0x4a, 0xd4, 0x28, 0x60, 0x49, 0x6e, 0x78, 0xff, 0x48, 0xe0, 0xa1, 0x90, 0xbb, 0x70, 0xeb, 0xf6, 0x1d, 0x1b, 0xd9, 0xbb, 0xf7, 0x16, 0x80, 0x7a, 0x60, 0x98, 0x4b, 0x62, 0x3b, 0xf7, 0x67, 0x84, 0x83, 0xa1, 0xbe, 0xdf, 0x5f, 0x7c, 0xb0, 0xf4, 0x70, 0x39, 0x57, 0xdd, 0xe7, 0x1e, 0x3d, 0x7e, 0xb2, 0xa8, 0xa8, 0x87, 0x4, 0xc6, 0x8d, 0x3, 0x9e, 0x1a, 0x3d, 0x7c, 0x24, 0x94, 0xbf, 0xf2, 0xf4, 0x59, 0xed, 0xf7, 0xf3, 0x17, 0x2b, 0x8a, 0x7a, 0x74, 0x34, 0x85, 0x2e, 0x41, 0x4a, 0xe2, 0xb1, 0x30, 0x7e, 0xee, 0xe5, 0xea, 0xab, 0x7a, 0xcb, 0xf2, 0xea, 0x6b, 0x70, 0x5c, 0x94, 0x48, 0xe7, 0x6, 0x14, 0x7f, 0x22, 0x34, 0xf0, 0x6f, 0xde, 0xbe, 0xf3, 0x9a, 0xde, 0x7f, 0xf8, 0x8, 0x4e, 0xf2, 0x94, 0x73, 0x3, 0x5a, 0x38, 0x85, 0x7e, 0xe4, 0xb, 0xc5, 0x52, 0x59, 0xd6, 0x8a, 0x16, 0xaa, 0x8b, 0xe, 0x8d, 0x6b, 0x9f, 0xfc, 0xa2, 0x4b, 0x6b, 0xe0, 0xb4, 0x40, 0xdb, 0x2, 0x4, 0x23, 0xf6, 0x20, 0xbb, 0xb1, 0x9e, 0xff, 0x5c, 0xcc, 0x6b, 0x90, 0xa8, 0xd9, 0x6c, 0xb, 0x1b, 0x9b, 0x7e, 0x81, 0xcd, 0xd, 0xa5, 0x5f, 0x64, 0x1c, 0x1, 0x76, 0xac, 0x17, 0x39, 0x0, 0x49, 0xeb, 0x15, 0xcd, 0x84, 0x2, 0x26, 0x12, 0xd8, 0xda, 0xf6, 0xb, 0x6c, 0x6f, 0x29, 0x67, 0xc6, 0x58, 0x47, 0x40, 0x1a, 0x47, 0x6f, 0xa8, 0x99, 0xd6, 0xf2, 0xa5, 0xa0, 0x19, 0x86, 0xa1, 0x57, 0x97, 0xaa, 0x35, 0x9b, 0x6b, 0x10, 0xd8, 0xac, 0xa2, 0x8e, 0x4b, 0xc8, 0x83, 0x18, 0x4a, 0x22, 0x1d, 0x7a, 0x50, 0xf8, 0xaa, 0x41, 0xa6, 0x66, 0x44, 0x78, 0xa0, 0xc6, 0x58, 0x37, 0x6, 0x13, 0xc8, 0x6e, 0x56, 0x64, 0xbd, 0x54, 0xf6, 0x8, 0x34, 0x8e, 0x1, 0x38, 0x8b, 0x62, 0x80, 0xd3, 0x69, 0xf7, 0x15, 0xb4, 0x92, 0x59, 0x2a, 0xc8, 0x1e, 0x81, 0xa0, 0x57, 0x48, 0xd3, 0x6e, 0x1e, 0x9c, 0x73, 0x7f, 0xe8, 0x95, 0x6f, 0x56, 0x2c, 0xcb, 0xba, 0xb3, 0x84, 0xe5, 0xc1, 0x79, 0x94, 0x7, 0x7f, 0x97, 0x89, 0xca, 0x5, 0x37, 0x13, 0x61, 0x2d, 0x5c, 0xfc, 0xf3, 0x5a, 0xb8, 0xb4, 0x53, 0xb, 0xbf, 0x51, 0x8d, 0xdf, 0x43, 0xab, 0x11, 0xf6, 0x83, 0xc9, 0xcb, 0xa1, 0x3e, 0xd4, 0xf7, 0x83, 0x1f, 0x40, 0xbd, 0x32, 0x59, 0xd3, 0xf, 0x60, 0x47, 0xe2, 0x84, 0xab, 0xd7, 0x82, 0xc8, 0x76, 0x47, 0x72, 0x9a, 0x92, 0xd5, 0x91, 0x7e, 0x82, 0xeb, 0x37, 0x4, 0x2e, 0x51, 0xdf, 0xd3, 0x92, 0x4c, 0x5c, 0xec, 0xea, 0x9e, 0x18, 0x91, 0x23, 0x91, 0xb9, 0x39, 0x30, 0x28, 0xc6, 0x99, 0xa4, 0xa7, 0x31, 0x63, 0x64, 0x8a, 0xe5, 0xd3, 0x53, 0xd3, 0x33, 0x6d, 0x11, 0x98, 0x99, 0x9e, 0x4a, 0xf3, 0x6c, 0x8a, 0xf4, 0xcd, 0x6, 0xc, 0x9f, 0xa5, 0xe6, 0xe6, 0xa5, 0xe8, 0xb9, 0x20, 0xcd, 0xcf, 0x51, 0xb3, 0x8d, 0x67, 0x8b, 0x35, 0x99, 0xa2, 0x7, 0x93, 0x35, 0x9a, 0x2, 0x26, 0xd3, 0xff, 0x8b, 0x5f, 0x3d, 0xdc, 0x7c, 0xb4, 0x8c, 0xb2, 0xd8, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_on_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x74, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x12, 0x12, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x1a, 0x1a, 0x1e, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x2c, 0x36, 0x27, 0x49, 0x65, 0x29, 0x5d, 0x85, 0x2a, 0x66, 0x95, 0x2a, 0x68, 0x99, 0x29, 0x64, 0x92, 0x28, 0x4c, 0x6b, 0x25, 0x27, 0x2d, 0x27, 0x43, 0x5c, 0x29, 0x5f, 0x89, 0x27, 0x49, 0x66, 0x25, 0x30, 0x3e, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x25, 0x2d, 0x38, 0x25, 0x3a, 0x4c, 0x27, 0x4d, 0x6b, 0x29, 0x60, 0x8c, 0x27, 0x44, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x59, 0x7f, 0x25, 0x34, 0x43, 0x25, 0x35, 0x45, 0x28, 0x58, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x40, 0x57, 0x27, 0x41, 0x57, 0x25, 0x2a, 0x33, 0x29, 0x5d, 0x87, 0x25, 0x34, 0x44, 0x25, 0x2b, 0x34, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x27, 0x43, 0x5b, 0x27, 0x4d, 0x6c, 0x27, 0x4e, 0x6d, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x28, 0x56, 0x7b, 0x26, 0x3b, 0x4e, 0x26, 0x3a, 0x4e, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0xe4, 0xe4, 0xe5, 0x27, 0x44, 0x5d, 0xdd, 0xc9, 0xf2, 0x7e, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0x8, 0x17, 0x35, 0x73, 0xd9, 0x7, 0x3a, 0x96, 0xf9, 0x9a, 0xb, 0x28, 0x76, 0xfb, 0x77, 0xde, 0x45, 0x5, 0x82, 0xc6, 0xc6, 0x37, 0xf, 0xe9, 0x4c, 0x4e, 0x4f, 0x50, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x37, 0x21, 0x5a, 0x6c, 0x0, 0x0, 0x2, 0x4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x63, 0x83, 0xdc, 0x60, 0x10, 0xc7, 0x1b, 0x3c, 0xc1, 0xda, 0x3e, 0xdb, 0x36, 0xe7, 0x6c, 0xdb, 0xa, 0xcf, 0xc6, 0x7e, 0xf8, 0x2a, 0x6d, 0xb8, 0xac, 0x7b, 0xbf, 0xf7, 0xf3, 0x1f, 0xcf, 0x7c, 0xf8, 0x97, 0xc0, 0x70, 0x82, 0x20, 0xb3, 0x42, 0x10, 0x38, 0x96, 0xd2, 0x1c, 0x27, 0x11, 0x45, 0x33, 0xac, 0x2d, 0xb, 0x2c, 0x43, 0x53, 0x88, 0xc4, 0xad, 0xde, 0x49, 0x3b, 0xe3, 0x70, 0xba, 0xdc, 0x1e, 0xaf, 0x2f, 0x23, 0x5e, 0x8f, 0xdb, 0xe5, 0x74, 0x30, 0x76, 0xd2, 0x14, 0x5, 0xee, 0xf, 0x4, 0x43, 0xe1, 0x48, 0x34, 0x16, 0x87, 0x2c, 0xc4, 0x63, 0xd1, 0x48, 0x38, 0x14, 0xc, 0xf8, 0x71, 0x83, 0x7d, 0xa2, 0xa0, 0xb0, 0xa8, 0x78, 0x4, 0x72, 0x64, 0xa4, 0xb8, 0xa8, 0xb0, 0x20, 0xa1, 0x53, 0xc0, 0x4a, 0xd8, 0xd2, 0xb2, 0x72, 0xc8, 0xc4, 0xe8, 0xd8, 0xf8, 0xc4, 0xa4, 0xc2, 0xd4, 0xf4, 0x28, 0x94, 0x97, 0x95, 0xb2, 0x25, 0x98, 0x96, 0x3f, 0xed, 0xac, 0xc8, 0x18, 0xfb, 0xcc, 0xec, 0xdc, 0xfc, 0xc2, 0xe2, 0xd2, 0x17, 0x96, 0x57, 0x56, 0xd7, 0xd6, 0x37, 0x66, 0xe2, 0x15, 0x4e, 0x5a, 0xad, 0x3, 0x5e, 0x59, 0x55, 0x5d, 0x93, 0xd1, 0x7e, 0x73, 0x6b, 0x1b, 0x74, 0xec, 0xec, 0x6e, 0xce, 0xd4, 0xd4, 0x56, 0x55, 0x7e, 0x4f, 0x82, 0x2c, 0x70, 0xd5, 0x41, 0x6, 0xf6, 0xf6, 0xb7, 0x56, 0xc0, 0xc0, 0xca, 0xd6, 0xc1, 0x5e, 0x5d, 0x7d, 0x41, 0x83, 0x12, 0x2, 0x86, 0x1c, 0x8d, 0x90, 0x89, 0xc3, 0xa3, 0x63, 0x30, 0xb1, 0x33, 0x77, 0x2, 0x8d, 0xe, 0xa4, 0x8, 0xe0, 0x94, 0xb3, 0x9, 0x54, 0x4e, 0xcf, 0x38, 0x5e, 0x0, 0x91, 0x93, 0x40, 0x94, 0x41, 0xe1, 0xfc, 0x2, 0x2c, 0x5c, 0x9e, 0x43, 0x73, 0x4b, 0xab, 0x92, 0x3, 0x41, 0xbb, 0xa2, 0xa0, 0x22, 0x5f, 0x9d, 0x5e, 0x73, 0xa7, 0x22, 0x27, 0x6b, 0x2, 0x37, 0xb7, 0x60, 0xe1, 0xee, 0x6, 0xda, 0xea, 0x69, 0x42, 0x11, 0x60, 0xda, 0x63, 0x5a, 0x0, 0xdc, 0x3d, 0xc0, 0xd5, 0x83, 0xf8, 0xc8, 0x8b, 0xaa, 0xc0, 0xd3, 0x33, 0x58, 0x78, 0x7e, 0x82, 0xf2, 0xe, 0x86, 0x54, 0x4, 0xa, 0x3a, 0xb5, 0x1e, 0x8a, 0x8f, 0x0, 0xf0, 0x72, 0x26, 0xca, 0x2f, 0x8f, 0xaa, 0xc0, 0xc4, 0x22, 0x58, 0x58, 0x9c, 0x0, 0xe8, 0x2a, 0xf8, 0x26, 0xc0, 0xb8, 0xb5, 0x21, 0xba, 0xff, 0x12, 0xc1, 0xd9, 0xeb, 0x67, 0xe3, 0xb7, 0xb3, 0x9c, 0x23, 0xa0, 0x5d, 0x6d, 0xa0, 0xf2, 0xf8, 0x0, 0xf7, 0xbc, 0xf0, 0x59, 0x40, 0xe0, 0x72, 0xad, 0x1, 0x4e, 0xb5, 0xe8, 0xba, 0x20, 0xf2, 0x8f, 0xfc, 0xd9, 0xd7, 0x2, 0x3e, 0xe6, 0xda, 0x5, 0xc, 0x39, 0xba, 0x41, 0xe3, 0xfe, 0x41, 0x2, 0x38, 0x15, 0x0, 0x24, 0x21, 0xf3, 0x1c, 0x74, 0x7, 0x11, 0xf6, 0xd3, 0x93, 0xa8, 0xee, 0x42, 0x6d, 0xfe, 0xbb, 0xd0, 0xa3, 0xed, 0x42, 0xe, 0xdb, 0xb8, 0x61, 0xdc, 0xc6, 0xa4, 0xba, 0x8d, 0xea, 0x3d, 0xe8, 0xed, 0xab, 0xc9, 0xe7, 0x1e, 0xd4, 0xf4, 0xf5, 0xab, 0xf7, 0x40, 0xb9, 0x48, 0xac, 0x73, 0x60, 0x10, 0x72, 0x66, 0x70, 0xc0, 0xc9, 0x26, 0x8c, 0x37, 0xad, 0x84, 0xe, 0xba, 0xc2, 0x91, 0xb6, 0x72, 0xc8, 0x4a, 0x79, 0x5b, 0x24, 0xec, 0xa, 0xd2, 0x25, 0xb8, 0xf9, 0x2a, 0x57, 0x32, 0x8e, 0x96, 0xfa, 0x8e, 0x21, 0x5f, 0x16, 0x86, 0x3a, 0xea, 0x5b, 0x1c, 0x4c, 0x25, 0x89, 0x59, 0xbf, 0x4a, 0x3, 0x6a, 0x1d, 0x2e, 0xc8, 0xfe, 0x17, 0xa, 0x86, 0x5b, 0x51, 0x3, 0x8e, 0xa5, 0xf9, 0x4c, 0x64, 0xe, 0x68, 0x9f, 0xe9, 0xbd, 0xf0, 0x9, 0xb7, 0x71, 0x36, 0xc6, 0x9b, 0x3d, 0x7f, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_on_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x53, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x1a, 0x1a, 0x1e, 0x40, 0x40, 0x44, 0x56, 0x56, 0x58, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x3c, 0x3c, 0x3d, 0x2e, 0x2e, 0x30, 0x27, 0x27, 0x29, 0x64, 0x64, 0x66, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0x5d, 0x5f, 0x34, 0x34, 0x36, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x32, 0x32, 0x37, 0x42, 0x42, 0x44, 0x6a, 0x6a, 0x6d, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x49, 0x4a, 0x0, 0x0, 0x0, 0x50, 0x50, 0x51, 0x70, 0x70, 0x74, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbd, 0xb, 0x85, 0x35, 0x0, 0x0, 0x0, 0x71, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xff, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xff, 0x7, 0x3a, 0x96, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x76, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x77, 0xde, 0xff, 0xff, 0x45, 0x5, 0x82, 0xff, 0xff, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x37, 0xf, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x4c, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4e, 0x4f, 0xff, 0x50, 0xff, 0xff, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x49, 0x96, 0x6e, 0xf, 0x0, 0x0, 0x1, 0xfa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x18, 0x5e, 0x0, 0xd0, 0x5c, 0x39, 0x28, 0x49, 0x12, 0xc0, 0x60, 0xb8, 0xda, 0xdd, 0xcb, 0x31, 0x33, 0xb6, 0x6d, 0xdb, 0x5e, 0xdb, 0xd6, 0xfb, 0x17, 0x4e, 0x7d, 0x37, 0xe6, 0xf9, 0x2b, 0x23, 0x7f, 0x9c, 0x20, 0x28, 0x86, 0xe1, 0xb, 0xc1, 0x30, 0x14, 0x99, 0x6a, 0x8e, 0xe2, 0x4, 0x49, 0xd1, 0xcc, 0xda, 0x2, 0x18, 0x9a, 0x22, 0x9, 0x1c, 0x9d, 0xf4, 0x8e, 0xaf, 0xd3, 0x1b, 0x9b, 0x5b, 0xdb, 0x1c, 0x2e, 0x6f, 0x2e, 0x5c, 0xce, 0xf6, 0xd6, 0xe6, 0x6, 0xbd, 0x8e, 0x8f, 0x45, 0x81, 0xf2, 0x5, 0x42, 0x91, 0x58, 0x22, 0x95, 0xc9, 0x61, 0x1, 0x72, 0x99, 0x54, 0x22, 0x16, 0x9, 0x5, 0x7c, 0x74, 0xc4, 0x5e, 0xa1, 0x54, 0xa9, 0x35, 0x5a, 0x58, 0x12, 0xad, 0x46, 0xad, 0x52, 0x2a, 0x86, 0x14, 0x10, 0x1d, 0xa3, 0x37, 0x18, 0x61, 0x1e, 0x26, 0xb3, 0xc5, 0x6a, 0x63, 0xb1, 0x3b, 0x4c, 0x60, 0x34, 0xe8, 0x19, 0x1d, 0x32, 0xc8, 0x9f, 0xda, 0x74, 0xce, 0x8d, 0xdd, 0xe5, 0xf6, 0x98, 0xbd, 0x3e, 0xff, 0x57, 0x2, 0xa6, 0x60, 0x28, 0xec, 0x76, 0xc9, 0x9d, 0x9b, 0x54, 0xbf, 0xe, 0x68, 0x24, 0x1a, 0x8b, 0xcf, 0xb5, 0x4f, 0x24, 0x53, 0x30, 0x44, 0x3a, 0x99, 0x70, 0xc5, 0x33, 0xd1, 0xc8, 0x8f, 0x24, 0x70, 0xe5, 0x56, 0x16, 0xe6, 0x90, 0xcb, 0x27, 0x4d, 0x63, 0x9, 0x25, 0xf3, 0xb9, 0x6c, 0x41, 0x59, 0x64, 0x43, 0x40, 0x88, 0x8d, 0x12, 0xcc, 0xa3, 0x5c, 0x49, 0xc3, 0x18, 0x69, 0x4f, 0x19, 0x4a, 0x1b, 0x4, 0x2b, 0x80, 0x92, 0x9b, 0x55, 0xe8, 0x53, 0xab, 0x37, 0x9a, 0x2d, 0x68, 0x37, 0x3a, 0xd0, 0xee, 0x2, 0x4b, 0xcf, 0x1, 0x13, 0x38, 0x7a, 0xb0, 0xb3, 0xbb, 0xc7, 0xe6, 0x80, 0x51, 0x5b, 0x52, 0xe8, 0xd3, 0xdd, 0xaf, 0x1d, 0x34, 0x6a, 0xed, 0x46, 0x77, 0x20, 0x70, 0x78, 0x4, 0x13, 0x1c, 0x1d, 0xc2, 0x71, 0x81, 0xc2, 0x58, 0x1, 0xfa, 0x44, 0x36, 0x8, 0xa0, 0x71, 0xa, 0xb0, 0x7f, 0xd6, 0x3e, 0x6f, 0xb6, 0xfb, 0x2, 0x17, 0x97, 0x30, 0xc1, 0xe5, 0x5, 0x18, 0xaf, 0x68, 0x9c, 0x15, 0x50, 0x5e, 0xf, 0x7a, 0xd8, 0x3e, 0x7, 0x80, 0x9b, 0x7a, 0xbb, 0x7b, 0x73, 0xde, 0x17, 0xb0, 0xfa, 0x60, 0x2, 0x9f, 0x15, 0xe0, 0x56, 0xf9, 0x5d, 0x80, 0xde, 0x1e, 0xc, 0xd1, 0xe9, 0xd7, 0x8, 0xea, 0x77, 0xed, 0x2e, 0xdc, 0xd7, 0x97, 0x8e, 0x80, 0xda, 0x3a, 0x86, 0x3e, 0xe7, 0x67, 0x70, 0xda, 0x6c, 0xb5, 0xbb, 0xd0, 0x6a, 0x2c, 0x5b, 0x3, 0x94, 0xdc, 0xad, 0x42, 0x9f, 0x76, 0xf3, 0xbc, 0x59, 0x87, 0xaf, 0xe1, 0x9f, 0x2f, 0xd5, 0x5, 0x76, 0xe, 0x1e, 0x60, 0xc0, 0xe9, 0x59, 0x7, 0xa0, 0xd6, 0x2, 0xe8, 0xb4, 0xe6, 0xcf, 0xc1, 0x83, 0x90, 0x40, 0x7e, 0x79, 0x12, 0xfb, 0xbb, 0x90, 0x59, 0x7d, 0x17, 0x1e, 0xd9, 0x5d, 0xf8, 0xd5, 0x6d, 0xec, 0xdf, 0x83, 0xa7, 0xe7, 0xf8, 0x2a, 0xf7, 0x20, 0xfe, 0xfc, 0xc2, 0xde, 0x83, 0xfe, 0x45, 0x62, 0x36, 0x5f, 0xdf, 0x60, 0x69, 0xde, 0x5e, 0x37, 0x19, 0xc5, 0xe8, 0x4d, 0xd3, 0x51, 0xc2, 0x2d, 0xb1, 0xe4, 0xd8, 0x8, 0xb, 0x31, 0x1e, 0x4b, 0xc4, 0x5b, 0x42, 0x4a, 0x87, 0x8e, 0x5f, 0xe5, 0x8, 0xbd, 0xb1, 0x5b, 0xb8, 0x7a, 0xe7, 0x2d, 0xe0, 0xfd, 0xaa, 0xb0, 0xbb, 0x41, 0x47, 0x70, 0x64, 0xf2, 0xab, 0x14, 0x89, 0xbd, 0xf, 0xe5, 0xe2, 0xbf, 0xa0, 0xfc, 0xd8, 0x23, 0x8a, 0x28, 0x32, 0xe3, 0x33, 0xe1, 0x4b, 0xc0, 0x7e, 0xa6, 0xff, 0x87, 0xcf, 0xb, 0x94, 0xb9, 0x37, 0x3c, 0xc6, 0xd8, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_on_disabled_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x69, 0x50, 0x4c, 0x54, 0x45, 0x93, 0x7f, 0x2b, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x15, 0x15, 0x18, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x69, 0x69, 0x6c, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x23, 0x23, 0x28, 0x1a, 0x1a, 0x1e, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0xd7, 0x77, 0xc6, 0x92, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xd1, 0x72, 0x83, 0x20, 0x10, 0x45, 0x4d, 0x90, 0xb8, 0x18, 0x96, 0x6c, 0xb5, 0x24, 0x41, 0x68, 0x9b, 0xf4, 0xff, 0x3f, 0xb2, 0x84, 0xb4, 0x8a, 0xa, 0x3a, 0x9d, 0x74, 0xa6, 0x2f, 0xb9, 0xbe, 0x38, 0xca, 0x3d, 0x5c, 0x16, 0xd8, 0xa2, 0x78, 0xea, 0x2f, 0xb5, 0xd9, 0xb2, 0xb2, 0xe4, 0x2b, 0x2a, 0x4b, 0xb6, 0xdd, 0x24, 0xed, 0xbb, 0x8a, 0x1, 0x8, 0x21, 0xee, 0xe3, 0xc4, 0x4d, 0x20, 0x6a, 0x91, 0x84, 0x54, 0xbb, 0xb9, 0x7f, 0xcf, 0x40, 0xa2, 0xea, 0x85, 0x32, 0x50, 0x6a, 0xc8, 0xe4, 0xd8, 0x4f, 0xfd, 0x7, 0x26, 0x48, 0xe1, 0x4b, 0x13, 0x7e, 0x37, 0x92, 0x50, 0x51, 0x1b, 0x92, 0xe4, 0x56, 0x72, 0x18, 0xfb, 0x5f, 0x99, 0x40, 0xd4, 0xf1, 0x8, 0x42, 0x6c, 0x17, 0x6b, 0x71, 0x1c, 0x1, 0x4e, 0x40, 0x21, 0x74, 0x24, 0x89, 0xd4, 0x2c, 0x11, 0x4e, 0xb1, 0xff, 0xc, 0x52, 0xe9, 0xe9, 0x8, 0x52, 0x8b, 0x11, 0xf8, 0x39, 0x2, 0x6c, 0x7d, 0x80, 0xf9, 0x8, 0xa4, 0xe1, 0xd5, 0x74, 0x16, 0xb9, 0xee, 0x5a, 0xae, 0xdd, 0xcf, 0xb7, 0xb7, 0x8, 0xe0, 0x2b, 0x40, 0x73, 0x40, 0x4, 0x75, 0x6, 0xa9, 0x43, 0xdd, 0xb9, 0x8, 0xc0, 0x46, 0x0, 0x25, 0xe7, 0x0, 0xa9, 0xfa, 0x0, 0x9d, 0xe7, 0x1b, 0xd4, 0xce, 0xea, 0x1, 0x50, 0x8e, 0x1, 0x89, 0x82, 0x35, 0x3d, 0x20, 0xb8, 0x94, 0xd1, 0x4e, 0xb9, 0x1, 0xc0, 0x7f, 0x91, 0x80, 0x42, 0x2, 0xe5, 0xcd, 0xd6, 0x24, 0x13, 0xbc, 0xc3, 0x5a, 0xd, 0x90, 0x93, 0xf5, 0x4b, 0xf0, 0x8b, 0x49, 0xd6, 0x60, 0x75, 0x17, 0xb4, 0x75, 0xd6, 0x84, 0x95, 0xb8, 0xe4, 0x2e, 0xac, 0x9f, 0x3, 0xba, 0x9d, 0x4b, 0xf4, 0xb3, 0xb4, 0xfd, 0x4c, 0xf1, 0x39, 0x28, 0x3e, 0xc4, 0x63, 0x27, 0xb1, 0x38, 0x3e, 0x7a, 0x17, 0xb2, 0xb7, 0x31, 0xeb, 0x9f, 0xdc, 0xc6, 0xa2, 0xb8, 0x30, 0x68, 0xa7, 0xfd, 0x60, 0xc1, 0x7f, 0x99, 0x77, 0x94, 0xeb, 0x27, 0xd4, 0x70, 0x6f, 0x48, 0xe2, 0x5b, 0xe0, 0x9f, 0xa4, 0xbf, 0xba, 0x66, 0x7b, 0x22, 0x5f, 0x55, 0xb6, 0x27, 0x3e, 0xf5, 0x8f, 0xfa, 0x2, 0xa0, 0x14, 0x20, 0xeb, 0xde, 0xb1, 0x8c, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_on_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x9b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1d, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x25, 0x2c, 0x36, 0x28, 0x4c, 0x6b, 0x29, 0x64, 0x92, 0x2a, 0x68, 0x99, 0x2a, 0x66, 0x95, 0x29, 0x5d, 0x85, 0x27, 0x49, 0x65, 0x25, 0x25, 0x28, 0x25, 0x27, 0x2d, 0x27, 0x44, 0x5c, 0x29, 0x60, 0x8c, 0x27, 0x4d, 0x6b, 0x25, 0x3a, 0x4c, 0x25, 0x2d, 0x38, 0x25, 0x26, 0x2c, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2d, 0x25, 0x30, 0x3e, 0x27, 0x49, 0x66, 0x29, 0x5f, 0x89, 0x27, 0x43, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x58, 0x7f, 0x25, 0x35, 0x45, 0x25, 0x34, 0x43, 0x28, 0x59, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x41, 0x57, 0x27, 0x40, 0x57, 0x25, 0x2b, 0x34, 0x25, 0x34, 0x44, 0x29, 0x5d, 0x87, 0x25, 0x2a, 0x33, 0x27, 0x43, 0x5b, 0x27, 0x4e, 0x6d, 0x27, 0x4d, 0x6c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x28, 0x56, 0x7b, 0x26, 0x3a, 0x4e, 0x26, 0x3b, 0x4e, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x27, 0x44, 0x5d, 0xa6, 0xa2, 0x25, 0x5b, 0x0, 0x0, 0x0, 0x4c, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x77, 0xfb, 0x8b, 0x5, 0x45, 0xde, 0xf6, 0x82, 0x9b, 0xf, 0x37, 0xc6, 0xe6, 0xe9, 0xfb, 0x4e, 0x50, 0x83, 0x9c, 0x78, 0x8c, 0x3c, 0x9c, 0xbb, 0x74, 0xda, 0x87, 0x53, 0x14, 0xd0, 0x92, 0x4e, 0x2c, 0x0, 0x0, 0x2, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0x63, 0x60, 0x18, 0x44, 0x80, 0x91, 0x89, 0x99, 0x99, 0x85, 0x20, 0x60, 0x66, 0x66, 0x62, 0xc4, 0xaa, 0x9d, 0x89, 0x85, 0x95, 0x8d, 0x9d, 0x83, 0x93, 0x8b, 0x0, 0xe0, 0xe4, 0x60, 0x67, 0x63, 0x65, 0x61, 0xc2, 0xb4, 0x9d, 0x85, 0x9b, 0x83, 0x87, 0x97, 0x8f, 0x5f, 0x40, 0x50, 0x8, 0x2f, 0x10, 0x14, 0xe0, 0xe7, 0xe3, 0xe5, 0xe1, 0xe0, 0x66, 0x41, 0x73, 0x5, 0x93, 0xb0, 0x88, 0xa8, 0x98, 0xb8, 0x84, 0xa4, 0x94, 0xb4, 0xf, 0x1, 0xe0, 0x2b, 0x2d, 0x23, 0x2b, 0x27, 0x26, 0x2a, 0x22, 0x8c, 0xe2, 0x8, 0x26, 0x79, 0x5, 0x45, 0x25, 0x65, 0x5f, 0xc, 0xd5, 0x7e, 0xfe, 0x7e, 0x58, 0xd, 0x51, 0x51, 0x55, 0x54, 0x90, 0x47, 0x32, 0x81, 0x51, 0x8d, 0x53, 0x5d, 0x43, 0xd3, 0x27, 0x20, 0x30, 0x28, 0x18, 0x2, 0x42, 0x42, 0xc3, 0x2, 0x20, 0x6, 0x84, 0xe3, 0x70, 0x87, 0x96, 0x3a, 0xa7, 0x1a, 0x23, 0xc2, 0xff, 0xec, 0xbc, 0xda, 0xd2, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x20, 0x90, 0x90, 0x98, 0x94, 0x9c, 0x12, 0x1, 0x36, 0x2, 0x97, 0x4f, 0x74, 0x78, 0xd9, 0xe1, 0xe1, 0xc0, 0xa4, 0xab, 0xa7, 0x6f, 0x10, 0x91, 0x9a, 0x96, 0x8e, 0xac, 0x22, 0x23, 0x33, 0x35, 0x2, 0x6f, 0x58, 0x18, 0xea, 0xe9, 0x42, 0x3d, 0xc1, 0x68, 0xa4, 0x60, 0x6c, 0x92, 0x95, 0x9d, 0x19, 0x8b, 0xaa, 0x22, 0x36, 0x33, 0x27, 0xb, 0x9f, 0x9, 0xa6, 0xc6, 0xa, 0x46, 0x10, 0x27, 0x30, 0xb1, 0xf2, 0x98, 0xf9, 0xe4, 0x26, 0xa7, 0xa3, 0xab, 0xc8, 0xcb, 0x2f, 0xc0, 0x1b, 0x1f, 0xe6, 0x3c, 0xac, 0x10, 0x3, 0x58, 0x2c, 0x2c, 0xad, 0x7c, 0xa, 0x8b, 0x30, 0x55, 0x14, 0x17, 0xc2, 0x99, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x3e, 0x95, 0x65, 0x55, 0x3e, 0x95, 0xd5, 0x30, 0x31, 0x6b, 0x4b, 0xb, 0x88, 0x1f, 0x84, 0xd9, 0x8d, 0x6d, 0x7c, 0x6a, 0x6a, 0x31, 0xd, 0xa8, 0xab, 0x81, 0x33, 0xab, 0xeb, 0x4b, 0x1a, 0xca, 0x4a, 0x2a, 0xcb, 0xaa, 0x91, 0xc, 0xb0, 0x35, 0x66, 0x67, 0x6, 0x1b, 0xa0, 0xc6, 0x61, 0xa7, 0xe9, 0xd3, 0xd8, 0x84, 0x69, 0x40, 0x53, 0x23, 0xdc, 0x1, 0x65, 0xcd, 0x3e, 0x3e, 0xf5, 0x2d, 0x95, 0xad, 0xe5, 0x95, 0x8, 0x3, 0x7c, 0xed, 0x38, 0x58, 0x20, 0x6, 0x28, 0xd8, 0xfb, 0xf8, 0x84, 0x24, 0x60, 0x1a, 0x90, 0x10, 0x2, 0x63, 0x55, 0xb6, 0x2, 0x89, 0xb6, 0xd2, 0xca, 0xea, 0xb6, 0x56, 0x84, 0x1, 0x3e, 0xf6, 0xa, 0x2c, 0xc4, 0xba, 0xa0, 0x19, 0xe4, 0x82, 0xd2, 0x76, 0xa0, 0xe6, 0xf2, 0x52, 0x4c, 0x17, 0x10, 0x13, 0x6, 0xad, 0x2d, 0x3e, 0xcd, 0xe5, 0x15, 0x40, 0x3, 0x2a, 0xca, 0x30, 0xc3, 0x80, 0x98, 0x58, 0xa8, 0x2c, 0x6f, 0x2d, 0x2f, 0x5, 0x7, 0x60, 0x2b, 0x66, 0x2c, 0x30, 0xb1, 0x8a, 0x3a, 0x10, 0x4c, 0x7, 0xcd, 0x2d, 0x55, 0xc0, 0xb0, 0xac, 0xf0, 0xf1, 0xa9, 0xaa, 0x80, 0x9, 0x39, 0x8a, 0x42, 0xd3, 0x1, 0xc5, 0x29, 0x11, 0x94, 0x17, 0x9c, 0x48, 0xcf, 0xb, 0xce, 0xf0, 0xbc, 0x0, 0xcb, 0x8d, 0x1d, 0xa8, 0xb9, 0x31, 0x12, 0xbf, 0x7e, 0xe4, 0xdc, 0x8, 0x2a, 0xf, 0x5c, 0x5c, 0xd, 0xb0, 0x94, 0x7, 0xb8, 0xf5, 0xbb, 0xb9, 0x20, 0x95, 0x7, 0xa0, 0x12, 0x89, 0x93, 0xd7, 0xdd, 0x3, 0x53, 0x9d, 0x5f, 0x38, 0xf6, 0xf2, 0x40, 0xc5, 0x93, 0x97, 0x53, 0x1e, 0xb5, 0x4c, 0x53, 0x63, 0x17, 0xe5, 0x13, 0x97, 0xb0, 0xd1, 0xf4, 0x21, 0x8, 0x7c, 0x6d, 0x65, 0xe5, 0xf8, 0x44, 0xd9, 0xd5, 0xd0, 0xa, 0x66, 0x46, 0x16, 0x5d, 0xe, 0x1e, 0x4b, 0x63, 0x3b, 0x2f, 0x21, 0x2, 0xc0, 0xcb, 0xce, 0xd8, 0x92, 0x87, 0x43, 0x97, 0x5, 0xa3, 0x6e, 0x60, 0x64, 0x32, 0x62, 0xb5, 0xf0, 0x56, 0x20, 0x5c, 0x2f, 0x28, 0x78, 0x5b, 0xb0, 0x1a, 0x61, 0xaf, 0x5b, 0x80, 0x35, 0x13, 0xe1, 0x8a, 0x9, 0x58, 0x35, 0xe1, 0xa8, 0x99, 0x86, 0x2e, 0x0, 0x0, 0x69, 0x2c, 0x6b, 0xc2, 0xf1, 0x2f, 0x53, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tooltip_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0xdd, 0xdd, 0xdd, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0xbc, 0x3, 0x4f, 0xe9, 0x0, 0x0, 0x0, 0xd, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xcc, 0xff, 0xb7, 0x4a, 0xbe, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x54, 0x76, 0x1, 0x2, 0x23, 0x1, 0x6, 0xd1, 0xf4, 0xe, 0x20, 0x28, 0xb, 0x64, 0xd0, 0x5c, 0x7d, 0x17, 0x8, 0x76, 0x4d, 0x62, 0x70, 0x7f, 0x7f, 0x6, 0x8, 0xfe, 0x95, 0x30, 0x78, 0xdc, 0x1, 0x31, 0xce, 0xb6, 0x50, 0xc8, 0x80, 0x1b, 0x8, 0xb7, 0x2, 0x6e, 0x29, 0xdc, 0x19, 0x0, 0xcf, 0x24, 0x4d, 0xb3, 0xd0, 0x4d, 0xb9, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tree_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tree_bg_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x94, 0xc8, 0x67, 0x6b, 0x60, 0xe6, 0x60, 0x64, 0x80, 0x80, 0xff, 0xc, 0x7f, 0x7f, 0xfc, 0x6a, 0x60, 0x94, 0xfb, 0xc0, 0xce, 0xcf, 0xc2, 0x80, 0x10, 0xfc, 0xc3, 0xf0, 0xf3, 0x23, 0xa3, 0xe2, 0x4f, 0xe, 0x36, 0x54, 0xc1, 0x1f, 0xbf, 0x18, 0x95, 0xbe, 0x73, 0x70, 0xb0, 0x30, 0xc0, 0x1, 0x48, 0xf0, 0x7, 0x85, 0x82, 0x58, 0x2d, 0xc2, 0xe6, 0xa4, 0x4f, 0x20, 0xc7, 0x37, 0x32, 0xb3, 0x23, 0x39, 0xfe, 0xfb, 0xaf, 0x46, 0x0, 0xee, 0x2a, 0x2f, 0xce, 0x4c, 0x47, 0x66, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tree_title_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x1, 0x3, 0x0, 0x0, 0x0, 0x25, 0x3d, 0x6d, 0x22, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x23, 0x25, 0x4c, 0x4a, 0x4e, 0x1, 0xf9, 0x98, 0x2e, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x0, 0x0, 0x0, 0x30, 0x0, 0x1, 0x6e, 0xa6, 0xf, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tree_title_pressed_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x1, 0x3, 0x0, 0x0, 0x0, 0x25, 0x3d, 0x6d, 0x22, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x36, 0x34, 0x36, 0x4c, 0x4a, 0x4e, 0x14, 0xd7, 0x5b, 0xf8, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x0, 0x0, 0x0, 0x30, 0x0, 0x1, 0x6e, 0xa6, 0xf, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char unchecked_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x23, 0xc3, 0x49, 0x39, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xa4, 0x7f, 0xe1, 0x5a, 0x0, 0x0, 0x0, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xb5, 0xcf, 0x45, 0x2, 0x80, 0x40, 0x8, 0x0, 0x40, 0x97, 0x66, 0xfb, 0xff, 0x9f, 0xb5, 0xdb, 0xb3, 0x73, 0xa4, 0x19, 0xbe, 0x2, 0x20, 0xf1, 0x8a, 0x10, 0xc2, 0x1c, 0x0, 0xd1, 0x94, 0x57, 0x49, 0x5, 0xe6, 0x0, 0x6a, 0xa9, 0x6d, 0x55, 0x8b, 0xe2, 0x1c, 0xa0, 0x54, 0xfb, 0xae, 0x26, 0x9a, 0x3, 0x9c, 0xdb, 0x11, 0x68, 0x99, 0xff, 0xa, 0x7c, 0xd6, 0xde, 0xf, 0x33, 0x9c, 0x3, 0xe0, 0x76, 0x9c, 0x1e, 0x1d, 0xbe, 0xcf, 0x7d, 0x4c, 0x93, 0xe2, 0x8, 0xa4, 0x66, 0x3c, 0xec, 0xed, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char unchecked_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x58, 0x58, 0x5b, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x27, 0xa1, 0xa6, 0x53, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xb4, 0xfa, 0xfa, 0xfb, 0xc7, 0x8b, 0xf6, 0x7e, 0x0, 0x0, 0x0, 0x52, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0xb5, 0xcf, 0x31, 0x12, 0xc0, 0x20, 0x8, 0x44, 0xd1, 0x15, 0x50, 0x83, 0x11, 0xe5, 0xfe, 0xa7, 0xcd, 0xe8, 0xc4, 0x22, 0xf4, 0x79, 0xe5, 0x36, 0x7c, 0x80, 0x8, 0x89, 0x58, 0xf2, 0x26, 0x4c, 0x9, 0x0, 0x15, 0xf5, 0xb9, 0xb9, 0x16, 0x2, 0xc0, 0x3a, 0xac, 0x6f, 0x36, 0x94, 0x1, 0x88, 0xdb, 0xfd, 0x32, 0x17, 0x0, 0x79, 0xf6, 0x33, 0xf4, 0x99, 0xff, 0x1a, 0xe2, 0xd9, 0x4f, 0x58, 0x5b, 0x61, 0x54, 0xdb, 0x49, 0xbf, 0xea, 0x4a, 0x8f, 0xcf, 0x45, 0xf, 0x4, 0x40, 0x7, 0x90, 0xb0, 0x7b, 0x47, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char updown_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0x81, 0x83, 0xf6, 0xf6, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x8d, 0x4e, 0xb5, 0x1, 0xc0, 0x30, 0xc, 0xf3, 0x15, 0xfe, 0xff, 0x96, 0x64, 0xa, 0x6c, 0xca, 0x56, 0xd2, 0x25, 0x65, 0xe6, 0xc8, 0x8b, 0x49, 0x20, 0x79, 0x28, 0x95, 0x81, 0xa1, 0xd4, 0x7d, 0x4, 0xbb, 0xa1, 0x50, 0xea, 0x3c, 0xa6, 0x71, 0x98, 0x96, 0x69, 0x58, 0x31, 0xcc, 0xb7, 0xe5, 0x2f, 0x48, 0x63, 0x26, 0xf6, 0xa2, 0xd4, 0x18, 0xf9, 0x7, 0x2d, 0xe3, 0x46, 0x89, 0xb4, 0xd2, 0xf8, 0xa3, 0x68, 0xe3, 0xd7, 0x14, 0x20, 0xe6, 0xc3, 0x3d, 0xd8, 0xca, 0x5e, 0x94, 0x32, 0xd0, 0x3, 0x91, 0xba, 0x5f, 0x1b, 0x4a, 0x9b, 0x12, 0x62, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vseparator_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x2, 0x3, 0x0, 0x0, 0x0, 0xb9, 0x61, 0x56, 0x18, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x73, 0x9b, 0xaa, 0xce, 0xdc, 0xe1, 0xeb, 0x64, 0x9a, 0x78, 0x0, 0x0, 0x0, 0x3, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xb3, 0xb3, 0x67, 0xf6, 0xdb, 0x93, 0x0, 0x0, 0x0, 0xf, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x80, 0x83, 0xac, 0x95, 0xc, 0x48, 0x0, 0x0, 0xe, 0x79, 0x1, 0x14, 0x17, 0x9a, 0x55, 0x26, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x51, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x34, 0x33, 0x3a, 0x2d, 0x2c, 0x32, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x40, 0x3e, 0x4a, 0x20, 0x20, 0x24, 0x34, 0x33, 0x3a, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x1e, 0x1e, 0x23, 0x23, 0x23, 0x27, 0x2d, 0x2c, 0x32, 0x1f, 0x1f, 0x23, 0x30, 0x7, 0x9c, 0xfe, 0x0, 0x0, 0x0, 0x13, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x4, 0x1a, 0x40, 0x5d, 0x19, 0x28, 0x96, 0xf0, 0xfd, 0x94, 0x95, 0xfc, 0x93, 0xfc, 0xc0, 0x0, 0xb4, 0xa, 0x5f, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x2c, 0xc8, 0x83, 0x11, 0x0, 0x30, 0x0, 0xc0, 0xc0, 0xda, 0xf6, 0xfe, 0x8b, 0xd6, 0x39, 0xe6, 0xc1, 0xe, 0x12, 0xca, 0x38, 0x67, 0x82, 0xc0, 0x73, 0xe7, 0xa5, 0xd2, 0xc6, 0x68, 0x2b, 0xbf, 0x40, 0xe1, 0x7c, 0x2e, 0x25, 0xfb, 0x20, 0x3e, 0x30, 0x9d, 0x6b, 0x6b, 0x35, 0x6b, 0xf6, 0x81, 0x9b, 0xd2, 0x76, 0x25, 0xf2, 0x7, 0x28, 0xf5, 0x71, 0x60, 0xf4, 0x84, 0x2e, 0xe0, 0x35, 0x49, 0x29, 0x51, 0x90, 0x80, 0xa8, 0x94, 0x24, 0x33, 0x19, 0x2, 0x18, 0x86, 0xa2, 0x5b, 0x8b, 0xe9, 0x30, 0x4c, 0xa7, 0x63, 0x7a, 0xe, 0xc3, 0xfb, 0x0, 0x89, 0x5c, 0xa, 0x6b, 0x4f, 0x78, 0xac, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_grabber_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xbc, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x17, 0x60, 0x66, 0xe0, 0x65, 0x90, 0x61, 0x50, 0x67, 0xd0, 0x60, 0x50, 0x62, 0x10, 0x67, 0xe0, 0x2, 0xf2, 0x19, 0x51, 0xa5, 0xc5, 0xd2, 0xf2, 0x3a, 0xf, 0xce, 0x78, 0x3f, 0xed, 0x43, 0xff, 0xed, 0xc6, 0xf5, 0x41, 0xa9, 0x8a, 0x86, 0xc, 0x22, 0xc, 0xac, 0x8, 0x5, 0xbc, 0x69, 0x79, 0x93, 0x5e, 0xf4, 0xfd, 0x6f, 0x7, 0xc2, 0xae, 0xff, 0xfd, 0xff, 0xa7, 0xfd, 0xe9, 0x7f, 0x50, 0x31, 0x87, 0x47, 0x87, 0x81, 0x1b, 0x66, 0x8e, 0x4c, 0xe7, 0xc1, 0xbe, 0xff, 0x2d, 0xff, 0x9b, 0xa1, 0xb0, 0xf5, 0x7f, 0xe7, 0xff, 0xc9, 0xff, 0x27, 0xdc, 0xb6, 0xf0, 0x7, 0x9a, 0xc3, 0x2, 0x52, 0xa0, 0x3e, 0xe3, 0x7d, 0x3b, 0x54, 0x12, 0xa1, 0xa8, 0xe7, 0xff, 0x9c, 0x2f, 0x45, 0xc5, 0xc, 0xdc, 0xf8, 0x14, 0x7c, 0xaf, 0xaa, 0x65, 0xe0, 0xc1, 0x69, 0xc5, 0xc4, 0x3b, 0xe, 0xa1, 0xc, 0x62, 0xc, 0x2c, 0x68, 0x8e, 0xec, 0xf8, 0xdf, 0xfb, 0x7f, 0xda, 0xbf, 0x89, 0xf, 0x4a, 0xa6, 0xf2, 0xe8, 0x0, 0x75, 0x33, 0xa2, 0x79, 0x73, 0xea, 0x87, 0xbe, 0x7b, 0xbd, 0x47, 0x7d, 0x53, 0x58, 0x34, 0x18, 0x84, 0x19, 0x58, 0xb1, 0x7, 0x94, 0xa, 0x83, 0x14, 0x50, 0x27, 0x13, 0x3, 0x3d, 0x1, 0x0, 0x79, 0xc3, 0x79, 0x54, 0x19, 0x56, 0x3b, 0x28, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_grabber_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xb4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x17, 0x60, 0x66, 0xe0, 0x65, 0x90, 0x61, 0x50, 0x67, 0xd0, 0x60, 0x50, 0x62, 0x10, 0x67, 0xe0, 0x2, 0xf2, 0x19, 0x51, 0xa5, 0xc5, 0x8c, 0xf3, 0x5c, 0xf, 0xfa, 0xbc, 0xf7, 0xfe, 0xe0, 0x71, 0xdb, 0x71, 0xbd, 0x66, 0xaa, 0xa0, 0x21, 0x83, 0x8, 0x3, 0x2b, 0x42, 0x1, 0xaf, 0x71, 0x9e, 0xe7, 0xb, 0xf7, 0xff, 0x2e, 0x40, 0xe8, 0xfa, 0xdf, 0xe3, 0xbf, 0xf7, 0x1f, 0x8f, 0x7, 0x36, 0x73, 0xd8, 0x74, 0x18, 0xb8, 0x61, 0xe6, 0xc8, 0xb8, 0x1e, 0x74, 0xff, 0xef, 0x4, 0x87, 0xce, 0x40, 0x65, 0x5e, 0xff, 0x3d, 0x6e, 0xcb, 0xf8, 0x3, 0xcd, 0x61, 0x1, 0x29, 0x50, 0xf7, 0x79, 0xef, 0x2, 0x95, 0x44, 0x28, 0x72, 0xfb, 0xef, 0xf7, 0xc5, 0xb2, 0x98, 0x81, 0x1b, 0x9f, 0x82, 0xef, 0xb6, 0xb5, 0xc, 0x3c, 0x38, 0xad, 0xf0, 0xbc, 0xa3, 0x10, 0xca, 0x20, 0xc6, 0xc0, 0x82, 0xec, 0x48, 0x30, 0x74, 0xff, 0xef, 0xfd, 0xcf, 0xf3, 0x81, 0xd5, 0x54, 0x36, 0x1d, 0xa0, 0x6e, 0x46, 0xc, 0x6f, 0xba, 0xdf, 0x73, 0x3f, 0xaa, 0x9e, 0xc2, 0xa4, 0xc1, 0x20, 0xcc, 0xc0, 0x8a, 0x3d, 0xa0, 0x54, 0x18, 0xa4, 0x80, 0x3a, 0x99, 0x18, 0xe8, 0x9, 0x0, 0xf1, 0x9, 0x63, 0x9b, 0x53, 0x7f, 0x6d, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_grabber_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x6c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x5b, 0xa6, 0xa5, 0x61, 0xb3, 0xbc, 0x63, 0xb7, 0xc8, 0x65, 0xbb, 0xca, 0x60, 0xaf, 0xb1, 0x55, 0x9b, 0x9a, 0x60, 0xb2, 0xbd, 0x5e, 0xb1, 0xcd, 0x61, 0xb3, 0xc2, 0x62, 0xb4, 0xbd, 0x68, 0xc0, 0xcf, 0x68, 0xc1, 0xcf, 0x63, 0xb7, 0xbf, 0x52, 0x96, 0x95, 0x62, 0xb3, 0xbf, 0x5e, 0xb0, 0xcd, 0x63, 0xb4, 0xb6, 0x60, 0xb1, 0xbc, 0x63, 0xb7, 0xc7, 0x55, 0xa3, 0xc8, 0x4f, 0x98, 0xc4, 0x4b, 0x93, 0xc2, 0x4c, 0x94, 0xc2, 0x54, 0xa2, 0xc8, 0x5a, 0xab, 0xcb, 0x4e, 0x97, 0xc4, 0x49, 0x8f, 0xc0, 0x47, 0x8c, 0xbf, 0x48, 0x8e, 0xc0, 0x52, 0x9e, 0xc6, 0x51, 0x9d, 0xc6, 0x5a, 0xac, 0xcc, 0x53, 0x9f, 0xc7, 0x4d, 0x96, 0xc3, 0x4b, 0x92, 0xc2, 0x7f, 0xcb, 0x5d, 0x16, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x47, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0x60, 0x65, 0x61, 0x66, 0x62, 0x44, 0xe2, 0x73, 0x4a, 0x88, 0x8b, 0x89, 0x8a, 0x70, 0xb0, 0xb3, 0xc1, 0xe4, 0x25, 0x64, 0x65, 0x80, 0x40, 0x5a, 0x4a, 0x92, 0xb, 0x22, 0xc0, 0x22, 0x2e, 0x3, 0x1, 0xd2, 0x72, 0xdc, 0x68, 0x2, 0xf2, 0x3c, 0xa8, 0x5a, 0x14, 0x78, 0x11, 0x86, 0x2a, 0x2b, 0x29, 0xa, 0xf0, 0xf3, 0x21, 0x59, 0x2b, 0x2c, 0x24, 0xc8, 0x40, 0x3d, 0x0, 0x0, 0x19, 0x8b, 0x5, 0xfc, 0x96, 0x5c, 0x15, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_tick_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x4, 0x4, 0x3, 0x0, 0x0, 0x0, 0x75, 0x9a, 0xa2, 0xdf, 0x0, 0x0, 0x0, 0x1b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x81, 0xa2, 0xad, 0x8c, 0xac, 0xb8, 0x38, 0x55, 0x5f, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x98, 0x98, 0x98, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x7c, 0xda, 0x48, 0x6d, 0x0, 0x0, 0x0, 0x9, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x79, 0x79, 0x79, 0x31, 0x1c, 0x18, 0xed, 0xfe, 0x2b, 0x0, 0x0, 0x0, 0x1c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x80, 0x3, 0xd7, 0x44, 0x5, 0x6, 0x6, 0xe6, 0x92, 0x30, 0x86, 0xf2, 0x62, 0x3, 0x20, 0xa3, 0xbd, 0x1c, 0x2e, 0x3, 0x0, 0x3f, 0xce, 0x3, 0xaf, 0xed, 0xed, 0x7c, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vsplit_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0xff, 0xff, 0xff, 0x11, 0xab, 0xb9, 0xf3, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vsplitter_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0x6c, 0x9, 0xa6, 0x3, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x18, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x1c, 0x2c, 0x48, 0xa5, 0xc, 0x33, 0xa4, 0x5a, 0x53, 0x86, 0x29, 0x7, 0xa3, 0x61, 0x0, 0x0, 0x18, 0x61, 0x34, 0xa1, 0xba, 0xa4, 0x4d, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char window_resizer_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0xbc, 0xe0, 0x45, 0x3f, 0x1, 0xe9, 0xec, 0xfe, 0x81, 0x94, 0x86, 0xb1, 0x70, 0x48, 0x23, 0x58, 0x84, 0xa4, 0x7, 0x15, 0x0, 0x0, 0xed, 0x9f, 0x18, 0xe8, 0xcd, 0x91, 0xd8, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char window_resizer_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x27, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x44, 0xe0, 0x45, 0x3f, 0x76, 0x71, 0x26, 0x18, 0xa3, 0x19, 0xa7, 0x12, 0x38, 0xc8, 0xee, 0xa7, 0xb1, 0x12, 0x98, 0x4, 0x4e, 0x25, 0x8, 0x9, 0x4a, 0x94, 0xc, 0x10, 0x0, 0x0, 0x9d, 0x84, 0x18, 0x73, 0x33, 0x1c, 0x96, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-// shaders block
diff --git a/scene/resources/default_theme/toggle_off.png b/scene/resources/default_theme/toggle_off.png
deleted file mode 100644
index 64b51c8c9d..0000000000
--- a/scene/resources/default_theme/toggle_off.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off.svg b/scene/resources/default_theme/toggle_off.svg
new file mode 100644
index 0000000000..95ec679cbe
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".5" stroke-width=".999997"><rect fill="#1a1a1a" height="14" rx="7.000001" width="30" x="1" y="1"/><circle cx="8" cy="8" fill="#fff" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_off_disabled.png b/scene/resources/default_theme/toggle_off_disabled.png
deleted file mode 100644
index 250cd29b66..0000000000
--- a/scene/resources/default_theme/toggle_off_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off_disabled.svg b/scene/resources/default_theme/toggle_off_disabled.svg
new file mode 100644
index 0000000000..c86a678768
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".25" stroke-width=".999997"><rect fill="#1a1a1a" height="14" rx="7.000001" width="30" x="1" y="1"/><circle cx="8" cy="8" fill="#fff" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_off_disabled_mirrored.png b/scene/resources/default_theme/toggle_off_disabled_mirrored.png
deleted file mode 100644
index 799b63c098..0000000000
--- a/scene/resources/default_theme/toggle_off_disabled_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off_disabled_mirrored.svg b/scene/resources/default_theme/toggle_off_disabled_mirrored.svg
new file mode 100644
index 0000000000..0182dd738e
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_disabled_mirrored.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".25" stroke-width=".999997" transform="scale(-1 1)"><rect fill="#1a1a1a" height="14" rx="7.000001" width="30" x="-31" y="1"/><circle cx="-24" cy="8" fill="#fff" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_off_mirrored.png b/scene/resources/default_theme/toggle_off_mirrored.png
deleted file mode 100644
index 3487605d58..0000000000
--- a/scene/resources/default_theme/toggle_off_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off_mirrored.svg b/scene/resources/default_theme/toggle_off_mirrored.svg
new file mode 100644
index 0000000000..f61517a4ae
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_mirrored.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".5" stroke-width=".999997" transform="scale(-1 1)"><rect fill="#1a1a1a" height="14" rx="7.000001" width="30" x="-31" y="1"/><circle cx="-24" cy="8" fill="#fff" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_on.png b/scene/resources/default_theme/toggle_on.png
deleted file mode 100644
index f0c699c181..0000000000
--- a/scene/resources/default_theme/toggle_on.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on.svg b/scene/resources/default_theme/toggle_on.svg
new file mode 100644
index 0000000000..7e02602792
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".999997" transform="scale(-1 1)"><rect fill="#fff" fill-opacity=".75" height="14" rx="7.000001" width="30" x="-31" y="1"/><circle cx="-24" cy="8" fill="#1a1a1a" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_on_disabled.png b/scene/resources/default_theme/toggle_on_disabled.png
deleted file mode 100644
index b1dacbaf32..0000000000
--- a/scene/resources/default_theme/toggle_on_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_disabled.svg b/scene/resources/default_theme/toggle_on_disabled.svg
new file mode 100644
index 0000000000..72ce5bb9b2
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".999997" transform="scale(-1 1)"><rect fill="#fff" fill-opacity=".37" height="14" rx="7.000001" width="30" x="-31" y="1"/><circle cx="-24" cy="8" fill="#1a1a1a" fill-opacity=".5" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_on_disabled_mirrored.png b/scene/resources/default_theme/toggle_on_disabled_mirrored.png
deleted file mode 100644
index 0758babd4f..0000000000
--- a/scene/resources/default_theme/toggle_on_disabled_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_disabled_mirrored.svg b/scene/resources/default_theme/toggle_on_disabled_mirrored.svg
new file mode 100644
index 0000000000..340a386def
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_disabled_mirrored.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".999997"><rect fill="#fff" fill-opacity=".37" height="14" rx="7.000001" width="30" x="1" y="1"/><circle cx="8" cy="8" fill="#1a1a1a" fill-opacity=".5" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_on_mirrored.png b/scene/resources/default_theme/toggle_on_mirrored.png
deleted file mode 100644
index 3fd953c8e2..0000000000
--- a/scene/resources/default_theme/toggle_on_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_mirrored.svg b/scene/resources/default_theme/toggle_on_mirrored.svg
new file mode 100644
index 0000000000..fbc6be2af9
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_mirrored.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".999997"><rect fill="#fff" fill-opacity=".75" height="14" rx="7.000001" width="30" x="1" y="1"/><circle cx="8" cy="8" fill="#1a1a1a" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/tooltip_bg.png b/scene/resources/default_theme/tooltip_bg.png
deleted file mode 100644
index 07b7d942ca..0000000000
--- a/scene/resources/default_theme/tooltip_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg.png b/scene/resources/default_theme/tree_bg.png
deleted file mode 100644
index 2b0c506f34..0000000000
--- a/scene/resources/default_theme/tree_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg_disabled.png b/scene/resources/default_theme/tree_bg_disabled.png
deleted file mode 100644
index 69d78febd9..0000000000
--- a/scene/resources/default_theme/tree_bg_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tree_title.png b/scene/resources/default_theme/tree_title.png
deleted file mode 100644
index e5f3f49695..0000000000
--- a/scene/resources/default_theme/tree_title.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tree_title_pressed.png b/scene/resources/default_theme/tree_title_pressed.png
deleted file mode 100644
index 35e2bb3008..0000000000
--- a/scene/resources/default_theme/tree_title_pressed.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/unchecked.png b/scene/resources/default_theme/unchecked.png
deleted file mode 100644
index 8c818af755..0000000000
--- a/scene/resources/default_theme/unchecked.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/unchecked.svg b/scene/resources/default_theme/unchecked.svg
new file mode 100644
index 0000000000..f475108f44
--- /dev/null
+++ b/scene/resources/default_theme/unchecked.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#1a1a1a" fill-opacity=".5" stroke-width="1.16667"/></svg>
diff --git a/scene/resources/default_theme/unchecked_disabled.png b/scene/resources/default_theme/unchecked_disabled.png
deleted file mode 100644
index bef9316f58..0000000000
--- a/scene/resources/default_theme/unchecked_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/unchecked_disabled.svg b/scene/resources/default_theme/unchecked_disabled.svg
new file mode 100644
index 0000000000..195b85e37f
--- /dev/null
+++ b/scene/resources/default_theme/unchecked_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#1a1a1a" fill-opacity=".25" stroke-width="1.16667"/></svg>
diff --git a/scene/resources/default_theme/updown.png b/scene/resources/default_theme/updown.png
deleted file mode 100644
index 56f81921e8..0000000000
--- a/scene/resources/default_theme/updown.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/updown.svg b/scene/resources/default_theme/updown.svg
new file mode 100644
index 0000000000..d6a630740f
--- /dev/null
+++ b/scene/resources/default_theme/updown.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-opacity=".75" stroke-width="2"><path d="m4 6 4-3.5 4 3.5"/><path d="m12 10-4 3.5-4-3.5"/></g></svg>
diff --git a/scene/resources/default_theme/visibility_visible.svg b/scene/resources/default_theme/visibility_visible.svg
new file mode 100644
index 0000000000..ac1af8db0a
--- /dev/null
+++ b/scene/resources/default_theme/visibility_visible.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2c-2.557 0-5.791 1.948-6.955 5.705-.057.186-.059.384-.006.57 1.124 3.936 4.461 5.725 6.961 5.725s5.836-1.789 6.961-5.725c.052-.18.052-.372 0-.552-1.1-3.788-4.407-5.723-6.961-5.723zm0 2c2.194 0 4 1.806 4 4s-1.806 4-4 4-4-1.806-4-4 1.806-4 4-4zm0 2c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2z" fill="#b2b2b2"/></svg>
diff --git a/scene/resources/default_theme/vseparator.png b/scene/resources/default_theme/vseparator.png
deleted file mode 100644
index 51e79f3ac5..0000000000
--- a/scene/resources/default_theme/vseparator.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_bg.png b/scene/resources/default_theme/vslider_bg.png
deleted file mode 100644
index ba3244e3e5..0000000000
--- a/scene/resources/default_theme/vslider_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber.png b/scene/resources/default_theme/vslider_grabber.png
deleted file mode 100644
index 6c6bf93e68..0000000000
--- a/scene/resources/default_theme/vslider_grabber.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber_disabled.png b/scene/resources/default_theme/vslider_grabber_disabled.png
deleted file mode 100644
index 49cced5055..0000000000
--- a/scene/resources/default_theme/vslider_grabber_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber_hl.png b/scene/resources/default_theme/vslider_grabber_hl.png
deleted file mode 100644
index 28774fdbf8..0000000000
--- a/scene/resources/default_theme/vslider_grabber_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_tick.png b/scene/resources/default_theme/vslider_tick.png
deleted file mode 100644
index bde788b563..0000000000
--- a/scene/resources/default_theme/vslider_tick.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_tick.svg b/scene/resources/default_theme/vslider_tick.svg
new file mode 100644
index 0000000000..1365d36aca
--- /dev/null
+++ b/scene/resources/default_theme/vslider_tick.svg
@@ -0,0 +1 @@
+<svg height="4" viewBox="0 0 16 3.9999998" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m-3 0h2v16h-2z" fill="#fff" fill-opacity=".25" stroke-width=".285079" transform="rotate(-90)"/></svg>
diff --git a/scene/resources/default_theme/vsplit_bg.png b/scene/resources/default_theme/vsplit_bg.png
deleted file mode 100644
index a5749f6d5c..0000000000
--- a/scene/resources/default_theme/vsplit_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vsplitter.png b/scene/resources/default_theme/vsplitter.png
deleted file mode 100644
index dde1f390df..0000000000
--- a/scene/resources/default_theme/vsplitter.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vsplitter.svg b/scene/resources/default_theme/vsplitter.svg
new file mode 100644
index 0000000000..c46d785925
--- /dev/null
+++ b/scene/resources/default_theme/vsplitter.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" viewBox="0 0 48 8" xmlns="http://www.w3.org/2000/svg"><path d="m4 4h40" fill="none" stroke="#808080" stroke-opacity=".65" stroke-width="1.7"/></svg>
diff --git a/scene/resources/default_theme/window_resizer.png b/scene/resources/default_theme/window_resizer.png
deleted file mode 100644
index b06e6f5366..0000000000
--- a/scene/resources/default_theme/window_resizer.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/window_resizer_mirrored.png b/scene/resources/default_theme/window_resizer_mirrored.png
deleted file mode 100644
index bbce5f1406..0000000000
--- a/scene/resources/default_theme/window_resizer_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/zoom_less.svg b/scene/resources/default_theme/zoom_less.svg
new file mode 100644
index 0000000000..a438f7f683
--- /dev/null
+++ b/scene/resources/default_theme/zoom_less.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#b2b2b2" fill-opacity=".65" r="8"/><circle cx="8" cy="8" fill="#fefffe" r="7"/><path d="m4 7h8v2h-8z" fill="#010001"/></svg>
diff --git a/scene/resources/default_theme/zoom_more.svg b/scene/resources/default_theme/zoom_more.svg
new file mode 100644
index 0000000000..192e0ed907
--- /dev/null
+++ b/scene/resources/default_theme/zoom_more.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#b2b2b2" fill-opacity=".65" r="8"/><circle cx="8" cy="8" fill="#fefffe" r="7"/><path d="m7 4h2v3h3v2h-3v3h-2v-3h-3v-2h3z" fill="#010001"/></svg>
diff --git a/scene/resources/default_theme/zoom_reset.svg b/scene/resources/default_theme/zoom_reset.svg
new file mode 100644
index 0000000000..427214cda2
--- /dev/null
+++ b/scene/resources/default_theme/zoom_reset.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#b2b2b2" fill-opacity=".65" r="8"/><circle cx="8" cy="8" fill="#fefffe" r="7"/><path d="m8.016 4.002h.029c.548 0 .999.45 1 .998v7h-2v-5.131l-1.445.963-1.11-1.664 3-2c.156-.103.338-.161.526-.166z" fill="#010001"/></svg>
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 4c25ed589b..5d1e07f6cd 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -155,7 +155,9 @@ float Environment::get_ambient_light_energy() const {
}
void Environment::set_ambient_light_sky_contribution(float p_ratio) {
- ambient_sky_contribution = p_ratio;
+ // Sky contribution values outside the [0.0; 1.0] range don't make sense and
+ // can result in negative colors.
+ ambient_sky_contribution = CLAMP(p_ratio, 0.0, 1.0);
_update_ambient_light();
}
@@ -187,6 +189,7 @@ void Environment::_update_ambient_light() {
void Environment::set_tonemapper(ToneMapper p_tone_mapper) {
tone_mapper = p_tone_mapper;
_update_tonemap();
+ notify_property_list_changed();
}
Environment::ToneMapper Environment::get_tonemapper() const {
@@ -426,6 +429,64 @@ void Environment::_update_ssao() {
ssao_ao_channel_affect);
}
+// SSIL
+
+void Environment::set_ssil_enabled(bool p_enabled) {
+ ssil_enabled = p_enabled;
+ _update_ssil();
+ notify_property_list_changed();
+}
+
+bool Environment::is_ssil_enabled() const {
+ return ssil_enabled;
+}
+
+void Environment::set_ssil_radius(float p_radius) {
+ ssil_radius = p_radius;
+ _update_ssil();
+}
+
+float Environment::get_ssil_radius() const {
+ return ssil_radius;
+}
+
+void Environment::set_ssil_intensity(float p_intensity) {
+ ssil_intensity = p_intensity;
+ _update_ssil();
+}
+
+float Environment::get_ssil_intensity() const {
+ return ssil_intensity;
+}
+
+void Environment::set_ssil_sharpness(float p_sharpness) {
+ ssil_sharpness = p_sharpness;
+ _update_ssil();
+}
+
+float Environment::get_ssil_sharpness() const {
+ return ssil_sharpness;
+}
+
+void Environment::set_ssil_normal_rejection(float p_normal_rejection) {
+ ssil_normal_rejection = p_normal_rejection;
+ _update_ssil();
+}
+
+float Environment::get_ssil_normal_rejection() const {
+ return ssil_normal_rejection;
+}
+
+void Environment::_update_ssil() {
+ RS::get_singleton()->environment_set_ssil(
+ environment,
+ ssil_enabled,
+ ssil_radius,
+ ssil_intensity,
+ ssil_sharpness,
+ ssil_normal_rejection);
+}
+
// SDFGI
void Environment::set_sdfgi_enabled(bool p_enabled) {
@@ -438,13 +499,13 @@ bool Environment::is_sdfgi_enabled() const {
return sdfgi_enabled;
}
-void Environment::set_sdfgi_cascades(SDFGICascades p_cascades) {
- ERR_FAIL_INDEX(p_cascades, SDFGI_CASCADES_8 + 1);
+void Environment::set_sdfgi_cascades(int p_cascades) {
+ ERR_FAIL_COND_MSG(p_cascades < 1 || p_cascades > 8, "Invalid number of SDFGI cascades (must be between 1 and 8).");
sdfgi_cascades = p_cascades;
_update_sdfgi();
}
-Environment::SDFGICascades Environment::get_sdfgi_cascades() const {
+int Environment::get_sdfgi_cascades() const {
return sdfgi_cascades;
}
@@ -459,9 +520,7 @@ float Environment::get_sdfgi_min_cell_size() const {
void Environment::set_sdfgi_max_distance(float p_distance) {
p_distance /= 64.0;
- int cc[3] = { 4, 6, 8 };
- int cascades = cc[sdfgi_cascades];
- for (int i = 0; i < cascades; i++) {
+ for (int i = 0; i < sdfgi_cascades; i++) {
p_distance *= 0.5; //halve for each cascade
}
sdfgi_min_cell_size = p_distance;
@@ -471,9 +530,7 @@ void Environment::set_sdfgi_max_distance(float p_distance) {
float Environment::get_sdfgi_max_distance() const {
float md = sdfgi_min_cell_size;
md *= 64.0;
- int cc[3] = { 4, 6, 8 };
- int cascades = cc[sdfgi_cascades];
- for (int i = 0; i < cascades; i++) {
+ for (int i = 0; i < sdfgi_cascades; i++) {
md *= 2.0;
}
return md;
@@ -554,7 +611,7 @@ void Environment::_update_sdfgi() {
RS::get_singleton()->environment_set_sdfgi(
environment,
sdfgi_enabled,
- RS::EnvironmentSDFGICascades(sdfgi_cascades),
+ sdfgi_cascades,
sdfgi_min_cell_size,
RS::EnvironmentSDFGIYScale(sdfgi_y_scale),
sdfgi_use_occlusion,
@@ -674,6 +731,24 @@ float Environment::get_glow_hdr_luminance_cap() const {
return glow_hdr_luminance_cap;
}
+void Environment::set_glow_map_strength(float p_strength) {
+ glow_map_strength = p_strength;
+ _update_glow();
+}
+
+float Environment::get_glow_map_strength() const {
+ return glow_map_strength;
+}
+
+void Environment::set_glow_map(Ref<Texture> p_glow_map) {
+ glow_map = p_glow_map;
+ _update_glow();
+}
+
+Ref<Texture> Environment::get_glow_map() const {
+ return glow_map;
+}
+
void Environment::_update_glow() {
Vector<float> normalized_levels;
if (glow_normalize_levels) {
@@ -689,6 +764,15 @@ void Environment::_update_glow() {
normalized_levels = glow_levels;
}
+ float _glow_map_strength = 0.0f;
+ RID glow_map_rid;
+ if (glow_map.is_valid()) {
+ glow_map_rid = glow_map->get_rid();
+ _glow_map_strength = glow_map_strength;
+ } else {
+ glow_map_rid = RID();
+ }
+
RS::get_singleton()->environment_set_glow(
environment,
glow_enabled,
@@ -700,7 +784,9 @@ void Environment::_update_glow() {
RS::EnvironmentGlowBlendMode(glow_blend_mode),
glow_hdr_bleed_threshold,
glow_hdr_bleed_scale,
- glow_hdr_luminance_cap);
+ glow_hdr_luminance_cap,
+ _glow_map_strength,
+ glow_map_rid);
}
// Fog
@@ -964,6 +1050,10 @@ void Environment::_validate_property(PropertyInfo &property) const {
}
}
+ if (property.name == "tonemap_white" && tone_mapper == TONE_MAPPER_LINEAR) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+
if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) {
property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
@@ -994,8 +1084,9 @@ void Environment::_validate_property(PropertyInfo &property) const {
"fog_",
"volumetric_fog_",
"auto_exposure_",
- "ss_reflections_",
+ "ssr_",
"ssao_",
+ "ssil_",
"sdfgi_",
"glow_",
"adjustment_",
@@ -1005,8 +1096,7 @@ void Environment::_validate_property(PropertyInfo &property) const {
static const char *high_end_prefixes[] = {
"auto_exposure_",
- "tonemap_",
- "ss_reflections_",
+ "ssr_",
"ssao_",
nullptr
@@ -1156,15 +1246,14 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ssr_depth_tolerance", "depth_tolerance"), &Environment::set_ssr_depth_tolerance);
ClassDB::bind_method(D_METHOD("get_ssr_depth_tolerance"), &Environment::get_ssr_depth_tolerance);
- ADD_GROUP("SS Reflections", "ss_reflections_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ss_reflections_enabled"), "set_ssr_enabled", "is_ssr_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "ss_reflections_max_steps", PROPERTY_HINT_RANGE, "1,512,1"), "set_ssr_max_steps", "get_ssr_max_steps");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ss_reflections_fade_in", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_in", "get_ssr_fade_in");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ss_reflections_fade_out", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_out", "get_ssr_fade_out");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ss_reflections_depth_tolerance", PROPERTY_HINT_RANGE, "0.01,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance");
+ ADD_GROUP("SSR", "ssr_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssr_enabled"), "set_ssr_enabled", "is_ssr_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "ssr_max_steps", PROPERTY_HINT_RANGE, "1,512,1"), "set_ssr_max_steps", "get_ssr_max_steps");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_in", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_in", "get_ssr_fade_in");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_out", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_out", "get_ssr_fade_out");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_depth_tolerance", PROPERTY_HINT_RANGE, "0.01,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance");
// SSAO
-
ClassDB::bind_method(D_METHOD("set_ssao_enabled", "enabled"), &Environment::set_ssao_enabled);
ClassDB::bind_method(D_METHOD("is_ssao_enabled"), &Environment::is_ssao_enabled);
ClassDB::bind_method(D_METHOD("set_ssao_radius", "radius"), &Environment::set_ssao_radius);
@@ -1195,6 +1284,25 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_light_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_direct_light_affect", "get_ssao_direct_light_affect");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_ao_channel_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_ao_channel_affect", "get_ssao_ao_channel_affect");
+ // SSIL
+ ClassDB::bind_method(D_METHOD("set_ssil_enabled", "enabled"), &Environment::set_ssil_enabled);
+ ClassDB::bind_method(D_METHOD("is_ssil_enabled"), &Environment::is_ssil_enabled);
+ ClassDB::bind_method(D_METHOD("set_ssil_radius", "radius"), &Environment::set_ssil_radius);
+ ClassDB::bind_method(D_METHOD("get_ssil_radius"), &Environment::get_ssil_radius);
+ ClassDB::bind_method(D_METHOD("set_ssil_intensity", "intensity"), &Environment::set_ssil_intensity);
+ ClassDB::bind_method(D_METHOD("get_ssil_intensity"), &Environment::get_ssil_intensity);
+ ClassDB::bind_method(D_METHOD("set_ssil_sharpness", "sharpness"), &Environment::set_ssil_sharpness);
+ ClassDB::bind_method(D_METHOD("get_ssil_sharpness"), &Environment::get_ssil_sharpness);
+ ClassDB::bind_method(D_METHOD("set_ssil_normal_rejection", "normal_rejection"), &Environment::set_ssil_normal_rejection);
+ ClassDB::bind_method(D_METHOD("get_ssil_normal_rejection"), &Environment::get_ssil_normal_rejection);
+
+ ADD_GROUP("SSIL", "ssil_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssil_enabled"), "set_ssil_enabled", "is_ssil_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_radius", PROPERTY_HINT_RANGE, "0.01,16,0.01,or_greater"), "set_ssil_radius", "get_ssil_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_intensity", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_ssil_intensity", "get_ssil_intensity");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_sharpness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssil_sharpness", "get_ssil_sharpness");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_normal_rejection", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssil_normal_rejection", "get_ssil_normal_rejection");
+
// SDFGI
ClassDB::bind_method(D_METHOD("set_sdfgi_enabled", "enabled"), &Environment::set_sdfgi_enabled);
@@ -1227,11 +1335,11 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_use_occlusion"), "set_sdfgi_use_occlusion", "is_sdfgi_using_occlusion");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_read_sky_light"), "set_sdfgi_read_sky_light", "is_sdfgi_reading_sky_light");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_bounce_feedback", PROPERTY_HINT_RANGE, "0,1.99,0.01"), "set_sdfgi_bounce_feedback", "get_sdfgi_bounce_feedback");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_ENUM, "4 Cascades,6 Cascades,8 Cascades"), "set_sdfgi_cascades", "get_sdfgi_cascades");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_RANGE, "1,8,1"), "set_sdfgi_cascades", "get_sdfgi_cascades");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_min_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_sdfgi_min_cell_size", "get_sdfgi_min_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_cascade0_distance", "get_sdfgi_cascade0_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_max_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_max_distance", "get_sdfgi_max_distance");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_y_scale", PROPERTY_HINT_ENUM, "Disable,75%,50%"), "set_sdfgi_y_scale", "get_sdfgi_y_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_y_scale", PROPERTY_HINT_ENUM, "50% (Compact),75% (Balanced),100% (Sparse)"), "set_sdfgi_y_scale", "get_sdfgi_y_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_energy"), "set_sdfgi_energy", "get_sdfgi_energy");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_normal_bias"), "set_sdfgi_normal_bias", "get_sdfgi_normal_bias");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_probe_bias"), "set_sdfgi_probe_bias", "get_sdfgi_probe_bias");
@@ -1260,6 +1368,10 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_glow_hdr_bleed_scale"), &Environment::get_glow_hdr_bleed_scale);
ClassDB::bind_method(D_METHOD("set_glow_hdr_luminance_cap", "amount"), &Environment::set_glow_hdr_luminance_cap);
ClassDB::bind_method(D_METHOD("get_glow_hdr_luminance_cap"), &Environment::get_glow_hdr_luminance_cap);
+ ClassDB::bind_method(D_METHOD("set_glow_map_strength", "strength"), &Environment::set_glow_map_strength);
+ ClassDB::bind_method(D_METHOD("get_glow_map_strength"), &Environment::get_glow_map_strength);
+ ClassDB::bind_method(D_METHOD("set_glow_map", "mode"), &Environment::set_glow_map);
+ ClassDB::bind_method(D_METHOD("get_glow_map"), &Environment::get_glow_map);
ADD_GROUP("Glow", "glow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "glow_enabled"), "set_glow_enabled", "is_glow_enabled");
@@ -1279,6 +1391,8 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_threshold", PROPERTY_HINT_RANGE, "0.0,4.0,0.01"), "set_glow_hdr_bleed_threshold", "get_glow_hdr_bleed_threshold");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_scale", PROPERTY_HINT_RANGE, "0.0,4.0,0.01"), "set_glow_hdr_bleed_scale", "get_glow_hdr_bleed_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_luminance_cap", PROPERTY_HINT_RANGE, "0.0,256.0,0.01"), "set_glow_hdr_luminance_cap", "get_glow_hdr_luminance_cap");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_map_strength", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_glow_map_strength", "get_glow_map_strength");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "glow_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_glow_map", "get_glow_map");
// Fog
@@ -1404,13 +1518,9 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_REPLACE);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_MIX);
- BIND_ENUM_CONSTANT(SDFGI_CASCADES_4);
- BIND_ENUM_CONSTANT(SDFGI_CASCADES_6);
- BIND_ENUM_CONSTANT(SDFGI_CASCADES_8);
-
- BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_DISABLED);
- BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT);
+ BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT);
+ BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_100_PERCENT);
}
Environment::Environment() {
@@ -1431,6 +1541,7 @@ Environment::Environment() {
_update_tonemap();
_update_ssr();
_update_ssao();
+ _update_ssil();
_update_sdfgi();
_update_glow();
_update_fog();
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index 024bef34de..b71fe8904a 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,6 @@
#include "core/io/resource.h"
#include "scene/resources/sky.h"
#include "scene/resources/texture.h"
-#include "servers/rendering_server.h"
class Environment : public Resource {
GDCLASS(Environment, Resource);
@@ -70,16 +69,10 @@ public:
TONE_MAPPER_ACES,
};
- enum SDFGICascades {
- SDFGI_CASCADES_4,
- SDFGI_CASCADES_6,
- SDFGI_CASCADES_8,
- };
-
enum SDFGIYScale {
- SDFGI_Y_SCALE_DISABLED,
- SDFGI_Y_SCALE_75_PERCENT,
SDFGI_Y_SCALE_50_PERCENT,
+ SDFGI_Y_SCALE_75_PERCENT,
+ SDFGI_Y_SCALE_100_PERCENT,
};
enum GlowBlendMode {
@@ -142,14 +135,23 @@ private:
float ssao_ao_channel_affect = 0.0;
void _update_ssao();
+ // SSIL
+ bool ssil_enabled = false;
+ float ssil_radius = 5.0;
+ float ssil_intensity = 1.0;
+ float ssil_sharpness = 0.98;
+ float ssil_normal_rejection = 1.0;
+
+ void _update_ssil();
+
// SDFGI
bool sdfgi_enabled = false;
- SDFGICascades sdfgi_cascades = SDFGI_CASCADES_6;
+ int sdfgi_cascades = 4;
float sdfgi_min_cell_size = 0.2;
- SDFGIYScale sdfgi_y_scale = SDFGI_Y_SCALE_DISABLED;
+ SDFGIYScale sdfgi_y_scale = SDFGI_Y_SCALE_75_PERCENT;
bool sdfgi_use_occlusion = false;
- float sdfgi_bounce_feedback = 0.0;
- bool sdfgi_read_sky_light = false;
+ float sdfgi_bounce_feedback = 0.5;
+ bool sdfgi_read_sky_light = true;
float sdfgi_energy = 1.0;
float sdfgi_normal_bias = 1.1;
float sdfgi_probe_bias = 1.1;
@@ -167,6 +169,8 @@ private:
float glow_hdr_bleed_threshold = 1.0;
float glow_hdr_bleed_scale = 2.0;
float glow_hdr_luminance_cap = 12.0;
+ float glow_map_strength = 0.8f;
+ Ref<Texture> glow_map;
void _update_glow();
// Fog
@@ -296,11 +300,23 @@ public:
void set_ssao_ao_channel_affect(float p_ao_channel_affect);
float get_ssao_ao_channel_affect() const;
+ // SSIL
+ void set_ssil_enabled(bool p_enabled);
+ bool is_ssil_enabled() const;
+ void set_ssil_radius(float p_radius);
+ float get_ssil_radius() const;
+ void set_ssil_intensity(float p_intensity);
+ float get_ssil_intensity() const;
+ void set_ssil_sharpness(float p_sharpness);
+ float get_ssil_sharpness() const;
+ void set_ssil_normal_rejection(float p_normal_rejection);
+ float get_ssil_normal_rejection() const;
+
// SDFGI
void set_sdfgi_enabled(bool p_enabled);
bool is_sdfgi_enabled() const;
- void set_sdfgi_cascades(SDFGICascades p_cascades);
- SDFGICascades get_sdfgi_cascades() const;
+ void set_sdfgi_cascades(int p_cascades);
+ int get_sdfgi_cascades() const;
void set_sdfgi_min_cell_size(float p_size);
float get_sdfgi_min_cell_size() const;
void set_sdfgi_max_distance(float p_distance);
@@ -345,6 +361,10 @@ public:
float get_glow_hdr_bleed_scale() const;
void set_glow_hdr_luminance_cap(float p_amount);
float get_glow_hdr_luminance_cap() const;
+ void set_glow_map_strength(float p_strength);
+ float get_glow_map_strength() const;
+ void set_glow_map(Ref<Texture> p_glow_map);
+ Ref<Texture> get_glow_map() const;
// Fog
@@ -412,7 +432,6 @@ VARIANT_ENUM_CAST(Environment::BGMode)
VARIANT_ENUM_CAST(Environment::AmbientSource)
VARIANT_ENUM_CAST(Environment::ReflectionSource)
VARIANT_ENUM_CAST(Environment::ToneMapper)
-VARIANT_ENUM_CAST(Environment::SDFGICascades)
VARIANT_ENUM_CAST(Environment::SDFGIYScale)
VARIANT_ENUM_CAST(Environment::GlowBlendMode)
diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp
index 978aaca33d..a05ef0c779 100644
--- a/scene/resources/fog_material.cpp
+++ b/scene/resources/fog_material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/fog_material.h b/scene/resources/fog_material.h
index e256bd4719..1f7cd7bfe6 100644
--- a/scene/resources/fog_material.h
+++ b/scene/resources/fog_material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index d9de47afc7..d586abac14 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,10 @@
#include "font.h"
+#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "core/string/translation.h"
+#include "core/templates/hash_map.h"
#include "core/templates/hashfuncs.h"
#include "scene/resources/text_line.h"
#include "scene/resources/text_paragraph.h"
@@ -39,7 +41,7 @@
_FORCE_INLINE_ void FontData::_clear_cache() {
for (int i = 0; i < cache.size(); i++) {
if (cache[i].is_valid()) {
- TS->free(cache[i]);
+ TS->free_rid(cache[i]);
cache.write[i] = RID();
}
}
@@ -53,23 +55,33 @@ _FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const {
cache.write[p_cache_index] = TS->create_font();
TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size);
TS->font_set_antialiased(cache[p_cache_index], antialiased);
+ TS->font_set_generate_mipmaps(cache[p_cache_index], mipmaps);
TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf);
TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range);
TS->font_set_msdf_size(cache[p_cache_index], msdf_size);
TS->font_set_fixed_size(cache[p_cache_index], fixed_size);
TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter);
TS->font_set_hinting(cache[p_cache_index], hinting);
+ TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning);
+ TS->font_set_embolden(cache[p_cache_index], embolden);
+ TS->font_set_transform(cache[p_cache_index], transform);
TS->font_set_oversampling(cache[p_cache_index], oversampling);
}
}
void FontData::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load_bitmap_font", "path"), &FontData::load_bitmap_font);
+ ClassDB::bind_method(D_METHOD("load_dynamic_font", "path"), &FontData::load_dynamic_font);
+
ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data);
ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data);
ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased);
+ ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &FontData::set_generate_mipmaps);
+ ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &FontData::get_generate_mipmaps);
+
ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontData::set_font_name);
ClassDB::bind_method(D_METHOD("get_font_name"), &FontData::get_font_name);
@@ -88,12 +100,24 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontData::set_msdf_size);
ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontData::get_msdf_size);
+ ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontData::set_fixed_size);
+ ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontData::get_fixed_size);
+
ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontData::set_force_autohinter);
ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontData::is_force_autohinter);
ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting);
ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting);
+ ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontData::set_subpixel_positioning);
+ ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontData::get_subpixel_positioning);
+
+ ClassDB::bind_method(D_METHOD("set_embolden", "strength"), &FontData::set_embolden);
+ ClassDB::bind_method(D_METHOD("get_embolden"), &FontData::get_embolden);
+
+ ClassDB::bind_method(D_METHOD("set_transform", "transform"), &FontData::set_transform);
+ ClassDB::bind_method(D_METHOD("get_transform"), &FontData::get_transform);
+
ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling);
ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling);
@@ -181,6 +205,9 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override);
ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides);
+ ClassDB::bind_method(D_METHOD("set_opentype_feature_overrides", "overrides"), &FontData::set_opentype_feature_overrides);
+ ClassDB::bind_method(D_METHOD("get_opentype_feature_overrides"), &FontData::get_opentype_feature_overrides);
+
ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
@@ -188,49 +215,29 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list);
ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_generate_mipmaps", "get_generate_mipmaps");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "embolden", PROPERTY_HINT_RANGE, "-2,2,0.01", PROPERTY_USAGE_STORAGE), "set_embolden", "get_embolden");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_transform", "get_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides");
}
bool FontData::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> tokens = p_name.operator String().split("/");
- if (tokens.size() == 1) {
- if (tokens[0] == "data") {
- set_data(p_value);
- return true;
- } else if (tokens[0] == "antialiased") {
- set_antialiased(p_value);
- return true;
- } else if (tokens[0] == "font_name") {
- set_font_name(p_value);
- return true;
- } else if (tokens[0] == "style_name") {
- set_font_style_name(p_value);
- return true;
- } else if (tokens[0] == "font_style") {
- set_font_style(p_value);
- return true;
- } else if (tokens[0] == "multichannel_signed_distance_field") {
- set_multichannel_signed_distance_field(p_value);
- return true;
- } else if (tokens[0] == "msdf_pixel_range") {
- set_msdf_pixel_range(p_value);
- return true;
- } else if (tokens[0] == "msdf_size") {
- set_msdf_size(p_value);
- return true;
- } else if (tokens[0] == "fixed_size") {
- set_fixed_size(p_value);
- return true;
- } else if (tokens[0] == "hinting") {
- set_hinting((TextServer::Hinting)p_value.operator int());
- return true;
- } else if (tokens[0] == "force_autohinter") {
- set_force_autohinter(p_value);
- return true;
- } else if (tokens[0] == "oversampling") {
- set_oversampling(p_value);
- return true;
- }
- } else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
+ if (tokens.size() == 2 && tokens[0] == "language_support_override") {
String lang = tokens[1];
set_language_support_override(lang, p_value);
return true;
@@ -306,45 +313,7 @@ bool FontData::_set(const StringName &p_name, const Variant &p_value) {
bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> tokens = p_name.operator String().split("/");
- if (tokens.size() == 1) {
- if (tokens[0] == "data") {
- r_ret = get_data();
- return true;
- } else if (tokens[0] == "antialiased") {
- r_ret = is_antialiased();
- return true;
- } else if (tokens[0] == "font_name") {
- r_ret = get_font_name();
- return true;
- } else if (tokens[0] == "style_name") {
- r_ret = get_font_style_name();
- return true;
- } else if (tokens[0] == "font_style") {
- r_ret = get_font_style();
- return true;
- } else if (tokens[0] == "multichannel_signed_distance_field") {
- r_ret = is_multichannel_signed_distance_field();
- return true;
- } else if (tokens[0] == "msdf_pixel_range") {
- r_ret = get_msdf_pixel_range();
- return true;
- } else if (tokens[0] == "msdf_size") {
- r_ret = get_msdf_size();
- return true;
- } else if (tokens[0] == "fixed_size") {
- r_ret = get_fixed_size();
- return true;
- } else if (tokens[0] == "hinting") {
- r_ret = get_hinting();
- return true;
- } else if (tokens[0] == "force_autohinter") {
- r_ret = is_force_autohinter();
- return true;
- } else if (tokens[0] == "oversampling") {
- r_ret = get_oversampling();
- return true;
- }
- } else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
+ if (tokens.size() == 2 && tokens[0] == "language_support_override") {
String lang = tokens[1];
r_ret = get_language_support_override(lang);
return true;
@@ -419,20 +388,6 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
}
void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
-
- p_list->push_back(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
- p_list->push_back(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
-
Vector<String> lang_over = get_language_support_overrides();
for (int i = 0; i < lang_over.size(); i++) {
p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
@@ -493,16 +448,774 @@ void FontData::reset_state() {
cache.clear();
antialiased = true;
+ mipmaps = false;
msdf = false;
force_autohinter = false;
hinting = TextServer::HINTING_LIGHT;
+ subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
msdf_pixel_range = 14;
msdf_size = 128;
+ fixed_size = 0;
+ embolden = 0.f;
+ transform = Transform2D();
oversampling = 0.f;
}
+void FontData::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_r;
+ imgdata_r.resize(w * h * 2);
+ uint8_t *wr = imgdata_r.ptrw();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_b;
+ imgdata_b.resize(w * h * 2);
+ uint8_t *wb = imgdata_b.ptrw();
+
+ PackedByteArray imgdata_a;
+ imgdata_a.resize(w * h * 2);
+ uint8_t *wa = imgdata_a.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * 4;
+ int ofs_dst = (i * w + j) * 2;
+ wr[ofs_dst + 0] = 255;
+ wr[ofs_dst + 1] = r[ofs_src + 0];
+ wg[ofs_dst + 0] = 255;
+ wg[ofs_dst + 1] = r[ofs_src + 1];
+ wb[ofs_dst + 0] = 255;
+ wb[ofs_dst + 1] = r[ofs_src + 2];
+ wa[ofs_dst + 0] = 255;
+ wa[ofs_dst + 1] = r[ofs_src + 3];
+ }
+ }
+ Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
+ Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
+ Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
+}
+
+void FontData::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_r;
+ imgdata_r.resize(w * h * 2);
+ uint8_t *wr = imgdata_r.ptrw();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_b;
+ imgdata_b.resize(w * h * 2);
+ uint8_t *wb = imgdata_b.ptrw();
+
+ PackedByteArray imgdata_a;
+ imgdata_a.resize(w * h * 2);
+ uint8_t *wa = imgdata_a.ptrw();
+
+ PackedByteArray imgdata_ro;
+ imgdata_ro.resize(w * h * 2);
+ uint8_t *wro = imgdata_ro.ptrw();
+
+ PackedByteArray imgdata_go;
+ imgdata_go.resize(w * h * 2);
+ uint8_t *wgo = imgdata_go.ptrw();
+
+ PackedByteArray imgdata_bo;
+ imgdata_bo.resize(w * h * 2);
+ uint8_t *wbo = imgdata_bo.ptrw();
+
+ PackedByteArray imgdata_ao;
+ imgdata_ao.resize(w * h * 2);
+ uint8_t *wao = imgdata_ao.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * 4;
+ int ofs_dst = (i * w + j) * 2;
+ wr[ofs_dst + 0] = 255;
+ wro[ofs_dst + 0] = 255;
+ if (r[ofs_src + 0] > 0x0F) {
+ wr[ofs_dst + 1] = (r[ofs_src + 0] - 0x0F) * 2;
+ wro[ofs_dst + 1] = 0;
+ } else {
+ wr[ofs_dst + 1] = 0;
+ wro[ofs_dst + 1] = r[ofs_src + 0] * 2;
+ }
+ wg[ofs_dst + 0] = 255;
+ wgo[ofs_dst + 0] = 255;
+ if (r[ofs_src + 1] > 0x0F) {
+ wg[ofs_dst + 1] = (r[ofs_src + 1] - 0x0F) * 2;
+ wgo[ofs_dst + 1] = 0;
+ } else {
+ wg[ofs_dst + 1] = 0;
+ wgo[ofs_dst + 1] = r[ofs_src + 1] * 2;
+ }
+ wb[ofs_dst + 0] = 255;
+ wbo[ofs_dst + 0] = 255;
+ if (r[ofs_src + 2] > 0x0F) {
+ wb[ofs_dst + 1] = (r[ofs_src + 2] - 0x0F) * 2;
+ wbo[ofs_dst + 1] = 0;
+ } else {
+ wb[ofs_dst + 1] = 0;
+ wbo[ofs_dst + 1] = r[ofs_src + 2] * 2;
+ }
+ wa[ofs_dst + 0] = 255;
+ wao[ofs_dst + 0] = 255;
+ if (r[ofs_src + 3] > 0x0F) {
+ wa[ofs_dst + 1] = (r[ofs_src + 3] - 0x0F) * 2;
+ wao[ofs_dst + 1] = 0;
+ } else {
+ wa[ofs_dst + 1] = 0;
+ wao[ofs_dst + 1] = r[ofs_src + 3] * 2;
+ }
+ }
+ }
+ Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
+ Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
+ Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
+
+ Ref<Image> img_ro = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ro));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 0, img_ro);
+ Ref<Image> img_go = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_go));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 1, img_go);
+ Ref<Image> img_bo = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_bo));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 2, img_bo);
+ Ref<Image> img_ao = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ao));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao);
+}
+
+void FontData::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 4);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_o;
+ imgdata_o.resize(w * h * 4);
+ uint8_t *wo = imgdata_o.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = (i * w + j) * 4;
+
+ if (r[ofs + 0] > 0x7F) {
+ wg[ofs + 0] = r[ofs + 0];
+ wo[ofs + 0] = 0;
+ } else {
+ wg[ofs + 0] = 0;
+ wo[ofs + 0] = r[ofs + 0] * 2;
+ }
+ if (r[ofs + 1] > 0x7F) {
+ wg[ofs + 1] = r[ofs + 1];
+ wo[ofs + 1] = 0;
+ } else {
+ wg[ofs + 1] = 0;
+ wo[ofs + 1] = r[ofs + 1] * 2;
+ }
+ if (r[ofs + 2] > 0x7F) {
+ wg[ofs + 2] = r[ofs + 2];
+ wo[ofs + 2] = 0;
+ } else {
+ wg[ofs + 2] = 0;
+ wo[ofs + 2] = r[ofs + 2] * 2;
+ }
+ if (r[ofs + 3] > 0x7F) {
+ wg[ofs + 3] = r[ofs + 3];
+ wo[ofs + 3] = 0;
+ } else {
+ wg[ofs + 3] = 0;
+ wo[ofs + 3] = r[ofs + 3] * 2;
+ }
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
+
+ Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_o));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o);
+}
+
+void FontData::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ int size = 4;
+ if (p_source->get_format() == Image::FORMAT_L8) {
+ size = 1;
+ p_ch = 0;
+ }
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * size;
+ int ofs_dst = (i * w + j) * 2;
+ wg[ofs_dst + 0] = 255;
+ wg[ofs_dst + 1] = r[ofs_src + p_ch];
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g);
+}
+
+void FontData::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ int size = 4;
+ if (p_source->get_format() == Image::FORMAT_L8) {
+ size = 1;
+ p_ch = 0;
+ }
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_o;
+ imgdata_o.resize(w * h * 2);
+ uint8_t *wo = imgdata_o.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * size;
+ int ofs_dst = (i * w + j) * 2;
+ wg[ofs_dst + 0] = 255;
+ wo[ofs_dst + 0] = 255;
+ if (r[ofs_src + p_ch] > 0x7F) {
+ wg[ofs_dst + 1] = r[ofs_src + p_ch];
+ wo[ofs_dst + 1] = 0;
+ } else {
+ wg[ofs_dst + 1] = 0;
+ wo[ofs_dst + 1] = r[ofs_src + p_ch] * 2;
+ }
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
+
+ Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_o));
+ set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o);
+}
+
/*************************************************************************/
+Error FontData::load_bitmap_font(const String &p_path) {
+ reset_state();
+
+ antialiased = false;
+ mipmaps = false;
+ msdf = false;
+ force_autohinter = false;
+ hinting = TextServer::HINTING_NONE;
+ oversampling = 1.0f;
+
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_CREATE, vformat(RTR("Cannot open font from file: %s."), p_path));
+
+ int base_size = 16;
+ int height = 0;
+ int ascent = 0;
+ int outline = 0;
+ uint32_t st_flags = 0;
+ String font_name;
+
+ bool packed = false;
+ uint8_t ch[4] = { 0, 0, 0, 0 }; // RGBA
+ int first_gl_ch = -1;
+ int first_ol_ch = -1;
+ int first_cm_ch = -1;
+
+ unsigned char magic[4];
+ f->get_buffer((unsigned char *)&magic, 4);
+ if (magic[0] == 'B' && magic[1] == 'M' && magic[2] == 'F') {
+ // Binary BMFont file.
+ ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(RTR("Version %d of BMFont is not supported."), (int)magic[3]));
+
+ uint8_t block_type = f->get_8();
+ uint32_t block_size = f->get_32();
+ while (!f->eof_reached()) {
+ uint64_t off = f->get_position();
+ switch (block_type) {
+ case 1: /* info */ {
+ ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, RTR("Invalid BMFont info block size."));
+ base_size = f->get_16();
+ uint8_t flags = f->get_8();
+ ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported."));
+ if (flags & (1 << 3)) {
+ st_flags |= TextServer::FONT_BOLD;
+ }
+ if (flags & (1 << 2)) {
+ st_flags |= TextServer::FONT_ITALIC;
+ }
+ f->get_8(); // non-unicode charset, skip
+ f->get_16(); // stretch_h, skip
+ f->get_8(); // aa, skip
+ f->get_32(); // padding, skip
+ f->get_16(); // spacing, skip
+ outline = f->get_8();
+ // font name
+ PackedByteArray name_data;
+ name_data.resize(block_size - 14);
+ f->get_buffer(name_data.ptrw(), block_size - 14);
+ font_name = String::utf8((const char *)name_data.ptr(), block_size - 14);
+ set_fixed_size(base_size);
+ } break;
+ case 2: /* common */ {
+ ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, RTR("Invalid BMFont common block size."));
+ height = f->get_16();
+ ascent = f->get_16();
+ f->get_32(); // scale, skip
+ f->get_16(); // pages, skip
+ uint8_t flags = f->get_8();
+ packed = (flags & 0x01);
+ ch[3] = f->get_8();
+ ch[0] = f->get_8();
+ ch[1] = f->get_8();
+ ch[2] = f->get_8();
+ for (int i = 0; i < 4; i++) {
+ if (ch[i] == 0 && first_gl_ch == -1) {
+ first_gl_ch = i;
+ }
+ if (ch[i] == 1 && first_ol_ch == -1) {
+ first_ol_ch = i;
+ }
+ if (ch[i] == 2 && first_cm_ch == -1) {
+ first_cm_ch = i;
+ }
+ }
+ } break;
+ case 3: /* pages */ {
+ int page = 0;
+ CharString cs;
+ char32_t c = f->get_8();
+ while (!f->eof_reached() && f->get_position() <= off + block_size) {
+ if (c == '\0') {
+ String base_dir = p_path.get_base_dir();
+ String file = base_dir.plus_file(String::utf8(cs.ptr(), cs.length()));
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat(RTR("Can't load font texture: %s."), file));
+
+ if (packed) {
+ if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_packed_8bit(img, page, base_size);
+ } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_packed_4bit(img, page, base_size);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
+ }
+ } else {
+ if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_rgba_4bit(img, page, base_size);
+ } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
+ _convert_mono_8bit(img, page, first_ol_ch, base_size, 1);
+ } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_mono_4bit(img, page, first_cm_ch, base_size, 1);
+ } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
+ }
+ }
+ }
+ page++;
+ cs = "";
+ } else {
+ cs += c;
+ }
+ c = f->get_8();
+ }
+ } break;
+ case 4: /* chars */ {
+ int char_count = block_size / 20;
+ for (int i = 0; i < char_count; i++) {
+ Vector2 advance;
+ Vector2 size;
+ Vector2 offset;
+ Rect2 uv_rect;
+
+ char32_t idx = f->get_32();
+ uv_rect.position.x = (int16_t)f->get_16();
+ uv_rect.position.y = (int16_t)f->get_16();
+ uv_rect.size.width = (int16_t)f->get_16();
+ size.width = uv_rect.size.width;
+ uv_rect.size.height = (int16_t)f->get_16();
+ size.height = uv_rect.size.height;
+ offset.x = (int16_t)f->get_16();
+ offset.y = (int16_t)f->get_16() - ascent;
+ advance.x = (int16_t)f->get_16();
+ if (advance.x < 0) {
+ advance.x = size.width + 1;
+ }
+
+ int texture_idx = f->get_8();
+ uint8_t channel = f->get_8();
+
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, RTR("Invalid glyph channel."));
+ int ch_off = 0;
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
+ set_glyph_advance(0, base_size, idx, advance);
+ set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
+ set_glyph_size(0, Vector2i(base_size, 0), idx, size);
+ set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
+ set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ if (outline > 0) {
+ set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
+ set_glyph_size(0, Vector2i(base_size, 1), idx, size);
+ set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
+ set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ }
+ }
+ } break;
+ case 5: /* kerning */ {
+ int pair_count = block_size / 10;
+ for (int i = 0; i < pair_count; i++) {
+ Vector2i kpk;
+ kpk.x = f->get_32();
+ kpk.y = f->get_32();
+ set_kerning(0, base_size, kpk, Vector2((int16_t)f->get_16(), 0));
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Invalid BMFont block type."));
+ } break;
+ }
+ f->seek(off + block_size);
+ block_type = f->get_8();
+ block_size = f->get_32();
+ }
+
+ } else {
+ // Text BMFont file.
+ f->seek(0);
+ while (true) {
+ String line = f->get_line();
+
+ int delimiter = line.find(" ");
+ String type = line.substr(0, delimiter);
+ int pos = delimiter + 1;
+ HashMap<String, String> keys;
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ while (pos < line.size()) {
+ int eq = line.find("=", pos);
+ if (eq == -1) {
+ break;
+ }
+ String key = line.substr(pos, eq - pos);
+ int end = -1;
+ String value;
+ if (line[eq + 1] == '"') {
+ end = line.find("\"", eq + 2);
+ if (end == -1) {
+ break;
+ }
+ value = line.substr(eq + 2, end - 1 - eq - 1);
+ pos = end + 1;
+ } else {
+ end = line.find(" ", eq + 1);
+ if (end == -1) {
+ end = line.size();
+ }
+ value = line.substr(eq + 1, end - eq);
+ pos = end;
+ }
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ keys[key] = value;
+ }
+
+ if (type == "info") {
+ if (keys.has("size")) {
+ base_size = keys["size"].to_int();
+ set_fixed_size(base_size);
+ }
+ if (keys.has("outline")) {
+ outline = keys["outline"].to_int();
+ }
+ if (keys.has("bold")) {
+ if (keys["bold"].to_int()) {
+ st_flags |= TextServer::FONT_BOLD;
+ }
+ }
+ if (keys.has("italic")) {
+ if (keys["italic"].to_int()) {
+ st_flags |= TextServer::FONT_ITALIC;
+ }
+ }
+ if (keys.has("face")) {
+ font_name = keys["face"];
+ }
+ ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported."));
+ } else if (type == "common") {
+ if (keys.has("lineHeight")) {
+ height = keys["lineHeight"].to_int();
+ }
+ if (keys.has("base")) {
+ ascent = keys["base"].to_int();
+ }
+ if (keys.has("packed")) {
+ packed = (keys["packed"].to_int() == 1);
+ }
+ if (keys.has("alphaChnl")) {
+ ch[3] = keys["alphaChnl"].to_int();
+ }
+ if (keys.has("redChnl")) {
+ ch[0] = keys["redChnl"].to_int();
+ }
+ if (keys.has("greenChnl")) {
+ ch[1] = keys["greenChnl"].to_int();
+ }
+ if (keys.has("blueChnl")) {
+ ch[2] = keys["blueChnl"].to_int();
+ }
+ for (int i = 0; i < 4; i++) {
+ if (ch[i] == 0 && first_gl_ch == -1) {
+ first_gl_ch = i;
+ }
+ if (ch[i] == 1 && first_ol_ch == -1) {
+ first_ol_ch = i;
+ }
+ if (ch[i] == 2 && first_cm_ch == -1) {
+ first_cm_ch = i;
+ }
+ }
+ } else if (type == "page") {
+ int page = 0;
+ if (keys.has("id")) {
+ page = keys["id"].to_int();
+ }
+ if (keys.has("file")) {
+ String base_dir = p_path.get_base_dir();
+ String file = base_dir.plus_file(keys["file"]);
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat(RTR("Can't load font texture: %s."), file));
+ if (packed) {
+ if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_packed_8bit(img, page, base_size);
+ } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_packed_4bit(img, page, base_size);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
+ }
+ } else {
+ if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_rgba_4bit(img, page, base_size);
+ } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
+ _convert_mono_8bit(img, page, first_ol_ch, base_size, 1);
+ } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_mono_4bit(img, page, first_cm_ch, base_size, 1);
+ } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
+ }
+ }
+ }
+ }
+ } else if (type == "char") {
+ char32_t idx = 0;
+ Vector2 advance;
+ Vector2 size;
+ Vector2 offset;
+ Rect2 uv_rect;
+ int texture_idx = -1;
+ uint8_t channel = 15;
+
+ if (keys.has("id")) {
+ idx = keys["id"].to_int();
+ }
+ if (keys.has("x")) {
+ uv_rect.position.x = keys["x"].to_int();
+ }
+ if (keys.has("y")) {
+ uv_rect.position.y = keys["y"].to_int();
+ }
+ if (keys.has("width")) {
+ uv_rect.size.width = keys["width"].to_int();
+ size.width = keys["width"].to_int();
+ }
+ if (keys.has("height")) {
+ uv_rect.size.height = keys["height"].to_int();
+ size.height = keys["height"].to_int();
+ }
+ if (keys.has("xoffset")) {
+ offset.x = keys["xoffset"].to_int();
+ }
+ if (keys.has("yoffset")) {
+ offset.y = keys["yoffset"].to_int() - ascent;
+ }
+ if (keys.has("page")) {
+ texture_idx = keys["page"].to_int();
+ }
+ if (keys.has("xadvance")) {
+ advance.x = keys["xadvance"].to_int();
+ }
+ if (advance.x < 0) {
+ advance.x = size.width + 1;
+ }
+ if (keys.has("chnl")) {
+ channel = keys["chnl"].to_int();
+ }
+
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, RTR("Invalid glyph channel."));
+ int ch_off = 0;
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
+ set_glyph_advance(0, base_size, idx, advance);
+ set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
+ set_glyph_size(0, Vector2i(base_size, 0), idx, size);
+ set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
+ set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ if (outline > 0) {
+ set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
+ set_glyph_size(0, Vector2i(base_size, 1), idx, size);
+ set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
+ set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ }
+ } else if (type == "kerning") {
+ Vector2i kpk;
+ if (keys.has("first")) {
+ kpk.x = keys["first"].to_int();
+ }
+ if (keys.has("second")) {
+ kpk.y = keys["second"].to_int();
+ }
+ if (keys.has("amount")) {
+ set_kerning(0, base_size, kpk, Vector2(keys["amount"].to_int(), 0));
+ }
+ }
+
+ if (f->eof_reached()) {
+ break;
+ }
+ }
+ }
+
+ set_font_name(font_name);
+ set_font_style(st_flags);
+ set_ascent(0, base_size, ascent);
+ set_descent(0, base_size, height - ascent);
+
+ return OK;
+}
+
+Error FontData::load_dynamic_font(const String &p_path) {
+ reset_state();
+
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
+ set_data(data);
+
+ return OK;
+}
+
void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) {
data.clear();
data_ptr = p_data;
@@ -585,6 +1298,21 @@ bool FontData::is_antialiased() const {
return antialiased;
}
+void FontData::set_generate_mipmaps(bool p_generate_mipmaps) {
+ if (mipmaps != p_generate_mipmaps) {
+ mipmaps = p_generate_mipmaps;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_generate_mipmaps(cache[i], mipmaps);
+ }
+ emit_changed();
+ }
+}
+
+bool FontData::get_generate_mipmaps() const {
+ return mipmaps;
+}
+
void FontData::set_multichannel_signed_distance_field(bool p_msdf) {
if (msdf != p_msdf) {
msdf = p_msdf;
@@ -675,6 +1403,51 @@ TextServer::Hinting FontData::get_hinting() const {
return hinting;
}
+void FontData::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) {
+ if (subpixel_positioning != p_subpixel) {
+ subpixel_positioning = p_subpixel;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_subpixel_positioning(cache[i], subpixel_positioning);
+ }
+ emit_changed();
+ }
+}
+
+TextServer::SubpixelPositioning FontData::get_subpixel_positioning() const {
+ return subpixel_positioning;
+}
+
+void FontData::set_embolden(float p_strength) {
+ if (embolden != p_strength) {
+ embolden = p_strength;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_embolden(cache[i], embolden);
+ }
+ emit_changed();
+ }
+}
+
+float FontData::get_embolden() const {
+ return embolden;
+}
+
+void FontData::set_transform(Transform2D p_transform) {
+ if (transform != p_transform) {
+ transform = p_transform;
+ for (int i = 0; i < cache.size(); i++) {
+ _ensure_rid(i);
+ TS->font_set_transform(cache[i], transform);
+ }
+ emit_changed();
+ }
+}
+
+Transform2D FontData::get_transform() const {
+ return transform;
+}
+
void FontData::set_oversampling(real_t p_oversampling) {
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
@@ -747,9 +1520,9 @@ void FontData::clear_cache() {
void FontData::remove_cache(int p_cache_index) {
ERR_FAIL_INDEX(p_cache_index, cache.size());
if (cache[p_cache_index].is_valid()) {
- TS->free(cache.write[p_cache_index]);
+ TS->free_rid(cache.write[p_cache_index]);
}
- cache.remove(p_cache_index);
+ cache.remove_at(p_cache_index);
emit_changed();
}
@@ -1074,6 +1847,16 @@ Vector<String> FontData::get_script_support_overrides() const {
return TS->font_get_script_support_overrides(cache[0]);
}
+void FontData::set_opentype_feature_overrides(const Dictionary &p_overrides) {
+ _ensure_rid(0);
+ TS->font_set_opentype_feature_overrides(cache[0], p_overrides);
+}
+
+Dictionary FontData::get_opentype_feature_overrides() const {
+ _ensure_rid(0);
+ return TS->font_get_opentype_feature_overrides(cache[0]);
+}
+
bool FontData::has_char(char32_t p_char) const {
_ensure_rid(0);
return TS->font_has_char(cache[0], p_char);
@@ -1149,11 +1932,11 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE));
ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE));
- ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "alignment", "width", "flags"), &Font::get_string_size, DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE));
ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
@@ -1162,6 +1945,8 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
+
+ ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids);
}
bool Font::_set(const StringName &p_name, const Variant &p_value) {
@@ -1353,8 +2138,8 @@ void Font::remove_data(int p_idx) {
data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed));
}
- data.remove(p_idx);
- rids.remove(p_idx);
+ data.remove_at(p_idx);
+ rids.remove_at(p_idx);
cache.clear();
cache_wrap.clear();
@@ -1446,7 +2231,7 @@ real_t Font::get_underline_thickness(int p_size) const {
return ret;
}
-Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint16_t p_flags) const {
+Size2 Font::get_string_size(const String &p_text, int p_size, HorizontalAlignment p_alignment, float p_width, uint16_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
for (int i = 0; i < data.size(); i++) {
@@ -1454,7 +2239,7 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re
}
uint64_t hash = p_text.hash64();
- if (p_align == HALIGN_FILL) {
+ if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
hash = hash_djb2_one_64(p_flags, hash);
}
@@ -1471,7 +2256,7 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re
return buffer->get_size();
}
-Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint16_t p_flags) const {
+Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint16_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
for (int i = 0; i < data.size(); i++) {
@@ -1508,7 +2293,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int
return ret;
}
-void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
+void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
for (int i = 0; i < data.size(); i++) {
@@ -1516,7 +2301,7 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
}
uint64_t hash = p_text.hash64();
- if (p_align == HALIGN_FILL) {
+ if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
hash = hash_djb2_one_64(p_flags, hash);
}
@@ -1539,7 +2324,7 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
}
buffer->set_width(p_width);
- buffer->set_align(p_align);
+ buffer->set_horizontal_alignment(p_alignment);
buffer->set_flags(p_flags);
if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
@@ -1548,7 +2333,7 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
buffer->draw(p_canvas_item, ofs, p_modulate);
}
-void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
+void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
for (int i = 0; i < data.size(); i++) {
@@ -1571,7 +2356,7 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
cache_wrap.insert(wrp_hash, lines_buffer);
}
- lines_buffer->set_align(p_align);
+ lines_buffer->set_alignment(p_alignment);
Vector2 lofs = p_pos;
for (int i = 0; i < lines_buffer->get_line_count(); i++) {
@@ -1585,7 +2370,7 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
}
}
if (p_width > 0) {
- lines_buffer->set_align(p_align);
+ lines_buffer->set_alignment(p_alignment);
}
if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
@@ -1645,8 +2430,9 @@ real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char,
bool Font::has_char(char32_t p_char) const {
for (int i = 0; i < data.size(); i++) {
- if (data[i]->has_char(p_char))
+ if (data[i]->has_char(p_char)) {
return true;
+ }
}
return false;
}
@@ -1664,11 +2450,15 @@ String Font::get_supported_chars() const {
return chars;
}
-Vector<RID> Font::get_rids() const {
+Array Font::get_rids() const {
+ Array _rids;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
+ if (rids[i].is_valid()) {
+ _rids.push_back(rids[i]);
+ }
}
- return rids;
+ return _rids;
}
void Font::update_changes() {
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 4d9ee72c84..96bfe3678b 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#include "core/io/resource.h"
#include "core/templates/lru.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "scene/resources/texture.h"
#include "servers/text_server.h"
@@ -49,13 +49,17 @@ class FontData : public Resource {
PackedByteArray data;
bool antialiased = true;
+ bool mipmaps = false;
bool msdf = false;
int msdf_pixel_range = 16;
int msdf_size = 48;
int fixed_size = 0;
bool force_autohinter = false;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
real_t oversampling = 0.f;
+ real_t embolden = 0.f;
+ Transform2D transform;
// Cache.
mutable Vector<RID> cache;
@@ -63,6 +67,12 @@ class FontData : public Resource {
_FORCE_INLINE_ void _clear_cache();
_FORCE_INLINE_ void _ensure_rid(int p_cache_index) const;
+ void _convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz);
+ void _convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz);
+ void _convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz);
+ void _convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol);
+ void _convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol);
+
protected:
static void _bind_methods();
@@ -73,6 +83,9 @@ protected:
virtual void reset_state() override;
public:
+ Error load_bitmap_font(const String &p_path);
+ Error load_dynamic_font(const String &p_path);
+
// Font source data.
virtual void set_data_ptr(const uint8_t *p_data, size_t p_size);
virtual void set_data(const PackedByteArray &p_data);
@@ -91,6 +104,9 @@ public:
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_multichannel_signed_distance_field(bool p_msdf);
virtual bool is_multichannel_signed_distance_field() const;
@@ -109,6 +125,15 @@ public:
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_embolden(float p_strength);
+ virtual float get_embolden() const;
+
+ virtual void set_transform(Transform2D p_transform);
+ virtual Transform2D get_transform() const;
+
virtual void set_oversampling(real_t p_oversampling);
virtual real_t get_oversampling() const;
@@ -198,6 +223,9 @@ public:
virtual void remove_script_support_override(const String &p_script);
virtual Vector<String> get_script_support_overrides() const;
+ virtual void set_opentype_feature_overrides(const Dictionary &p_overrides);
+ virtual Dictionary get_opentype_feature_overrides() const;
+
// Base font properties.
virtual bool has_char(char32_t p_char) const;
virtual String get_supported_chars() const;
@@ -273,11 +301,11 @@ public:
virtual real_t get_underline_thickness(int p_size = DEFAULT_FONT_SIZE) const;
// Drawing string.
- virtual Size2 get_string_size(const String &p_text, int p_size = DEFAULT_FONT_SIZE, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = DEFAULT_FONT_SIZE, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
+ virtual Size2 get_string_size(const String &p_text, int p_size = DEFAULT_FONT_SIZE, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = DEFAULT_FONT_SIZE, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
- virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
// Helper functions.
virtual bool has_char(char32_t p_char) const;
@@ -287,7 +315,7 @@ public:
virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE) const;
virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
- Vector<RID> get_rids() const;
+ Array get_rids() const;
void update_changes();
diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp
index 4559b4ce0a..a9c44dc6bf 100644
--- a/scene/resources/gradient.cpp
+++ b/scene/resources/gradient.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -71,7 +71,7 @@ void Gradient::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_mode", PROPERTY_HINT_ENUM, "Linear,Constant,Cubic"), "set_interpolation_mode", "get_interpolation_mode");
- ADD_GROUP("Raw data", "");
+ ADD_GROUP("Raw Data", "");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
@@ -144,7 +144,7 @@ void Gradient::add_point(float p_offset, const Color &p_color) {
void Gradient::remove_point(int p_index) {
ERR_FAIL_INDEX(p_index, points.size());
ERR_FAIL_COND(points.size() <= 1);
- points.remove(p_index);
+ points.remove_at(p_index);
emit_signal(CoreStringNames::get_singleton()->changed);
}
@@ -157,7 +157,7 @@ void Gradient::reverse() {
emit_signal(CoreStringNames::get_singleton()->changed);
}
-void Gradient::set_points(Vector<Gradient::Point> &p_points) {
+void Gradient::set_points(const Vector<Gradient::Point> &p_points) {
points = p_points;
is_sorted = false;
emit_signal(CoreStringNames::get_singleton()->changed);
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index eb438d0bba..a3d3449099 100644
--- a/scene/resources/gradient.h
+++ b/scene/resources/gradient.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -73,7 +73,7 @@ public:
void add_point(float p_offset, const Color &p_color);
void remove_point(int p_index);
- void set_points(Vector<Point> &p_points);
+ void set_points(const Vector<Point> &p_points);
Vector<Point> &get_points();
void reverse();
diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp
index d1a958ad38..824dc4a544 100644
--- a/scene/resources/height_map_shape_3d.cpp
+++ b/scene/resources/height_map_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "height_map_shape_3d.h"
+
#include "servers/physics_server_3d.h"
Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
@@ -186,8 +187,8 @@ void HeightMapShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_map_data", "data"), &HeightMapShape3D::set_map_data);
ClassDB::bind_method(D_METHOD("get_map_data"), &HeightMapShape3D::get_map_data);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_width", "get_map_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_depth", "get_map_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_width", "get_map_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_depth", "get_map_depth");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "map_data"), "set_map_data", "get_map_data");
}
diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h
index 1273aee040..79d1b15674 100644
--- a/scene/resources/height_map_shape_3d.h
+++ b/scene/resources/height_map_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp
index fe7124de9e..044477e744 100644
--- a/scene/resources/immediate_mesh.cpp
+++ b/scene/resources/immediate_mesh.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -144,6 +144,7 @@ void ImmediateMesh::surface_add_vertex_2d(const Vector2 &p_vertex) {
active_surface_data.vertex_2d = true;
}
+
void ImmediateMesh::surface_end() {
ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
ERR_FAIL_COND_MSG(!vertices.size(), "No vertices were added, surface can't be created.");
@@ -185,7 +186,7 @@ void ImmediateMesh::surface_end() {
vtx[2] = vertices[i].z;
}
if (i == 0) {
- aabb.position = vertices[i];
+ aabb = AABB(vertices[i], SMALL_VEC3); // Must have a bit of size.
} else {
aabb.expand_to(vertices[i]);
}
@@ -210,7 +211,7 @@ void ImmediateMesh::surface_end() {
value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10;
value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20;
if (t.d > 0) {
- value |= 3 << 30;
+ value |= 3UL << 30;
}
*tangent = value;
@@ -381,7 +382,7 @@ AABB ImmediateMesh::get_aabb() const {
if (i == 0) {
aabb = surfaces[i].aabb;
} else {
- aabb.merge(surfaces[i].aabb);
+ aabb = aabb.merge(surfaces[i].aabb);
}
}
return aabb;
diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h
index 6673ee6f3d..e5f627ae8e 100644
--- a/scene/resources/immediate_mesh.h
+++ b/scene/resources/immediate_mesh.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -75,6 +75,8 @@ class ImmediateMesh : public Mesh {
Vector<uint8_t> surface_vertex_create_cache;
Vector<uint8_t> surface_attribute_create_cache;
+ const Vector3 SMALL_VEC3 = Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON);
+
protected:
static void _bind_methods();
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
index 7afa4c91f0..0fbd29b1a6 100644
--- a/scene/resources/importer_mesh.cpp
+++ b/scene/resources/importer_mesh.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -275,6 +275,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX];
Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV];
+ Vector<Vector2> uv2s = surfaces[i].arrays[RS::ARRAY_TEX_UV2];
unsigned int index_count = indices.size();
unsigned int vertex_count = vertices.size();
@@ -287,7 +288,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
const int *indices_ptr = indices.ptr();
if (normals.is_empty()) {
- normals.resize(vertices.size());
+ normals.resize(index_count);
Vector3 *n_ptr = normals.ptrw();
for (unsigned int j = 0; j < index_count; j += 3) {
const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]];
@@ -305,7 +306,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle));
const Vector3 *normals_ptr = normals.ptr();
- Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices;
+ HashMap<Vector3, LocalVector<Pair<int, int>>> unique_vertices;
LocalVector<int> vertex_remap;
LocalVector<int> vertex_inverse_remap;
@@ -313,22 +314,25 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
LocalVector<Vector3> merged_normals;
LocalVector<int> merged_normals_counts;
const Vector2 *uvs_ptr = uvs.ptr();
+ const Vector2 *uv2s_ptr = uv2s.ptr();
for (unsigned int j = 0; j < vertex_count; j++) {
const Vector3 &v = vertices_ptr[j];
const Vector3 &n = normals_ptr[j];
- Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v);
+ HashMap<Vector3, LocalVector<Pair<int, int>>>::Iterator E = unique_vertices.find(v);
if (E) {
- const LocalVector<Pair<int, int>> &close_verts = E->get();
+ const LocalVector<Pair<int, int>> &close_verts = E->value;
bool found = false;
for (unsigned int k = 0; k < close_verts.size(); k++) {
const Pair<int, int> &idx = close_verts[k];
- // TODO check more attributes?
- if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) {
+ bool is_uvs_close = (!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2);
+ bool is_uv2s_close = (!uv2s_ptr || uv2s_ptr[j].distance_squared_to(uv2s_ptr[idx.second]) < CMP_EPSILON2);
+ bool is_normals_close = normals[idx.second].dot(n) > normal_merge_threshold;
+ if (is_uvs_close && is_uv2s_close && is_normals_close) {
vertex_remap.push_back(idx.first);
merged_normals[idx.first] += normals[idx.second];
merged_normals_counts[idx.first]++;
@@ -415,7 +419,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
continue;
}
- if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) {
+ if (new_index_count == 0 || (new_index_count >= (index_count * 0.75f))) {
break;
}
@@ -485,7 +489,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
raycaster->intersect(rays);
LocalVector<Vector3> ray_normals;
- LocalVector<float> ray_normal_weights;
+ LocalVector<real_t> ray_normal_weights;
ray_normals.resize(new_index_count);
ray_normal_weights.resize(new_index_count);
@@ -517,10 +521,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
Vector3 normal = n0 * w + n1 * u + n2 * v;
Vector2 orig_uv = ray_uvs[j];
- float orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y };
+ const real_t orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y };
for (int k = 0; k < 3; k++) {
int idx = orig_tri_id * 3 + k;
- float weight = orig_bary[k];
+ real_t weight = orig_bary[k];
ray_normals[idx] += normal * weight;
ray_normal_weights[idx] += weight;
}
@@ -653,7 +657,7 @@ Ref<ArrayMesh> ImporterMesh::get_mesh(const Ref<ArrayMesh> &p_base) {
if (surfaces[i].material.is_valid()) {
mesh->surface_set_material(mesh->get_surface_count() - 1, surfaces[i].material);
}
- if (surfaces[i].name != String()) {
+ if (!surfaces[i].name.is_empty()) {
mesh->surface_set_name(mesh->get_surface_count() - 1, surfaces[i].name);
}
}
@@ -702,15 +706,15 @@ void ImporterMesh::create_shadow_mesh() {
Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
int vertex_count = vertices.size();
{
- Map<Vector3, int> unique_vertices;
+ HashMap<Vector3, int> unique_vertices;
const Vector3 *vptr = vertices.ptr();
for (int j = 0; j < vertex_count; j++) {
const Vector3 &v = vptr[j];
- Map<Vector3, int>::Element *E = unique_vertices.find(v);
+ HashMap<Vector3, int>::Iterator E = unique_vertices.find(v);
if (E) {
- vertex_remap.push_back(E->get());
+ vertex_remap.push_back(E->value);
} else {
int vcount = unique_vertices.size();
unique_vertices[v] = vcount;
@@ -839,7 +843,7 @@ Dictionary ImporterMesh::_get_data() const {
d["material"] = surfaces[i].material;
}
- if (surfaces[i].name != String()) {
+ if (!surfaces[i].name.is_empty()) {
d["name"] = surfaces[i].name;
}
@@ -894,16 +898,16 @@ Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecomposit
Vector<uint32_t> indices;
indices.resize(face_count * 3);
{
- Map<Vector3, uint32_t> vertex_map;
+ HashMap<Vector3, uint32_t> vertex_map;
Vector3 *vertex_w = vertices.ptrw();
uint32_t *index_w = indices.ptrw();
for (int i = 0; i < face_count; i++) {
for (int j = 0; j < 3; j++) {
const Vector3 &vertex = faces[i].vertex[j];
- Map<Vector3, uint32_t>::Element *found_vertex = vertex_map.find(vertex);
+ HashMap<Vector3, uint32_t>::Iterator found_vertex = vertex_map.find(vertex);
uint32_t index;
if (found_vertex) {
- index = found_vertex->get();
+ index = found_vertex->value;
} else {
index = ++vertex_count;
vertex_map[vertex] = index;
@@ -956,7 +960,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
return Ref<NavigationMesh>();
}
- Map<Vector3, int> unique_vertices;
+ HashMap<Vector3, int> unique_vertices;
LocalVector<int> face_indices;
for (int i = 0; i < faces.size(); i++) {
@@ -1019,7 +1023,7 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform,
// Keep only the scale
Basis basis = p_base_transform.get_basis();
- Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
+ Vector3 scale = Vector3(basis.get_column(0).length(), basis.get_column(1).length(), basis.get_column(2).length());
Transform3D transform;
transform.scale(scale);
diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h
index 8576312a8a..8f77597a58 100644
--- a/scene/resources/importer_mesh.h
+++ b/scene/resources/importer_mesh.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 8399b14a56..b8171ca4bd 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -84,10 +84,41 @@ void Material::inspect_native_shader_code() {
SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
RID shader = get_shader_rid();
if (st && shader.is_valid()) {
- st->call_group("_native_shader_source_visualizer", "_inspect_shader", shader);
+ st->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_native_shader_source_visualizer", "_inspect_shader", shader);
}
}
+RID Material::get_shader_rid() const {
+ RID ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret)) {
+ return ret;
+ }
+ return RID();
+}
+Shader::Mode Material::get_shader_mode() const {
+ Shader::Mode ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret)) {
+ return ret;
+ }
+
+ return Shader::MODE_MAX;
+}
+
+bool Material::_can_do_next_pass() const {
+ bool ret;
+ if (GDVIRTUAL_CALL(_can_do_next_pass, ret)) {
+ return ret;
+ }
+ return false;
+}
+bool Material::_can_use_render_priority() const {
+ bool ret;
+ if (GDVIRTUAL_CALL(_can_use_render_priority, ret)) {
+ return ret;
+ }
+ return false;
+}
+
void Material::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_next_pass", "next_pass"), &Material::set_next_pass);
ClassDB::bind_method(D_METHOD("get_next_pass"), &Material::get_next_pass);
@@ -103,6 +134,11 @@ void Material::_bind_methods() {
BIND_CONSTANT(RENDER_PRIORITY_MAX);
BIND_CONSTANT(RENDER_PRIORITY_MIN);
+
+ GDVIRTUAL_BIND(_get_shader_rid)
+ GDVIRTUAL_BIND(_get_shader_mode)
+ GDVIRTUAL_BIND(_can_do_next_pass)
+ GDVIRTUAL_BIND(_can_use_render_priority)
}
Material::Material() {
@@ -151,9 +187,9 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const {
}
if (pr) {
- const Map<StringName, Variant>::Element *E = param_cache.find(pr);
+ HashMap<StringName, Variant>::ConstIterator E = param_cache.find(pr);
if (E) {
- r_ret = E->get();
+ r_ret = E->value;
} else {
r_ret = Variant();
}
@@ -312,7 +348,7 @@ ShaderMaterial::~ShaderMaterial() {
Mutex BaseMaterial3D::material_mutex;
SelfList<BaseMaterial3D>::List *BaseMaterial3D::dirty_materials = nullptr;
-Map<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData> BaseMaterial3D::shader_map;
+HashMap<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData, BaseMaterial3D::MaterialKey> BaseMaterial3D::shader_map;
BaseMaterial3D::ShaderNames *BaseMaterial3D::shader_names = nullptr;
void BaseMaterial3D::init_shaders() {
@@ -330,7 +366,7 @@ void BaseMaterial3D::init_shaders() {
shader_names->rim = "rim";
shader_names->rim_tint = "rim_tint";
shader_names->clearcoat = "clearcoat";
- shader_names->clearcoat_gloss = "clearcoat_gloss";
+ shader_names->clearcoat_roughness = "clearcoat_roughness";
shader_names->anisotropy = "anisotropy_ratio";
shader_names->heightmap_scale = "heightmap_scale";
shader_names->subsurface_scattering_strength = "subsurface_scattering_strength";
@@ -359,6 +395,9 @@ void BaseMaterial3D::init_shaders() {
shader_names->distance_fade_min = "distance_fade_min";
shader_names->distance_fade_max = "distance_fade_max";
+ shader_names->msdf_pixel_range = "msdf_pixel_range";
+ shader_names->msdf_outline_size = "msdf_outline_size";
+
shader_names->metallic_texture_channel = "metallic_texture_channel";
shader_names->ao_texture_channel = "ao_texture_channel";
shader_names->clearcoat_texture_channel = "clearcoat_texture_channel";
@@ -396,12 +435,10 @@ void BaseMaterial3D::init_shaders() {
shader_names->albedo_texture_size = "albedo_texture_size";
}
-Ref<StandardMaterial3D> BaseMaterial3D::materials_for_2d[BaseMaterial3D::MAX_MATERIALS_FOR_2D];
+HashMap<uint64_t, Ref<StandardMaterial3D>> BaseMaterial3D::materials_for_2d;
void BaseMaterial3D::finish_shaders() {
- for (int i = 0; i < MAX_MATERIALS_FOR_2D; i++) {
- materials_for_2d[i].unref();
- }
+ materials_for_2d.clear();
memdelete(dirty_materials);
dirty_materials = nullptr;
@@ -449,10 +486,10 @@ void BaseMaterial3D::_update_shader() {
texfilter_str = "filter_linear_mipmap";
break;
case TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC:
- texfilter_str = "filter_nearest_mipmap_aniso";
+ texfilter_str = "filter_nearest_mipmap_anisotropic";
break;
case TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC:
- texfilter_str = "filter_linear_mipmap_aniso";
+ texfilter_str = "filter_linear_mipmap_anisotropic";
break;
case TEXTURE_FILTER_MAX:
break; // Internal value, skip.
@@ -541,12 +578,6 @@ void BaseMaterial3D::_update_shader() {
case SPECULAR_SCHLICK_GGX:
code += ",specular_schlick_ggx";
break;
- case SPECULAR_BLINN:
- code += ",specular_blinn";
- break;
- case SPECULAR_PHONG:
- code += ",specular_phong";
- break;
case SPECULAR_TOON:
code += ",specular_toon";
break;
@@ -614,6 +645,11 @@ void BaseMaterial3D::_update_shader() {
code += "uniform float distance_fade_max;\n";
}
+ if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) {
+ code += "uniform float msdf_pixel_range;\n";
+ code += "uniform float msdf_outline_size;\n";
+ }
+
// alpha scissor is only valid if there is not antialiasing edge
// alpha hash is valid whenever, but not with alpha scissor
if (transparency == TRANSPARENCY_ALPHA_SCISSOR) {
@@ -690,12 +726,12 @@ void BaseMaterial3D::_update_shader() {
}
if (features[FEATURE_CLEARCOAT]) {
code += "uniform float clearcoat : hint_range(0,1);\n";
- code += "uniform float clearcoat_gloss : hint_range(0,1);\n";
+ code += "uniform float clearcoat_roughness : hint_range(0,1);\n";
code += "uniform sampler2D texture_clearcoat : hint_white," + texfilter_str + ";\n";
}
if (features[FEATURE_ANISOTROPY]) {
code += "uniform float anisotropy_ratio : hint_range(0,256);\n";
- code += "uniform sampler2D texture_flowmap : hint_aniso," + texfilter_str + ";\n";
+ code += "uniform sampler2D texture_flowmap : hint_anisotropy," + texfilter_str + ";\n";
}
if (features[FEATURE_AMBIENT_OCCLUSION]) {
code += "uniform sampler2D texture_ambient_occlusion : hint_white, " + texfilter_str + ";\n";
@@ -779,26 +815,26 @@ void BaseMaterial3D::_update_shader() {
case BILLBOARD_DISABLED: {
} break;
case BILLBOARD_ENABLED: {
- code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],CAMERA_MATRIX[1],CAMERA_MATRIX[2],WORLD_MATRIX[3]);\n";
+ code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat4(INV_VIEW_MATRIX[0], INV_VIEW_MATRIX[1], INV_VIEW_MATRIX[2], MODEL_MATRIX[3]);\n";
if (flags[FLAG_BILLBOARD_KEEP_SCALE]) {
- code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
}
} break;
case BILLBOARD_FIXED_Y: {
- code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), CAMERA_MATRIX[2].xyz)),0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(normalize(cross(CAMERA_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))),0.0),WORLD_MATRIX[3]);\n";
+ code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), INV_VIEW_MATRIX[2].xyz)), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))), 0.0), MODEL_MATRIX[3]);\n";
if (flags[FLAG_BILLBOARD_KEEP_SCALE]) {
- code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
}
} break;
case BILLBOARD_PARTICLES: {
//make billboard
- code += " mat4 mat_world = mat4(normalize(CAMERA_MATRIX[0])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[1])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[2])*length(WORLD_MATRIX[2]),WORLD_MATRIX[3]);\n";
+ code += " mat4 mat_world = mat4(normalize(INV_VIEW_MATRIX[0]) * length(MODEL_MATRIX[0]), normalize(INV_VIEW_MATRIX[1]) * length(MODEL_MATRIX[0]),normalize(INV_VIEW_MATRIX[2]) * length(MODEL_MATRIX[2]), MODEL_MATRIX[3]);\n";
//rotate by rotation
- code += " mat_world = mat_world * mat4( vec4(cos(INSTANCE_CUSTOM.x),-sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0),vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " mat_world = mat_world * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
//set modelview
- code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n";
+ code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat_world;\n";
//handle animation
code += " float h_frames = float(particles_anim_h_frames);\n";
@@ -853,8 +889,8 @@ void BaseMaterial3D::_update_shader() {
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
if (flags[FLAG_UV1_USE_WORLD_TRIPLANAR]) {
- code += " uv1_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL),vec3(uv1_blend_sharpness));\n";
- code += " uv1_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv1_scale + uv1_offset;\n";
+ code += " uv1_power_normal=pow(abs(mat3(MODEL_MATRIX) * NORMAL),vec3(uv1_blend_sharpness));\n";
+ code += " uv1_triplanar_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv1_scale + uv1_offset;\n";
} else {
code += " uv1_power_normal=pow(abs(NORMAL),vec3(uv1_blend_sharpness));\n";
code += " uv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;\n";
@@ -865,8 +901,8 @@ void BaseMaterial3D::_update_shader() {
if (flags[FLAG_UV2_USE_TRIPLANAR]) {
if (flags[FLAG_UV2_USE_WORLD_TRIPLANAR]) {
- code += " uv2_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL), vec3(uv2_blend_sharpness));\n";
- code += " uv2_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv2_scale + uv2_offset;\n";
+ code += " uv2_power_normal=pow(abs(mat3(MODEL_MATRIX) * NORMAL), vec3(uv2_blend_sharpness));\n";
+ code += " uv2_triplanar_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv2_scale + uv2_offset;\n";
} else {
code += " uv2_power_normal=pow(abs(NORMAL), vec3(uv2_blend_sharpness));\n";
code += " uv2_triplanar_pos = VERTEX * uv2_scale + uv2_offset;\n";
@@ -881,6 +917,12 @@ void BaseMaterial3D::_update_shader() {
code += "}\n";
code += "\n\n";
+ if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) {
+ code += "float msdf_median(float r, float g, float b, float a) {\n";
+ code += " return min(max(min(r, g), min(max(r, g), b)), a);\n";
+ code += "}\n";
+ }
+ code += "\n\n";
if (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR]) {
code += "vec4 triplanar_texture(sampler2D p_sampler,vec3 p_weights,vec3 p_triplanar_pos) {\n";
code += " vec4 samp=vec4(0.0);\n";
@@ -980,7 +1022,30 @@ void BaseMaterial3D::_update_shader() {
}
}
- if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) {
+ if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) {
+ code += " {\n";
+ code += " albedo_tex.rgb = mix(vec3(1.0 + 0.055) * pow(albedo_tex.rgb, vec3(1.0 / 2.4)) - vec3(0.055), vec3(12.92) * albedo_tex.rgb.rgb, lessThan(albedo_tex.rgb, vec3(0.0031308)));\n";
+ code += " vec2 msdf_size = vec2(msdf_pixel_range) / vec2(textureSize(texture_albedo, 0));\n";
+ if (flags[FLAG_USE_POINT_SIZE]) {
+ code += " vec2 dest_size = vec2(1.0) / fwidth(POINT_COORD);\n";
+ } else {
+ if (flags[FLAG_UV1_USE_TRIPLANAR]) {
+ code += " vec2 dest_size = vec2(1.0) / fwidth(uv1_triplanar_pos);\n";
+ } else {
+ code += " vec2 dest_size = vec2(1.0) / fwidth(base_uv);\n";
+ }
+ }
+ code += " float px_size = max(0.5 * dot(msdf_size, dest_size), 1.0);\n";
+ code += " float d = msdf_median(albedo_tex.r, albedo_tex.g, albedo_tex.b, albedo_tex.a) - 0.5;\n";
+ code += " if (msdf_outline_size > 0.0) {\n";
+ code += " float cr = clamp(msdf_outline_size, 0.0, msdf_pixel_range / 2.0) / msdf_pixel_range;\n";
+ code += " albedo_tex.a = clamp((d + cr) * px_size, 0.0, 1.0);\n";
+ code += " } else {\n";
+ code += " albedo_tex.a = clamp(d * px_size + 0.5, 0.0, 1.0);\n";
+ code += " }\n";
+ code += " albedo_tex.rgb = vec3(1.0);\n";
+ code += " }\n";
+ } else if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) {
code += " albedo_tex.rgb = mix(pow((albedo_tex.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)),vec3(2.4)),albedo_tex.rgb.rgb * (1.0 / 12.92),lessThan(albedo_tex.rgb,vec3(0.04045)));\n";
}
@@ -1110,7 +1175,7 @@ void BaseMaterial3D::_update_shader() {
if (!RenderingServer::get_singleton()->is_low_end()) {
code += " {\n";
if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) {
- code += " float fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n";
+ code += " float fade_distance = abs((VIEW_MATRIX * MODEL_MATRIX[3]).z);\n";
} else {
code += " float fade_distance=-VERTEX.z;\n";
@@ -1166,7 +1231,7 @@ void BaseMaterial3D::_update_shader() {
code += " vec2 clearcoat_tex = texture(texture_clearcoat,base_uv).xy;\n";
}
code += " CLEARCOAT = clearcoat*clearcoat_tex.x;";
- code += " CLEARCOAT_GLOSS = clearcoat_gloss*clearcoat_tex.y;\n";
+ code += " CLEARCOAT_ROUGHNESS = clearcoat_roughness*clearcoat_tex.y;\n";
}
if (features[FEATURE_ANISOTROPY]) {
@@ -1408,13 +1473,13 @@ float BaseMaterial3D::get_clearcoat() const {
return clearcoat;
}
-void BaseMaterial3D::set_clearcoat_gloss(float p_clearcoat_gloss) {
- clearcoat_gloss = p_clearcoat_gloss;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat_gloss, p_clearcoat_gloss);
+void BaseMaterial3D::set_clearcoat_roughness(float p_clearcoat_roughness) {
+ clearcoat_roughness = p_clearcoat_roughness;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat_roughness, p_clearcoat_roughness);
}
-float BaseMaterial3D::get_clearcoat_gloss() const {
- return clearcoat_gloss;
+float BaseMaterial3D::get_clearcoat_roughness() const {
+ return clearcoat_roughness;
}
void BaseMaterial3D::set_anisotropy(float p_anisotropy) {
@@ -1658,13 +1723,16 @@ bool BaseMaterial3D::get_feature(Feature p_feature) const {
void BaseMaterial3D::set_texture(TextureParam p_param, const Ref<Texture2D> &p_texture) {
ERR_FAIL_INDEX(p_param, TEXTURE_MAX);
+
textures[p_param] = p_texture;
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
RS::get_singleton()->material_set_param(_get_material(), shader_names->texture_names[p_param], rid);
+
if (p_texture.is_valid() && p_param == TEXTURE_ALBEDO) {
RS::get_singleton()->material_set_param(_get_material(), shader_names->albedo_texture_size,
Vector2i(p_texture->get_width(), p_texture->get_height()));
}
+
notify_property_list_changed();
_queue_shader_change();
}
@@ -1720,8 +1788,6 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
_validate_high_end("refraction", property);
_validate_high_end("subsurf_scatter", property);
- _validate_high_end("anisotropy", property);
- _validate_high_end("clearcoat", property);
_validate_high_end("heightmap", property);
if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) {
@@ -1744,6 +1810,14 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
property.usage = PROPERTY_USAGE_NO_EDITOR;
}
+ if (property.name == "msdf_pixel_range" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+
+ if (property.name == "msdf_outline_size" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+
if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) {
property.usage = PROPERTY_USAGE_NO_EDITOR;
}
@@ -2092,35 +2166,45 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel()
return refraction_texture_channel;
}
-Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y, RID *r_shader_rid) {
- int version = 0;
+Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y, bool p_msdf, bool p_no_depth, bool p_fixed_size, TextureFilter p_filter, RID *r_shader_rid) {
+ int64_t hash = 0;
if (p_shaded) {
- version = 1;
+ hash |= 1 << 0;
}
if (p_transparent) {
- version |= 2;
+ hash |= 1 << 1;
}
if (p_cut_alpha) {
- version |= 4;
+ hash |= 1 << 2;
}
if (p_opaque_prepass) {
- version |= 8;
+ hash |= 1 << 3;
}
if (p_double_sided) {
- version |= 16;
+ hash |= 1 << 4;
}
if (p_billboard) {
- version |= 32;
+ hash |= 1 << 5;
}
if (p_billboard_y) {
- version |= 64;
+ hash |= 1 << 6;
+ }
+ if (p_msdf) {
+ hash |= 1 << 7;
+ }
+ if (p_no_depth) {
+ hash |= 1 << 8;
+ }
+ if (p_fixed_size) {
+ hash |= 1 << 9;
}
+ hash = hash_djb2_one_64(p_filter, hash);
- if (materials_for_2d[version].is_valid()) {
+ if (materials_for_2d.has(hash)) {
if (r_shader_rid) {
- *r_shader_rid = materials_for_2d[version]->get_shader_rid();
+ *r_shader_rid = materials_for_2d[hash]->get_shader_rid();
}
- return materials_for_2d[version];
+ return materials_for_2d[hash];
}
Ref<StandardMaterial3D> material;
@@ -2131,18 +2215,22 @@ Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transpar
material->set_cull_mode(p_double_sided ? CULL_DISABLED : CULL_BACK);
material->set_flag(FLAG_SRGB_VERTEX_COLOR, true);
material->set_flag(FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ material->set_flag(FLAG_ALBEDO_TEXTURE_MSDF, p_msdf);
+ material->set_flag(FLAG_DISABLE_DEPTH_TEST, p_no_depth);
+ material->set_flag(FLAG_FIXED_SIZE, p_fixed_size);
+ material->set_texture_filter(p_filter);
if (p_billboard || p_billboard_y) {
material->set_flag(FLAG_BILLBOARD_KEEP_SCALE, true);
material->set_billboard_mode(p_billboard_y ? BILLBOARD_FIXED_Y : BILLBOARD_ENABLED);
}
- materials_for_2d[version] = material;
+ materials_for_2d[hash] = material;
if (r_shader_rid) {
- *r_shader_rid = materials_for_2d[version]->get_shader_rid();
+ *r_shader_rid = materials_for_2d[hash]->get_shader_rid();
}
- return materials_for_2d[version];
+ return materials_for_2d[hash];
}
void BaseMaterial3D::set_on_top_of_alpha() {
@@ -2170,6 +2258,24 @@ float BaseMaterial3D::get_proximity_fade_distance() const {
return proximity_fade_distance;
}
+void BaseMaterial3D::set_msdf_pixel_range(float p_range) {
+ msdf_pixel_range = p_range;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_pixel_range, p_range);
+}
+
+float BaseMaterial3D::get_msdf_pixel_range() const {
+ return msdf_pixel_range;
+}
+
+void BaseMaterial3D::set_msdf_outline_size(float p_size) {
+ msdf_outline_size = p_size;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_outline_size, p_size);
+}
+
+float BaseMaterial3D::get_msdf_outline_size() const {
+ return msdf_outline_size;
+}
+
void BaseMaterial3D::set_distance_fade(DistanceFadeMode p_mode) {
distance_fade = p_mode;
_queue_shader_change();
@@ -2212,7 +2318,9 @@ BaseMaterial3D::EmissionOperator BaseMaterial3D::get_emission_operator() const {
RID BaseMaterial3D::get_shader_rid() const {
MutexLock lock(material_mutex);
- ((BaseMaterial3D *)this)->_update_shader();
+ if (element.in_list()) { // _is_shader_dirty() would create anoder mutex lock
+ ((BaseMaterial3D *)this)->_update_shader();
+ }
ERR_FAIL_COND_V(!shader_map.has(current_key), RID());
return shader_map[current_key].shader;
}
@@ -2266,8 +2374,8 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_clearcoat", "clearcoat"), &BaseMaterial3D::set_clearcoat);
ClassDB::bind_method(D_METHOD("get_clearcoat"), &BaseMaterial3D::get_clearcoat);
- ClassDB::bind_method(D_METHOD("set_clearcoat_gloss", "clearcoat_gloss"), &BaseMaterial3D::set_clearcoat_gloss);
- ClassDB::bind_method(D_METHOD("get_clearcoat_gloss"), &BaseMaterial3D::get_clearcoat_gloss);
+ ClassDB::bind_method(D_METHOD("set_clearcoat_roughness", "clearcoat_roughness"), &BaseMaterial3D::set_clearcoat_roughness);
+ ClassDB::bind_method(D_METHOD("get_clearcoat_roughness"), &BaseMaterial3D::get_clearcoat_roughness);
ClassDB::bind_method(D_METHOD("set_anisotropy", "anisotropy"), &BaseMaterial3D::set_anisotropy);
ClassDB::bind_method(D_METHOD("get_anisotropy"), &BaseMaterial3D::get_anisotropy);
@@ -2410,6 +2518,12 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_proximity_fade_distance", "distance"), &BaseMaterial3D::set_proximity_fade_distance);
ClassDB::bind_method(D_METHOD("get_proximity_fade_distance"), &BaseMaterial3D::get_proximity_fade_distance);
+ ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "range"), &BaseMaterial3D::set_msdf_pixel_range);
+ ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &BaseMaterial3D::get_msdf_pixel_range);
+
+ ClassDB::bind_method(D_METHOD("set_msdf_outline_size", "size"), &BaseMaterial3D::set_msdf_outline_size);
+ ClassDB::bind_method(D_METHOD("get_msdf_outline_size"), &BaseMaterial3D::get_msdf_outline_size);
+
ClassDB::bind_method(D_METHOD("set_distance_fade", "mode"), &BaseMaterial3D::set_distance_fade);
ClassDB::bind_method(D_METHOD("get_distance_fade"), &BaseMaterial3D::get_distance_fade);
@@ -2433,7 +2547,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_GROUP("Shading", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,Per-Pixel,Per-Vertex"), "set_shading_mode", "get_shading_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "diffuse_mode", PROPERTY_HINT_ENUM, "Burley,Lambert,Lambert Wrap,Toon"), "set_diffuse_mode", "get_diffuse_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Blinn,Phong,Toon,Disabled"), "set_specular_mode", "get_specular_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Toon,Disabled"), "set_specular_mode", "get_specular_mode");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT);
ADD_GROUP("Vertex Color", "vertex_color");
@@ -2444,6 +2558,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "albedo_color"), "set_albedo", "get_albedo");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "albedo_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_ALBEDO);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "albedo_tex_msdf"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_MSDF);
ADD_GROUP("ORM", "orm_");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orm_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_ORM);
@@ -2481,7 +2596,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_GROUP("Clearcoat", "clearcoat_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "clearcoat_enabled"), "set_feature", "get_feature", FEATURE_CLEARCOAT);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat", "get_clearcoat");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat_gloss", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat_gloss", "get_clearcoat_gloss");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat_roughness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat_roughness", "get_clearcoat_roughness");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "clearcoat_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_CLEARCOAT);
ADD_GROUP("Anisotropy", "anisotropy_");
@@ -2554,7 +2669,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "uv2_world_triplanar"), "set_flag", "get_flag", FLAG_UV2_USE_WORLD_TRIPLANAR);
ADD_GROUP("Sampling", "texture_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "texture_repeat"), "set_flag", "get_flag", FLAG_USE_TEXTURE_REPEAT);
ADD_GROUP("Shadows", "");
@@ -2581,6 +2696,9 @@ void BaseMaterial3D::_bind_methods() {
ADD_GROUP("Proximity Fade", "proximity_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "proximity_fade_enable"), "set_proximity_fade", "is_proximity_fade_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_proximity_fade_distance", "get_proximity_fade_distance");
+ ADD_GROUP("MSDF", "msdf_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), "set_msdf_pixel_range", "get_msdf_pixel_range");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_outline_size", PROPERTY_HINT_RANGE, "1,250,1"), "set_msdf_outline_size", "get_msdf_outline_size");
ADD_GROUP("Distance Fade", "distance_fade_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "distance_fade_mode", PROPERTY_HINT_ENUM, "Disabled,PixelAlpha,PixelDither,ObjectDither"), "set_distance_fade", "get_distance_fade");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_distance_fade_min_distance", "get_distance_fade_min_distance");
@@ -2680,6 +2798,7 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_INVERT_HEIGHTMAP);
BIND_ENUM_CONSTANT(FLAG_SUBSURFACE_MODE_SKIN);
BIND_ENUM_CONSTANT(FLAG_PARTICLE_TRAILS_MODE);
+ BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_MSDF);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
@@ -2688,8 +2807,6 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(DIFFUSE_TOON);
BIND_ENUM_CONSTANT(SPECULAR_SCHLICK_GGX);
- BIND_ENUM_CONSTANT(SPECULAR_BLINN);
- BIND_ENUM_CONSTANT(SPECULAR_PHONG);
BIND_ENUM_CONSTANT(SPECULAR_TOON);
BIND_ENUM_CONSTANT(SPECULAR_DISABLED);
@@ -2727,7 +2844,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_rim(1.0);
set_rim_tint(0.5);
set_clearcoat(1);
- set_clearcoat_gloss(0.5);
+ set_clearcoat_roughness(0.5);
set_anisotropy(0);
set_heightmap_scale(0.05);
set_subsurface_scattering_strength(0);
@@ -2771,6 +2888,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_heightmap_deep_parallax_max_layers(32);
set_heightmap_deep_parallax_flip_tangent(false); //also sets binormal
+ flags[FLAG_ALBEDO_TEXTURE_MSDF] = false;
flags[FLAG_USE_TEXTURE_REPEAT] = true;
is_initialized = true;
@@ -2884,10 +3002,13 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value)
idx++;
}
- print_line("remapped parameter not found: " + String(p_name));
+ WARN_PRINT("Godot 3.x SpatialMaterial remapped parameter not found: " + String(p_name));
return true;
}
return false;
}
+
#endif // DISABLE_DEPRECATED
+
+///////////////////////
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 798f7568df..b845fd68c8 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,6 @@
#include "core/templates/self_list.h"
#include "scene/resources/shader.h"
#include "scene/resources/texture.h"
-#include "servers/rendering/shader_language.h"
#include "servers/rendering_server.h"
class Material : public Resource {
@@ -52,11 +51,15 @@ class Material : public Resource {
protected:
_FORCE_INLINE_ RID _get_material() const { return material; }
static void _bind_methods();
- virtual bool _can_do_next_pass() const { return false; }
- virtual bool _can_use_render_priority() const { return false; }
+ virtual bool _can_do_next_pass() const;
+ virtual bool _can_use_render_priority() const;
void _validate_property(PropertyInfo &property) const override;
+ GDVIRTUAL0RC(RID, _get_shader_rid)
+ GDVIRTUAL0RC(Shader::Mode, _get_shader_mode)
+ GDVIRTUAL0RC(bool, _can_do_next_pass)
+ GDVIRTUAL0RC(bool, _can_use_render_priority)
public:
enum {
RENDER_PRIORITY_MAX = RS::MATERIAL_RENDER_PRIORITY_MAX,
@@ -69,9 +72,8 @@ public:
int get_render_priority() const;
virtual RID get_rid() const override;
- virtual RID get_shader_rid() const = 0;
-
- virtual Shader::Mode get_shader_mode() const = 0;
+ virtual RID get_shader_rid() const;
+ virtual Shader::Mode get_shader_mode() const;
Material();
virtual ~Material();
};
@@ -80,7 +82,7 @@ class ShaderMaterial : public Material {
GDCLASS(ShaderMaterial, Material);
Ref<Shader> shader;
- Map<StringName, Variant> param_cache;
+ HashMap<StringName, Variant> param_cache;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -240,6 +242,7 @@ public:
FLAG_INVERT_HEIGHTMAP,
FLAG_SUBSURFACE_MODE_SKIN,
FLAG_PARTICLE_TRAILS_MODE,
+ FLAG_ALBEDO_TEXTURE_MSDF,
FLAG_MAX
};
@@ -253,8 +256,6 @@ public:
enum SpecularMode {
SPECULAR_SCHLICK_GGX,
- SPECULAR_BLINN,
- SPECULAR_PHONG,
SPECULAR_TOON,
SPECULAR_DISABLED,
SPECULAR_MAX
@@ -322,6 +323,9 @@ private:
memset(this, 0, sizeof(MaterialKey));
}
+ static uint32_t hash(const MaterialKey &p_key) {
+ return hash_djb2_buffer((const uint8_t *)&p_key, sizeof(MaterialKey));
+ }
bool operator==(const MaterialKey &p_key) const {
return memcmp(this, &p_key, sizeof(MaterialKey)) == 0;
}
@@ -336,7 +340,7 @@ private:
int users = 0;
};
- static Map<MaterialKey, ShaderData> shader_map;
+ static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map;
MaterialKey current_key;
@@ -388,7 +392,7 @@ private:
StringName rim;
StringName rim_tint;
StringName clearcoat;
- StringName clearcoat_gloss;
+ StringName clearcoat_roughness;
StringName anisotropy;
StringName heightmap_scale;
StringName subsurface_scattering_strength;
@@ -412,6 +416,8 @@ private:
StringName uv2_blend_sharpness;
StringName grow;
StringName proximity_fade_distance;
+ StringName msdf_pixel_range;
+ StringName msdf_outline_size;
StringName distance_fade_min;
StringName distance_fade_max;
StringName ao_light_affect;
@@ -446,36 +452,36 @@ private:
bool orm;
Color albedo;
- float specular;
- float metallic;
- float roughness;
+ float specular = 0.0f;
+ float metallic = 0.0f;
+ float roughness = 0.0f;
Color emission;
- float emission_energy;
- float normal_scale;
- float rim;
- float rim_tint;
- float clearcoat;
- float clearcoat_gloss;
- float anisotropy;
- float heightmap_scale;
- float subsurface_scattering_strength;
- float transmittance_amount;
+ float emission_energy = 0.0f;
+ float normal_scale = 0.0f;
+ float rim = 0.0f;
+ float rim_tint = 0.0f;
+ float clearcoat = 0.0f;
+ float clearcoat_roughness = 0.0f;
+ float anisotropy = 0.0f;
+ float heightmap_scale = 0.0f;
+ float subsurface_scattering_strength = 0.0f;
+ float transmittance_amount = 0.0f;
Color transmittance_color;
- float transmittance_depth;
- float transmittance_boost;
+ float transmittance_depth = 0.0f;
+ float transmittance_boost = 0.0f;
Color backlight;
- float refraction;
- float point_size;
- float alpha_scissor_threshold;
- float alpha_hash_scale;
- float alpha_antialiasing_edge;
+ float refraction = 0.0f;
+ float point_size = 0.0f;
+ float alpha_scissor_threshold = 0.0f;
+ float alpha_hash_scale = 0.0f;
+ float alpha_antialiasing_edge = 0.0f;
bool grow_enabled = false;
- float ao_light_affect;
- float grow;
- int particles_anim_h_frames;
- int particles_anim_v_frames;
- bool particles_anim_loop;
+ float ao_light_affect = 0.0f;
+ float grow = 0.0f;
+ int particles_anim_h_frames = 0;
+ int particles_anim_v_frames = 0;
+ bool particles_anim_loop = false;
Transparency transparency = TRANSPARENCY_DISABLED;
ShadingMode shading_mode = SHADING_MODE_PER_PIXEL;
@@ -483,26 +489,29 @@ private:
Vector3 uv1_scale;
Vector3 uv1_offset;
- float uv1_triplanar_sharpness;
+ float uv1_triplanar_sharpness = 0.0f;
Vector3 uv2_scale;
Vector3 uv2_offset;
- float uv2_triplanar_sharpness;
+ float uv2_triplanar_sharpness = 0.0f;
DetailUV detail_uv = DETAIL_UV_1;
bool deep_parallax = false;
- int deep_parallax_min_layers;
- int deep_parallax_max_layers;
+ int deep_parallax_min_layers = 0;
+ int deep_parallax_max_layers = 0;
bool heightmap_parallax_flip_tangent = false;
bool heightmap_parallax_flip_binormal = false;
bool proximity_fade_enabled = false;
- float proximity_fade_distance;
+ float proximity_fade_distance = 0.0f;
+
+ float msdf_pixel_range = 4.f;
+ float msdf_outline_size = 0.f;
DistanceFadeMode distance_fade = DISTANCE_FADE_DISABLED;
- float distance_fade_max_distance;
- float distance_fade_min_distance;
+ float distance_fade_max_distance = 0.0f;
+ float distance_fade_min_distance = 0.0f;
BlendMode blend_mode = BLEND_MODE_MIX;
BlendMode detail_blend_mode = BLEND_MODE_MIX;
@@ -527,9 +536,7 @@ private:
_FORCE_INLINE_ void _validate_feature(const String &text, Feature feature, PropertyInfo &property) const;
- static const int MAX_MATERIALS_FOR_2D = 128;
-
- static Ref<StandardMaterial3D> materials_for_2d[MAX_MATERIALS_FOR_2D]; //used by Sprite3D and other stuff
+ static HashMap<uint64_t, Ref<StandardMaterial3D>> materials_for_2d; //used by Sprite3D, Label3D and other stuff
void _validate_high_end(const String &text, PropertyInfo &property) const;
@@ -573,8 +580,8 @@ public:
void set_clearcoat(float p_clearcoat);
float get_clearcoat() const;
- void set_clearcoat_gloss(float p_clearcoat_gloss);
- float get_clearcoat_gloss() const;
+ void set_clearcoat_roughness(float p_clearcoat_roughness);
+ float get_clearcoat_roughness() const;
void set_anisotropy(float p_anisotropy);
float get_anisotropy() const;
@@ -714,6 +721,12 @@ public:
void set_proximity_fade_distance(float p_distance);
float get_proximity_fade_distance() const;
+ void set_msdf_pixel_range(float p_range);
+ float get_msdf_pixel_range() const;
+
+ void set_msdf_outline_size(float p_size);
+ float get_msdf_outline_size() const;
+
void set_distance_fade(DistanceFadeMode p_mode);
DistanceFadeMode get_distance_fade() const;
@@ -739,7 +752,7 @@ public:
static void finish_shaders();
static void flush_changes();
- static Ref<Material> get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false, RID *r_shader_rid = nullptr);
+ static Ref<Material> get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RID *r_shader_rid = nullptr);
virtual RID get_shader_rid() const override;
@@ -787,6 +800,13 @@ public:
BaseMaterial3D(true) {}
};
+class PlaceholderMaterial : public Material {
+ GDCLASS(PlaceholderMaterial, Material)
+public:
+ virtual RID get_shader_rid() const override { return RID(); }
+ virtual Shader::Mode get_shader_mode() const override { return Shader::MODE_CANVAS_ITEM; }
+};
+
//////////////////////
#endif
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 51b4e1fbd8..ab1873ffe9 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,6 +40,122 @@
Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr;
+int Mesh::get_surface_count() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int Mesh::surface_get_array_len(int p_idx) const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int Mesh::surface_get_array_index_len(int p_idx) const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+Array Mesh::surface_get_arrays(int p_surface) const {
+ Array ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret)) {
+ return ret;
+ }
+ return Array();
+}
+
+Array Mesh::surface_get_blend_shape_arrays(int p_surface) const {
+ Array ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret)) {
+ return ret;
+ }
+
+ return Array();
+}
+
+Dictionary Mesh::surface_get_lods(int p_surface) const {
+ Dictionary ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret)) {
+ return ret;
+ }
+
+ return Dictionary();
+}
+
+uint32_t Mesh::surface_get_format(int p_idx) const {
+ uint32_t ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret)) {
+ return ret;
+ }
+
+ return 0;
+}
+
+Mesh::PrimitiveType Mesh::surface_get_primitive_type(int p_idx) const {
+ uint32_t ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret)) {
+ return (Mesh::PrimitiveType)ret;
+ }
+
+ return PRIMITIVE_MAX;
+}
+
+void Mesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material)) {
+ return;
+ }
+}
+
+Ref<Material> Mesh::surface_get_material(int p_idx) const {
+ Ref<Material> ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret)) {
+ return ret;
+ }
+
+ return Ref<Material>();
+}
+
+int Mesh::get_blend_shape_count() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret)) {
+ return ret;
+ }
+
+ return 0;
+}
+
+StringName Mesh::get_blend_shape_name(int p_index) const {
+ StringName ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret)) {
+ return ret;
+ }
+
+ return StringName();
+}
+
+void Mesh::set_blend_shape_name(int p_index, const StringName &p_name) {
+ if (GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name)) {
+ return;
+ }
+}
+
+AABB Mesh::get_aabb() const {
+ AABB ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret)) {
+ return ret;
+ }
+
+ return AABB();
+}
+
Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
if (triangle_mesh.is_valid()) {
return triangle_mesh;
@@ -339,7 +455,7 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
has_indices = true;
}
- Map<Vector3, Vector3> normal_accum;
+ HashMap<Vector3, Vector3> normal_accum;
//fill normals with triangle normals
for (int i = 0; i < vc; i += 3) {
@@ -358,13 +474,13 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
Vector3 n = Plane(t[0], t[1], t[2]).normal;
for (int j = 0; j < 3; j++) {
- Map<Vector3, Vector3>::Element *E = normal_accum.find(t[j]);
+ HashMap<Vector3, Vector3>::Iterator E = normal_accum.find(t[j]);
if (!E) {
normal_accum[t[j]] = n;
} else {
- float d = n.dot(E->get());
+ float d = n.dot(E->value);
if (d < 1.0) {
- E->get() += n * (1.0 - d);
+ E->value += n * (1.0 - d);
}
//E->get()+=n;
}
@@ -383,10 +499,10 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
for (int i = 0; i < vc2; i++) {
Vector3 t = r[i];
- Map<Vector3, Vector3>::Element *E = normal_accum.find(t);
+ HashMap<Vector3, Vector3>::Iterator E = normal_accum.find(t);
ERR_CONTINUE(!E);
- t += E->get() * p_margin;
+ t += E->value * p_margin;
r[i] = t;
}
@@ -502,6 +618,21 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
+
+ GDVIRTUAL_BIND(_get_surface_count)
+ GDVIRTUAL_BIND(_surface_get_array_len, "index")
+ GDVIRTUAL_BIND(_surface_get_array_index_len, "index")
+ GDVIRTUAL_BIND(_surface_get_arrays, "index")
+ GDVIRTUAL_BIND(_surface_get_blend_shape_arrays, "index")
+ GDVIRTUAL_BIND(_surface_get_lods, "index")
+ GDVIRTUAL_BIND(_surface_get_format, "index")
+ GDVIRTUAL_BIND(_surface_get_primitive_type, "index")
+ GDVIRTUAL_BIND(_surface_set_material, "index", "material")
+ GDVIRTUAL_BIND(_surface_get_material, "index")
+ GDVIRTUAL_BIND(_get_blend_shape_count)
+ GDVIRTUAL_BIND(_get_blend_shape_name, "index")
+ GDVIRTUAL_BIND(_set_blend_shape_name, "index", "name")
+ GDVIRTUAL_BIND(_get_aabb)
}
void Mesh::clear_cache() const {
@@ -596,7 +727,7 @@ enum OldArrayFormat {
OLD_ARRAY_FLAG_USE_2D_VERTICES = OLD_ARRAY_COMPRESS_INDEX << 1,
OLD_ARRAY_FLAG_USE_16_BIT_BONES = OLD_ARRAY_COMPRESS_INDEX << 2,
OLD_ARRAY_FLAG_USE_DYNAMIC_UPDATE = OLD_ARRAY_COMPRESS_INDEX << 3,
-
+ OLD_ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION = OLD_ARRAY_COMPRESS_INDEX << 4,
};
#ifndef DISABLE_DEPRECATED
@@ -626,6 +757,27 @@ static Mesh::PrimitiveType _old_primitives[7] = {
};
#endif // DISABLE_DEPRECATED
+// Convert Octahedron-mapped normalized vector back to Cartesian
+// Assumes normalized format (elements of v within range [-1, 1])
+Vector3 _oct_to_norm(const Vector2 v) {
+ Vector3 res(v.x, v.y, 1 - (Math::absf(v.x) + Math::absf(v.y)));
+ float t = MAX(-res.z, 0.0f);
+ res.x += t * -SIGN(res.x);
+ res.y += t * -SIGN(res.y);
+ return res.normalized();
+}
+
+// Convert Octahedron-mapped normalized tangent vector back to Cartesian
+// out_sign provides the direction for the original cartesian tangent
+// Assumes normalized format (elements of v within range [-1, 1])
+Vector3 _oct_to_tangent(const Vector2 v, float *out_sign) {
+ Vector2 v_decompressed = v;
+ v_decompressed.y = Math::absf(v_decompressed.y) * 2 - 1;
+ Vector3 res = _oct_to_norm(v_decompressed);
+ *out_sign = SIGN(v[1]);
+ return res;
+}
+
void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_format, uint32_t p_new_format, uint32_t p_elements, Vector<uint8_t> &vertex_data, Vector<uint8_t> &attribute_data, Vector<uint8_t> &skin_data) {
uint32_t dst_vertex_stride;
uint32_t dst_attribute_stride;
@@ -692,66 +844,133 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
}
} break;
case OLD_ARRAY_NORMAL: {
- if (p_old_format & OLD_ARRAY_COMPRESS_NORMAL) {
- const float multiplier = 1.f / 127.f * 1023.0f;
+ if (p_old_format & OLD_ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if ((p_old_format & OLD_ARRAY_COMPRESS_NORMAL) && (p_old_format & OLD_ARRAY_FORMAT_TANGENT) && (p_old_format & OLD_ARRAY_COMPRESS_TANGENT)) {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
+ const Vector2 src_vec(src[0] / 127.0f, src[1] / 127.0f);
+
+ const Vector3 res = _oct_to_norm(src_vec) * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+ *dst = 0;
+ *dst |= CLAMP(int(res.x * 1023.0f), 0, 1023);
+ *dst |= CLAMP(int(res.y * 1023.0f), 0, 1023) << 10;
+ *dst |= CLAMP(int(res.z * 1023.0f), 0, 1023) << 20;
+ }
+ src_offset += sizeof(int8_t) * 2;
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
+ const Vector2 src_vec(src[0] / 32767.0f, src[1] / 32767.0f);
+
+ const Vector3 res = _oct_to_norm(src_vec) * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+ *dst = 0;
+ *dst |= CLAMP(int(res.x * 1023.0f), 0, 1023);
+ *dst |= CLAMP(int(res.y * 1023.0f), 0, 1023) << 10;
+ *dst |= CLAMP(int(res.z * 1023.0f), 0, 1023) << 20;
+ }
+ src_offset += sizeof(int16_t) * 2;
+ }
+ } else { // No Octahedral compression
+ if (p_old_format & OLD_ARRAY_COMPRESS_NORMAL) {
+ const float multiplier = 1.f / 127.f * 1023.0f;
- for (uint32_t i = 0; i < p_elements; i++) {
- const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
- uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
- *dst = 0;
- *dst |= CLAMP(int(src[0] * multiplier), 0, 1023);
- *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10;
- *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20;
- }
- src_offset += sizeof(uint32_t);
- } else {
- for (uint32_t i = 0; i < p_elements; i++) {
- const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
- uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
+ *dst = 0;
+ *dst |= CLAMP(int(src[0] * multiplier), 0, 1023);
+ *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10;
+ *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20;
+ }
+ src_offset += sizeof(uint32_t);
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
- *dst = 0;
- *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023);
- *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10;
- *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20;
+ *dst = 0;
+ *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023);
+ *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10;
+ *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20;
+ }
+ src_offset += sizeof(float) * 3;
}
- src_offset += sizeof(float) * 3;
}
} break;
case OLD_ARRAY_TANGENT: {
- if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) {
- const float multiplier = 1.f / 127.f * 1023.0f;
-
- for (uint32_t i = 0; i < p_elements; i++) {
- const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
- uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
-
- *dst = 0;
- *dst |= CLAMP(int(src[0] * multiplier), 0, 1023);
- *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10;
- *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20;
- if (src[3] > 0) {
- *dst |= 3 << 30;
+ if (p_old_format & OLD_ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) { // int8
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
+ const Vector2 src_vec(src[0] / 127.0f, src[1] / 127.0f);
+ float out_sign;
+ const Vector3 res = _oct_to_tangent(src_vec, &out_sign) * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+
+ *dst = 0;
+ *dst |= CLAMP(int(res.x * 1023.0), 0, 1023);
+ *dst |= CLAMP(int(res.y * 1023.0), 0, 1023) << 10;
+ *dst |= CLAMP(int(res.z * 1023.0), 0, 1023) << 20;
+ if (out_sign > 0) {
+ *dst |= 3 << 30;
+ }
+ }
+ src_offset += sizeof(int8_t) * 2;
+ } else { // int16
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
+ const Vector2 src_vec(src[0] / 32767.0f, src[1] / 32767.0f);
+ float out_sign;
+ Vector3 res = _oct_to_tangent(src_vec, &out_sign) * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+
+ *dst = 0;
+ *dst |= CLAMP(int(res.x * 1023.0), 0, 1023);
+ *dst |= CLAMP(int(res.y * 1023.0), 0, 1023) << 10;
+ *dst |= CLAMP(int(res.z * 1023.0), 0, 1023) << 20;
+ if (out_sign > 0) {
+ *dst |= 3 << 30;
+ }
}
+ src_offset += sizeof(int16_t) * 2;
}
- src_offset += sizeof(uint32_t);
- } else {
- for (uint32_t i = 0; i < p_elements; i++) {
- const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
- uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
-
- *dst = 0;
- *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023);
- *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10;
- *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20;
- if (src[3] > 0) {
- *dst |= 3 << 30;
+ } else { // No Octahedral compression
+ if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) {
+ const float multiplier = 1.f / 127.f * 1023.0f;
+
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
+
+ *dst = 0;
+ *dst |= CLAMP(int(src[0] * multiplier), 0, 1023);
+ *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10;
+ *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20;
+ if (src[3] > 0) {
+ *dst |= 3 << 30;
+ }
}
+ src_offset += sizeof(uint32_t);
+ } else {
+ for (uint32_t i = 0; i < p_elements; i++) {
+ const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
+ uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
+
+ *dst = 0;
+ *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023);
+ *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10;
+ *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20;
+ if (src[3] > 0) {
+ *dst |= 3 << 30;
+ }
+ }
+ src_offset += sizeof(float) * 4;
}
- src_offset += sizeof(float) * 4;
}
-
} break;
case OLD_ARRAY_COLOR: {
if (p_old_format & OLD_ARRAY_COMPRESS_COLOR) {
@@ -898,7 +1117,9 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
return false;
}
- WARN_DEPRECATED_MSG("Mesh uses old surface format, which is deprecated (and loads slower). Consider re-importing or re-saving the scene.");
+ WARN_DEPRECATED_MSG(vformat(
+ "Mesh uses old surface format, which is deprecated (and loads slower). Consider re-importing or re-saving the scene. Path: \"%s\"",
+ get_path()));
int idx = sname.get_slicec('/', 1).to_int();
String what = sname.get_slicec('/', 2);
@@ -994,9 +1215,9 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
}
//clear unused flags
- print_line("format pre: " + itos(old_format));
+ print_verbose("Mesh format pre-conversion: " + itos(old_format));
- print_line("format post: " + itos(new_format));
+ print_verbose("Mesh format post-conversion: " + itos(new_format));
ERR_FAIL_COND_V(!d.has("aabb"), false);
AABB aabb = d["aabb"];
@@ -1104,7 +1325,7 @@ Array ArrayMesh::_get_surfaces() const {
data["material"] = surfaces[i].material;
}
- if (surfaces[i].name != String()) {
+ if (!surfaces[i].name.is_empty()) {
data["name"] = surfaces[i].name;
}
@@ -1385,12 +1606,12 @@ void ArrayMesh::add_blend_shape(const StringName &p_name) {
StringName name = p_name;
- if (blend_shapes.find(name) != -1) {
+ if (blend_shapes.has(name)) {
int count = 2;
do {
name = String(p_name) + " " + itos(count);
count++;
- } while (blend_shapes.find(name) != -1);
+ } while (blend_shapes.has(name));
}
blend_shapes.push_back(name);
@@ -1594,6 +1815,7 @@ Error ArrayMesh::lightmap_unwrap(const Transform3D &p_base_transform, float p_te
Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache) {
ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
+ ERR_FAIL_COND_V_MSG(p_texel_size <= 0.0f, ERR_PARAMETER_RANGE_ERROR, "Texel size must be greater than 0.");
LocalVector<float> vertices;
LocalVector<float> normals;
@@ -1605,7 +1827,7 @@ Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, flo
// Keep only the scale
Basis basis = p_base_transform.get_basis();
- Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
+ Vector3 scale = Vector3(basis.get_column(0).length(), basis.get_column(1).length(), basis.get_column(2).length());
Transform3D transform;
transform.scale(scale);
@@ -1870,3 +2092,17 @@ ArrayMesh::~ArrayMesh() {
RenderingServer::get_singleton()->free(mesh);
}
}
+///////////////
+
+void PlaceholderMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_aabb", "aabb"), &PlaceholderMesh::set_aabb);
+ ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb", PROPERTY_HINT_NONE, ""), "set_aabb", "get_aabb");
+}
+
+PlaceholderMesh::PlaceholderMesh() {
+ rid = RS::get_singleton()->mesh_create();
+}
+
+PlaceholderMesh::~PlaceholderMesh() {
+ RS::get_singleton()->free(rid);
+}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index a95b4d4a5e..b166d12ead 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,9 +45,34 @@ class Mesh : public Resource {
mutable Vector<Vector3> debug_lines;
Size2i lightmap_size_hint;
+public:
+ enum PrimitiveType {
+ PRIMITIVE_POINTS = RenderingServer::PRIMITIVE_POINTS,
+ PRIMITIVE_LINES = RenderingServer::PRIMITIVE_LINES,
+ PRIMITIVE_LINE_STRIP = RenderingServer::PRIMITIVE_LINE_STRIP,
+ PRIMITIVE_TRIANGLES = RenderingServer::PRIMITIVE_TRIANGLES,
+ PRIMITIVE_TRIANGLE_STRIP = RenderingServer::PRIMITIVE_TRIANGLE_STRIP,
+ PRIMITIVE_MAX = RenderingServer::PRIMITIVE_MAX,
+ };
+
protected:
static void _bind_methods();
+ GDVIRTUAL0RC(int, _get_surface_count)
+ GDVIRTUAL1RC(int, _surface_get_array_len, int)
+ GDVIRTUAL1RC(int, _surface_get_array_index_len, int)
+ GDVIRTUAL1RC(Array, _surface_get_arrays, int)
+ GDVIRTUAL1RC(Array, _surface_get_blend_shape_arrays, int)
+ GDVIRTUAL1RC(Dictionary, _surface_get_lods, int)
+ GDVIRTUAL1RC(uint32_t, _surface_get_format, int)
+ GDVIRTUAL1RC(uint32_t, _surface_get_primitive_type, int)
+ GDVIRTUAL2(_surface_set_material, int, Ref<Material>)
+ GDVIRTUAL1RC(Ref<Material>, _surface_get_material, int)
+ GDVIRTUAL0RC(int, _get_blend_shape_count)
+ GDVIRTUAL1RC(StringName, _get_blend_shape_name, int)
+ GDVIRTUAL2(_set_blend_shape_name, int, StringName)
+ GDVIRTUAL0RC(AABB, _get_aabb)
+
public:
enum {
NO_INDEX_ARRAY = RenderingServer::NO_INDEX_ARRAY,
@@ -120,28 +145,20 @@ public:
};
- enum PrimitiveType {
- PRIMITIVE_POINTS = RenderingServer::PRIMITIVE_POINTS,
- PRIMITIVE_LINES = RenderingServer::PRIMITIVE_LINES,
- PRIMITIVE_LINE_STRIP = RenderingServer::PRIMITIVE_LINE_STRIP,
- PRIMITIVE_TRIANGLES = RenderingServer::PRIMITIVE_TRIANGLES,
- PRIMITIVE_TRIANGLE_STRIP = RenderingServer::PRIMITIVE_TRIANGLE_STRIP,
- PRIMITIVE_MAX = RenderingServer::PRIMITIVE_MAX,
- };
-
- virtual int get_surface_count() const = 0;
- virtual int surface_get_array_len(int p_idx) const = 0;
- virtual int surface_get_array_index_len(int p_idx) const = 0;
- virtual Array surface_get_arrays(int p_surface) const = 0;
- virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0;
- virtual Dictionary surface_get_lods(int p_surface) const = 0;
- virtual uint32_t surface_get_format(int p_idx) const = 0;
- virtual PrimitiveType surface_get_primitive_type(int p_idx) const = 0;
- virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) = 0;
- virtual Ref<Material> surface_get_material(int p_idx) const = 0;
- virtual int get_blend_shape_count() const = 0;
- virtual StringName get_blend_shape_name(int p_index) const = 0;
- virtual void set_blend_shape_name(int p_index, const StringName &p_name) = 0;
+ virtual int get_surface_count() const;
+ virtual int surface_get_array_len(int p_idx) const;
+ virtual int surface_get_array_index_len(int p_idx) const;
+ virtual Array surface_get_arrays(int p_surface) const;
+ virtual Array surface_get_blend_shape_arrays(int p_surface) const;
+ virtual Dictionary surface_get_lods(int p_surface) const;
+ virtual uint32_t surface_get_format(int p_idx) const;
+ virtual PrimitiveType surface_get_primitive_type(int p_idx) const;
+ virtual void surface_set_material(int p_idx, const Ref<Material> &p_material);
+ virtual Ref<Material> surface_get_material(int p_idx) const;
+ virtual int get_blend_shape_count() const;
+ virtual StringName get_blend_shape_name(int p_index) const;
+ virtual void set_blend_shape_name(int p_index, const StringName &p_name);
+ virtual AABB get_aabb() const;
Vector<Face3> get_faces() const;
Ref<TriangleMesh> generate_triangle_mesh() const;
@@ -153,8 +170,6 @@ public:
Ref<Mesh> create_outline(float p_margin) const;
- virtual AABB get_aabb() const = 0;
-
void set_lightmap_size_hint(const Size2i &p_size);
Size2i get_lightmap_size_hint() const;
void clear_cache() const;
@@ -313,4 +328,38 @@ VARIANT_ENUM_CAST(Mesh::ArrayCustomFormat);
VARIANT_ENUM_CAST(Mesh::PrimitiveType);
VARIANT_ENUM_CAST(Mesh::BlendShapeMode);
+class PlaceholderMesh : public Mesh {
+ GDCLASS(PlaceholderMesh, Mesh);
+
+ RID rid;
+ AABB aabb;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_surface_count() const override { return 0; }
+ virtual int surface_get_array_len(int p_idx) const override { return 0; }
+ virtual int surface_get_array_index_len(int p_idx) const override { return 0; }
+ virtual Array surface_get_arrays(int p_surface) const override { return Array(); }
+ virtual Array surface_get_blend_shape_arrays(int p_surface) const override { return Array(); }
+ virtual Dictionary surface_get_lods(int p_surface) const override { return Dictionary(); }
+ virtual uint32_t surface_get_format(int p_idx) const override { return 0; }
+ virtual PrimitiveType surface_get_primitive_type(int p_idx) const override { return PRIMITIVE_TRIANGLES; }
+ virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override {}
+ virtual Ref<Material> surface_get_material(int p_idx) const override { return Ref<Material>(); }
+ virtual int get_blend_shape_count() const override { return 0; }
+ virtual StringName get_blend_shape_name(int p_index) const override { return StringName(); }
+ virtual void set_blend_shape_name(int p_index, const StringName &p_name) override {}
+ virtual RID get_rid() const override { return rid; }
+ virtual AABB get_aabb() const override { return aabb; }
+ void set_aabb(const AABB &p_aabb) { aabb = p_aabb; }
+
+ virtual int get_builtin_bind_pose_count() const override { return 0; }
+ virtual Transform3D get_builtin_bind_pose(int p_index) const override { return Transform3D(); }
+
+ PlaceholderMesh();
+ ~PlaceholderMesh();
+};
+
#endif
diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp
index 9ecd8ec2f3..33d63adc71 100644
--- a/scene/resources/mesh_data_tool.cpp
+++ b/scene/resources/mesh_data_tool.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -150,7 +150,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
vertices.write[i] = v;
}
- Map<Point2i, int> edge_indices;
+ HashMap<Point2i, int> edge_indices;
for (int i = 0; i < icount; i += 3) {
Vertex *v[3] = { &vertices.write[r[i + 0]], &vertices.write[r[i + 1]], &vertices.write[r[i + 2]] };
diff --git a/scene/resources/mesh_data_tool.h b/scene/resources/mesh_data_tool.h
index b0ebfba7ee..ff27d78c29 100644
--- a/scene/resources/mesh_data_tool.h
+++ b/scene/resources/mesh_data_tool.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index 309670e0b1..5168bf83eb 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,8 @@
#include "mesh_library.h"
+#include "box_shape_3d.h"
+
bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (name.begins_with("item/")) {
@@ -255,12 +257,35 @@ int MeshLibrary::get_last_unused_item_id() const {
}
void MeshLibrary::_set_item_shapes(int p_item, const Array &p_shapes) {
- ERR_FAIL_COND(p_shapes.size() & 1);
+ Array arr_shapes = p_shapes;
+ int size = p_shapes.size();
+ if (size & 1) {
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
+ int prev_size = item_map[p_item].shapes.size() * 2;
+
+ if (prev_size < size) {
+ // Check if last element is a shape.
+ Ref<Shape3D> shape = arr_shapes[size - 1];
+ if (shape.is_null()) {
+ Ref<BoxShape3D> box_shape;
+ box_shape.instantiate();
+ arr_shapes[size - 1] = box_shape;
+ }
+
+ // Make sure the added element is a Transform3D.
+ arr_shapes.push_back(Transform3D());
+ size++;
+ } else {
+ size--;
+ arr_shapes.resize(size);
+ }
+ }
+
Vector<ShapeData> shapes;
- for (int i = 0; i < p_shapes.size(); i += 2) {
+ for (int i = 0; i < size; i += 2) {
ShapeData sd;
- sd.shape = p_shapes[i + 0];
- sd.local_transform = p_shapes[i + 1];
+ sd.shape = arr_shapes[i + 0];
+ sd.local_transform = arr_shapes[i + 1];
if (sd.shape.is_valid()) {
shapes.push_back(sd);
diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h
index c25df757e9..4105bd6960 100644
--- a/scene/resources/mesh_library.h
+++ b/scene/resources/mesh_library.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define MESH_LIBRARY_H
#include "core/io/resource.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "mesh.h"
#include "scene/3d/navigation_region_3d.h"
#include "shape_3d.h"
@@ -56,7 +56,7 @@ public:
Ref<NavigationMesh> navmesh;
};
- Map<int, Item> item_map;
+ RBMap<int, Item> item_map;
void _set_item_shapes(int p_item, const Array &p_shapes);
Array _get_item_shapes(int p_item) const;
diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp
index 8894f0bb11..e5fc61ade5 100644
--- a/scene/resources/multimesh.cpp
+++ b/scene/resources/multimesh.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -101,9 +101,9 @@ void MultiMesh::_set_transform_2d_array(const Vector<Vector2> &p_array) {
for (int i = 0; i < len / 3; i++) {
Transform2D t;
- t.elements[0] = r[i * 3 + 0];
- t.elements[1] = r[i * 3 + 1];
- t.elements[2] = r[i * 3 + 2];
+ t.columns[0] = r[i * 3 + 0];
+ t.columns[1] = r[i * 3 + 1];
+ t.columns[2] = r[i * 3 + 2];
set_instance_transform_2d(i, t);
}
@@ -125,9 +125,9 @@ Vector<Vector2> MultiMesh::_get_transform_2d_array() const {
for (int i = 0; i < instance_count; i++) {
Transform2D t = get_instance_transform_2d(i);
- w[i * 3 + 0] = t.elements[0];
- w[i * 3 + 1] = t.elements[1];
- w[i * 3 + 2] = t.elements[2];
+ w[i * 3 + 0] = t.columns[0];
+ w[i * 3 + 1] = t.columns[1];
+ w[i * 3 + 2] = t.columns[2];
}
return xforms;
diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h
index 2fe0927e6f..30ada5365f 100644
--- a/scene/resources/multimesh.h
+++ b/scene/resources/multimesh.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index db091ec37b..7813de94ca 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -229,7 +229,7 @@ float NavigationMesh::get_verts_per_poly() const {
}
void NavigationMesh::set_detail_sample_distance(float p_value) {
- ERR_FAIL_COND(p_value < 0);
+ ERR_FAIL_COND(p_value < 0.1);
detail_sample_distance = p_value;
}
@@ -338,7 +338,7 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
}
}
- Map<_EdgeKey, bool> edge_map;
+ HashMap<_EdgeKey, bool, _EdgeKey> edge_map;
Vector<Vector3> tmeshfaces;
tmeshfaces.resize(faces.size() * 3);
@@ -356,10 +356,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
SWAP(ek.from, ek.to);
}
- Map<_EdgeKey, bool>::Element *F = edge_map.find(ek);
+ HashMap<_EdgeKey, bool, _EdgeKey>::Iterator F = edge_map.find(ek);
if (F) {
- F->get() = false;
+ F->value = false;
} else {
edge_map[ek] = true;
@@ -388,6 +388,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (!lines.size()) {
+ return debug_mesh;
+ }
+
Array arr;
arr.resize(Mesh::ARRAY_MAX);
arr[Mesh::ARRAY_VERTEX] = varr;
@@ -497,7 +501,7 @@ void NavigationMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge/max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01,or_greater"), "set_edge_max_length", "get_edge_max_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge/max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01,or_greater"), "set_edge_max_error", "get_edge_max_error");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "polygon/verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_verts_per_poly", "get_verts_per_poly");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_distance", "get_detail_sample_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.1,16.0,0.01,or_greater"), "set_detail_sample_distance", "get_detail_sample_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail/sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_max_error", "get_detail_sample_max_error");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles");
diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h
index 009239838f..4d8611c3c9 100644
--- a/scene/resources/navigation_mesh.h
+++ b/scene/resources/navigation_mesh.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,7 +49,13 @@ class NavigationMesh : public Resource {
Vector3 from;
Vector3 to;
- bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; }
+ static uint32_t hash(const _EdgeKey &p_key) {
+ return HashMapHasherDefault::hash(p_key.from) ^ HashMapHasherDefault::hash(p_key.to);
+ }
+
+ bool operator==(const _EdgeKey &p_with) const {
+ return HashMapComparatorDefault<Vector3>::compare(from, p_with.from) && HashMapComparatorDefault<Vector3>::compare(to, p_with.to);
+ }
};
protected:
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 3f4765d20f..c7b1981aed 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,12 +33,13 @@
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
+#include "core/io/missing_resource.h"
#include "core/io/resource_loader.h"
-#include "editor/editor_inspector.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/node_3d.h"
#include "scene/gui/control.h"
#include "scene/main/instance_placeholder.h"
+#include "scene/main/missing_node.h"
#include "scene/property_utils.h"
#define PACKED_SCENE_VERSION 2
@@ -48,21 +49,18 @@ bool SceneState::can_instantiate() const {
}
static Array _sanitize_node_pinned_properties(Node *p_node) {
- if (!p_node->has_meta("_edit_pinned_properties_")) {
- return Array();
- }
- Array pinned = p_node->get_meta("_edit_pinned_properties_");
+ Array pinned = p_node->get_meta("_edit_pinned_properties_", Array());
if (pinned.is_empty()) {
return Array();
}
- Set<StringName> storable_properties;
+ RBSet<StringName> storable_properties;
p_node->get_storable_properties(storable_properties);
int i = 0;
do {
if (storable_properties.has(pinned[i])) {
i++;
} else {
- pinned.remove(i);
+ pinned.remove_at(i);
}
} while (i < pinned.size());
if (pinned.is_empty()) {
@@ -108,12 +106,13 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.is_empty();
- Map<Ref<Resource>, Ref<Resource>> resources_local_to_scene;
+ HashMap<Ref<Resource>, Ref<Resource>> resources_local_to_scene;
for (int i = 0; i < nc; i++) {
const NodeData &n = nd[i];
Node *parent = nullptr;
+ String old_parent_path;
if (i > 0) {
ERR_FAIL_COND_V_MSG(n.parent == -1, nullptr, vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name]));
@@ -121,6 +120,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
#ifdef DEBUG_ENABLED
if (!nparent && (n.parent & FLAG_ID_IS_PATH)) {
WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data());
+ old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@");
+ nparent = ret_nodes[0];
}
#endif
parent = nparent;
@@ -131,6 +132,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
Node *node = nullptr;
+ MissingNode *missing_node = nullptr;
if (i == 0 && base_scene_idx >= 0) {
//scene inheritance on root node
@@ -175,37 +177,44 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
#endif
}
} else {
- Object *obj = nullptr;
+ //node belongs to this scene and must be created
+ Object *obj = ClassDB::instantiate(snames[n.type]);
- if (ClassDB::is_class_enabled(snames[n.type])) {
- //node belongs to this scene and must be created
- obj = ClassDB::instantiate(snames[n.type]);
- }
+ node = Object::cast_to<Node>(obj);
- if (!Object::cast_to<Node>(obj)) {
+ if (!node) {
if (obj) {
memdelete(obj);
obj = nullptr;
}
- WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data());
- if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) {
- if (Object::cast_to<Control>(ret_nodes[n.parent])) {
- obj = memnew(Control);
- } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) {
- obj = memnew(Node2D);
+
+ if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) {
+ missing_node = memnew(MissingNode);
+ missing_node->set_original_class(snames[n.type]);
+ missing_node->set_recording_properties(true);
+ node = missing_node;
+ obj = missing_node;
+ } else {
+ WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data());
+ if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) {
+ if (Object::cast_to<Control>(ret_nodes[n.parent])) {
+ obj = memnew(Control);
+ } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) {
+ obj = memnew(Node2D);
#ifndef _3D_DISABLED
- } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
- obj = memnew(Node3D);
+ } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
+ obj = memnew(Node3D);
#endif // _3D_DISABLED
+ }
+ }
+
+ if (!obj) {
+ obj = memnew(Node);
}
- }
- if (!obj) {
- obj = memnew(Node);
+ node = Object::cast_to<Node>(obj);
}
}
-
- node = Object::cast_to<Node>(obj);
}
if (node) {
@@ -217,6 +226,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
if (nprop_count) {
const NodeData::Property *nprops = &n.properties[0];
+ Dictionary missing_resource_properties;
+
for (int j = 0; j < nprop_count; j++) {
bool valid;
ERR_FAIL_INDEX_V(nprops[j].name, sname_count, nullptr);
@@ -246,10 +257,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
Ref<Resource> res = value;
if (res.is_valid()) {
if (res->is_local_to_scene()) {
- Map<Ref<Resource>, Ref<Resource>>::Element *E = resources_local_to_scene.find(res);
+ HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res);
if (E) {
- value = E->get();
+ value = E->value;
} else {
Node *base = i == 0 ? node : ret_nodes[0];
@@ -273,9 +284,24 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
} else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) {
value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor
}
- node->set(snames[nprops[j].name], value, &valid);
+
+ bool set_valid = true;
+ if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled() && value.get_type() == Variant::OBJECT) {
+ Ref<MissingResource> mr = value;
+ if (mr.is_valid()) {
+ missing_resource_properties[snames[nprops[j].name]] = mr;
+ set_valid = false;
+ }
+ }
+
+ if (set_valid) {
+ node->set(snames[nprops[j].name], value, &valid);
+ }
}
}
+ if (!missing_resource_properties.is_empty()) {
+ node->set_meta(META_MISSING_RESOURCES, missing_resource_properties);
+ }
}
//name
@@ -309,10 +335,17 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
+ if (!old_parent_path.is_empty()) {
+ node->_set_name_nocheck(old_parent_path + "@" + node->get_name());
+ }
+
if (n.owner >= 0) {
NODE_FROM_ID(owner, n.owner);
if (owner) {
node->_set_owner_nocheck(owner);
+ if (node->data.unique_name_in_owner) {
+ node->_acquire_unique_name_in_owner();
+ }
}
}
@@ -324,6 +357,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
+ if (missing_node) {
+ missing_node->set_recording_properties(false);
+ }
+
ret_nodes[i] = node;
if (node && gen_node_path_cache && ret_nodes[0]) {
@@ -353,15 +390,26 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
continue;
}
- Vector<Variant> binds;
- if (c.binds.size()) {
- binds.resize(c.binds.size());
- for (int j = 0; j < c.binds.size(); j++) {
- binds.write[j] = props[c.binds[j]];
+ Callable callable(cto, snames[c.method]);
+ if (c.unbinds > 0) {
+ callable = callable.unbind(c.unbinds);
+ } else if (!c.binds.is_empty()) {
+ Vector<Variant> binds;
+ if (c.binds.size()) {
+ binds.resize(c.binds.size());
+ for (int j = 0; j < c.binds.size(); j++) {
+ binds.write[j] = props[c.binds[j]];
+ }
+ }
+
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * binds.size());
+ for (int j = 0; j < binds.size(); j++) {
+ argptrs[j] = &binds[j];
}
+ callable = callable.bind(argptrs, binds.size());
}
- cfrom->connect(snames[c.signal], Callable(cto, snames[c.method]), binds, CONNECT_PERSIST | c.flags);
+ cfrom->connect(snames[c.signal], callable, varray(), CONNECT_PERSIST | c.flags);
}
//Node *s = ret_nodes[0];
@@ -382,7 +430,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
return ret_nodes[0];
}
-static int _nm_get_string(const String &p_string, Map<StringName, int> &name_map) {
+static int _nm_get_string(const String &p_string, HashMap<StringName, int> &name_map) {
if (name_map.has(p_string)) {
return name_map[p_string];
}
@@ -402,7 +450,7 @@ static int _vm_get_variant(const Variant &p_variant, HashMap<Variant, int, Varia
return idx;
}
-Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) {
+Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) {
// this function handles all the work related to properly packing scenes, be it
// instantiated or inherited.
// given the complexity of this process, an attempt will be made to properly
@@ -417,7 +465,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// save the child instantiated scenes that are chosen as editable, so they can be restored
// upon load back
- if (p_node != p_owner && p_node->get_scene_file_path() != String() && p_owner->is_editable_instance(p_node)) {
+ if (p_node != p_owner && !p_node->get_scene_file_path().is_empty() && p_owner->is_editable_instance(p_node)) {
editable_instances.push_back(p_owner->get_path_to(p_node));
// Node is the root of an editable instance.
is_editable_instance = true;
@@ -451,7 +499,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
bool instantiated_by_owner = false;
Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, p_owner, &instantiated_by_owner);
- if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) {
+ if (!p_node->get_scene_file_path().is_empty() && p_node->get_owner() == p_owner && instantiated_by_owner) {
if (p_node->get_scene_instance_load_placeholder()) {
//it's a placeholder, use the placeholder path
nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map);
@@ -474,35 +522,39 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
p_node->get_property_list(&plist);
Array pinned_props = _sanitize_node_pinned_properties(p_node);
+ Dictionary missing_resource_properties = p_node->get_meta(META_MISSING_RESOURCES, Dictionary());
for (const PropertyInfo &E : plist) {
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- Variant forced_value;
+ if (E.name == META_PROPERTY_MISSING_RESOURCES) {
+ continue; // Ignore this property when packing.
+ }
- // If instance or inheriting, not saving if property requested so, or it's meta
- if (states_stack.size()) {
+ // If instance or inheriting, not saving if property requested so.
+ if (!states_stack.is_empty()) {
if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) {
continue;
}
- // Meta is normally not saved in instances/inherited (see GH-12838), but we need to save the pinned list
- if (E.name == "__meta__") {
- if (pinned_props.size()) {
- Dictionary meta_override;
- meta_override["_edit_pinned_properties_"] = pinned_props;
- forced_value = meta_override;
- }
- }
}
StringName name = E.name;
- Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value;
+ Variant value = p_node->get(name);
- if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) {
- Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &states_stack, true);
- if (!PropertyUtils::is_property_value_different(value, default_value)) {
+ if (E.type == Variant::OBJECT && missing_resource_properties.has(E.name)) {
+ // Was this missing resource overriden? If so do not save the old value.
+ Ref<Resource> ures = value;
+ if (ures.is_null()) {
+ value = missing_resource_properties[E.name];
+ }
+ }
+
+ if (!pinned_props.has(name)) {
+ bool is_valid_default = false;
+ Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &is_valid_default, &states_stack, true);
+ if (is_valid_default && !PropertyUtils::is_property_value_different(value, default_value)) {
continue;
}
}
@@ -522,10 +574,6 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
if (!gi.persistent) {
continue;
}
- /*
- if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name))
- continue; //group was instantiated, don't add here
- */
bool skip = false;
for (const SceneState::PackState &ia : states_stack) {
@@ -558,11 +606,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
nd.owner = -1;
}
+ MissingNode *missing_node = Object::cast_to<MissingNode>(p_node);
+
// Save the right type. If this node was created by an instance
// then flag that the node should not be created but reused
if (states_stack.is_empty() && !is_editable_instance) {
//this node is not part of an instancing process, so save the type
- nd.type = _nm_get_string(p_node->get_class(), name_map);
+ if (missing_node != nullptr) {
+ // Its a missing node (type non existant on load).
+ nd.type = _nm_get_string(missing_node->get_original_class(), name_map);
+ } else {
+ nd.type = _nm_get_string(p_node->get_class(), name_map);
+ }
} else {
// this node is part of an instantiated process, so do not save the type.
// instead, save that it was instantiated
@@ -617,7 +672,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
return OK;
}
-Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) {
+Error SceneState::_parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) {
if (p_node != p_owner && p_node->get_owner() && p_node->get_owner() != p_owner && !p_owner->is_editable_instance(p_node->get_owner())) {
return OK;
}
@@ -651,12 +706,32 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
continue;
}
+ Vector<Variant> binds;
+ int unbinds = 0;
+ Callable base_callable;
+
+ if (c.callable.is_custom()) {
+ CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(c.callable.get_custom());
+ if (ccb) {
+ binds = ccb->get_binds();
+ base_callable = ccb->get_callable();
+ }
+
+ CallableCustomUnbind *ccu = dynamic_cast<CallableCustomUnbind *>(c.callable.get_custom());
+ if (ccu) {
+ unbinds = ccu->get_unbinds();
+ base_callable = ccu->get_callable();
+ }
+ } else {
+ base_callable = c.callable;
+ }
+
//find if this connection already exists
Node *common_parent = target->find_common_parent_with(p_node);
ERR_CONTINUE(!common_parent);
- if (common_parent != p_owner && common_parent->get_scene_file_path() == String()) {
+ if (common_parent != p_owner && common_parent->get_scene_file_path().is_empty()) {
common_parent = common_parent->get_owner();
}
@@ -676,7 +751,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
NodePath signal_from = common_parent->get_path_to(p_node);
NodePath signal_to = common_parent->get_path_to(target);
- if (ps->has_connection(signal_from, c.signal.get_name(), signal_to, c.callable.get_method())) {
+ if (ps->has_connection(signal_from, c.signal.get_name(), signal_to, base_callable.get_method())) {
exists = true;
break;
}
@@ -707,7 +782,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
if (from_node >= 0 && to_node >= 0) {
//this one has state for this node, save
- if (state->is_connection(from_node, c.signal.get_name(), to_node, c.callable.get_method())) {
+ if (state->is_connection(from_node, c.signal.get_name(), to_node, base_callable.get_method())) {
exists2 = true;
break;
}
@@ -716,7 +791,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
nl = nullptr;
} else {
- if (nl->get_scene_file_path() != String()) {
+ if (!nl->get_scene_file_path().is_empty()) {
//is an instance
Ref<SceneState> state = nl->get_scene_instance_state();
if (state.is_valid()) {
@@ -725,7 +800,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
if (from_node >= 0 && to_node >= 0) {
//this one has state for this node, save
- if (state->is_connection(from_node, c.signal.get_name(), to_node, c.callable.get_method())) {
+ if (state->is_connection(from_node, c.signal.get_name(), to_node, base_callable.get_method())) {
exists2 = true;
break;
}
@@ -772,12 +847,16 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
ConnectionData cd;
cd.from = src_id;
cd.to = target_id;
- cd.method = _nm_get_string(c.callable.get_method(), name_map);
+ cd.method = _nm_get_string(base_callable.get_method(), name_map);
cd.signal = _nm_get_string(c.signal.get_name(), name_map);
cd.flags = c.flags;
- for (int i = 0; i < c.binds.size(); i++) {
+ 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));
+ }
connections.push_back(cd);
}
}
@@ -800,10 +879,10 @@ Error SceneState::pack(Node *p_scene) {
Node *scene = p_scene;
- Map<StringName, int> name_map;
+ HashMap<StringName, int> name_map;
HashMap<Variant, int, VariantHasher, VariantComparator> variant_map;
- Map<Node *, int> node_map;
- Map<Node *, int> nodepath_map;
+ HashMap<Node *, int> node_map;
+ HashMap<Node *, int> nodepath_map;
// If using scene inheritance, pack the scene it inherits from.
if (scene->get_scene_inherited_state().is_valid()) {
@@ -834,10 +913,10 @@ Error SceneState::pack(Node *p_scene) {
}
variants.resize(variant_map.size());
- const Variant *K = nullptr;
- while ((K = variant_map.next(K))) {
- int idx = variant_map[*K];
- variants.write[idx] = *K;
+
+ for (const KeyValue<Variant, int> &E : variant_map) {
+ int idx = E.value;
+ variants.write[idx] = E.key;
}
node_paths.resize(nodepath_map.size());
@@ -1389,6 +1468,11 @@ int SceneState::get_connection_flags(int p_idx) const {
return connections[p_idx].flags;
}
+int SceneState::get_connection_unbinds(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, connections.size(), -1);
+ return connections[p_idx].unbinds;
+}
+
Array SceneState::get_connection_binds(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, connections.size(), Array());
Array binds;
@@ -1493,7 +1577,7 @@ void SceneState::set_base_scene(int p_idx) {
base_scene_idx = p_idx;
}
-void SceneState::add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, const Vector<int> &p_binds) {
+void SceneState::add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, int p_unbinds, const Vector<int> &p_binds) {
ERR_FAIL_INDEX(p_signal, names.size());
ERR_FAIL_INDEX(p_method, names.size());
@@ -1506,6 +1590,7 @@ void SceneState::add_connection(int p_from, int p_to, int p_signal, int p_method
c.signal = p_signal;
c.method = p_method;
c.flags = p_flags;
+ c.unbinds = p_unbinds;
c.binds = p_binds;
connections.push_back(c);
}
@@ -1548,6 +1633,7 @@ void SceneState::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_connection_method", "idx"), &SceneState::get_connection_method);
ClassDB::bind_method(D_METHOD("get_connection_flags", "idx"), &SceneState::get_connection_flags);
ClassDB::bind_method(D_METHOD("get_connection_binds", "idx"), &SceneState::get_connection_binds);
+ ClassDB::bind_method(D_METHOD("get_connection_unbinds", "idx"), &SceneState::get_connection_unbinds);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE);
@@ -1619,7 +1705,7 @@ void PackedScene::recreate_state() {
#endif
}
-Ref<SceneState> PackedScene::get_state() {
+Ref<SceneState> PackedScene::get_state() const {
return state;
}
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index a03da558e4..05abb23284 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,7 +42,7 @@ class SceneState : public RefCounted {
Vector<NodePath> node_paths;
Vector<NodePath> editable_instances;
mutable HashMap<NodePath, int> node_path_cache;
- mutable Map<int, int> base_scene_node_remap;
+ mutable HashMap<int, int> base_scene_node_remap;
int base_scene_idx = -1;
@@ -77,13 +77,14 @@ class SceneState : public RefCounted {
int signal = 0;
int method = 0;
int flags = 0;
+ int unbinds = 0;
Vector<int> binds;
};
Vector<ConnectionData> connections;
- Error _parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map);
- Error _parse_connections(Node *p_owner, Node *p_node, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map);
+ Error _parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map);
+ Error _parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map);
String path;
@@ -163,6 +164,7 @@ public:
NodePath get_connection_target(int p_idx) const;
StringName get_connection_method(int p_idx) const;
int get_connection_flags(int p_idx) const;
+ int get_connection_unbinds(int p_idx) const;
Array get_connection_binds(int p_idx) const;
bool has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method);
@@ -178,7 +180,7 @@ public:
void add_node_property(int p_node, int p_name, int p_value);
void add_node_group(int p_node, int p_group);
void set_base_scene(int p_idx);
- void add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, const Vector<int> &p_binds);
+ void add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, int p_unbinds, const Vector<int> &p_binds);
void add_editable_instance(const NodePath &p_path);
virtual void set_last_modified_time(uint64_t p_time) { last_modified_time = p_time; }
@@ -223,10 +225,12 @@ public:
virtual void set_path(const String &p_path, bool p_take_over = false) override;
#ifdef TOOLS_ENABLED
- virtual void set_last_modified_time(uint64_t p_time) override { state->set_last_modified_time(p_time); }
+ virtual void set_last_modified_time(uint64_t p_time) override {
+ state->set_last_modified_time(p_time);
+ }
#endif
- Ref<SceneState> get_state();
+ Ref<SceneState> get_state() const;
PackedScene();
};
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index e77f5a0be7..587b84a11d 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,7 @@
Mutex ParticlesMaterial::material_mutex;
SelfList<ParticlesMaterial>::List *ParticlesMaterial::dirty_materials = nullptr;
-Map<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData> ParticlesMaterial::shader_map;
+HashMap<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData, ParticlesMaterial::MaterialKey> ParticlesMaterial::shader_map;
ParticlesMaterial::ShaderNames *ParticlesMaterial::shader_names = nullptr;
void ParticlesMaterial::init_shaders() {
@@ -85,6 +85,7 @@ void ParticlesMaterial::init_shaders() {
shader_names->color = "color_value";
shader_names->color_ramp = "color_ramp";
+ shader_names->color_initial_ramp = "color_initial_ramp";
shader_names->emission_sphere_radius = "emission_sphere_radius";
shader_names->emission_box_extents = "emission_box_extents";
@@ -189,6 +190,9 @@ void ParticlesMaterial::_update_shader() {
case EMISSION_SHAPE_SPHERE: {
code += "uniform float emission_sphere_radius;\n";
} break;
+ case EMISSION_SHAPE_SPHERE_SURFACE: {
+ code += "uniform float emission_sphere_radius;\n";
+ } break;
case EMISSION_SHAPE_BOX: {
code += "uniform vec3 emission_box_extents;\n";
} break;
@@ -229,44 +233,48 @@ void ParticlesMaterial::_update_shader() {
code += "uniform vec3 gravity;\n";
if (color_ramp.is_valid()) {
- code += "uniform sampler2D color_ramp;\n";
+ code += "uniform sampler2D color_ramp : repeat_disable;\n";
+ }
+
+ if (color_initial_ramp.is_valid()) {
+ code += "uniform sampler2D color_initial_ramp : repeat_disable;\n";
}
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += "uniform sampler2D linear_velocity_texture;\n";
+ code += "uniform sampler2D linear_velocity_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
- code += "uniform sampler2D orbit_velocity_texture;\n";
+ code += "uniform sampler2D orbit_velocity_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
- code += "uniform sampler2D angular_velocity_texture;\n";
+ code += "uniform sampler2D angular_velocity_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
- code += "uniform sampler2D linear_accel_texture;\n";
+ code += "uniform sampler2D linear_accel_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
- code += "uniform sampler2D radial_accel_texture;\n";
+ code += "uniform sampler2D radial_accel_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
- code += "uniform sampler2D tangent_accel_texture;\n";
+ code += "uniform sampler2D tangent_accel_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_DAMPING].is_valid()) {
- code += "uniform sampler2D damping_texture;\n";
+ code += "uniform sampler2D damping_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_ANGLE].is_valid()) {
- code += "uniform sampler2D angle_texture;\n";
+ code += "uniform sampler2D angle_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_SCALE].is_valid()) {
- code += "uniform sampler2D scale_texture;\n";
+ code += "uniform sampler2D scale_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) {
- code += "uniform sampler2D hue_variation_texture;\n";
+ code += "uniform sampler2D hue_variation_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) {
- code += "uniform sampler2D anim_speed_texture;\n";
+ code += "uniform sampler2D anim_speed_texture : repeat_disable;\n";
}
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) {
- code += "uniform sampler2D anim_offset_texture;\n";
+ code += "uniform sampler2D anim_offset_texture : repeat_disable;\n";
}
if (collision_enabled) {
@@ -311,6 +319,9 @@ void ParticlesMaterial::_update_shader() {
code += " float scale_rand = rand_from_seed(alt_seed);\n";
code += " float hue_rot_rand = rand_from_seed(alt_seed);\n";
code += " float anim_offset_rand = rand_from_seed(alt_seed);\n";
+ if (color_initial_ramp.is_valid()) {
+ code += " float color_initial_rand = rand_from_seed(alt_seed);\n";
+ }
code += " float pi = 3.14159;\n";
code += " float degree_to_rad = pi / 180.0;\n";
code += "\n";
@@ -390,6 +401,13 @@ void ParticlesMaterial::_update_shader() {
case EMISSION_SHAPE_SPHERE: {
code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
+ code += " float p = rand_from_seed(alt_seed);\n";
+ code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
+ code += " TRANSFORM[3].xyz = mix(vec3(0.0, 0.0, 0.0), vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s), p);\n";
+ } break;
+ case EMISSION_SHAPE_SPHERE_SURFACE: {
+ code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
+ code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
} break;
@@ -462,6 +480,10 @@ void ParticlesMaterial::_update_shader() {
code += " float scale_rand = rand_from_seed(alt_seed);\n";
code += " float hue_rot_rand = rand_from_seed(alt_seed);\n";
code += " float anim_offset_rand = rand_from_seed(alt_seed);\n";
+ if (color_initial_ramp.is_valid()) {
+ code += " float color_initial_rand = rand_from_seed(alt_seed);\n";
+ }
+
code += " float pi = 3.14159;\n";
code += " float degree_to_rad = pi / 180.0;\n";
code += "\n";
@@ -584,7 +606,7 @@ void ParticlesMaterial::_update_shader() {
code += " float base_angle = (tex_angle) * mix(initial_angle_min, initial_angle_max, rand_from_seed(alt_seed));\n";
code += " base_angle += CUSTOM.y * LIFETIME * (tex_angular_velocity) * mix(angular_velocity_min,angular_velocity_max, rand_from_seed(alt_seed));\n";
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
- code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, rand_from_seed(alt_seed)) + CUSTOM.y * tex_anim_speed * mix(anim_speed_min, anim_speed_max, rand_from_seed(alt_seed));\n"; // angle
+ code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, rand_from_seed(alt_seed)) + tv * tex_anim_speed * mix(anim_speed_min, anim_speed_max, rand_from_seed(alt_seed));\n"; // angle
// apply color
// apply hue rotation
@@ -620,6 +642,12 @@ void ParticlesMaterial::_update_shader() {
} else {
code += " COLOR = hue_rot_mat * color_value;\n";
}
+
+ if (color_initial_ramp.is_valid()) {
+ code += " vec4 start_color = textureLod(color_initial_ramp, vec2(color_initial_rand, 0.0), 0.0);\n";
+ code += " COLOR *= start_color;\n";
+ }
+
if (emission_color_texture.is_valid() && (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS)) {
code += " COLOR *= texelFetch(emission_texture_color, emission_tex_ofs, 0);\n";
}
@@ -988,6 +1016,18 @@ Ref<Texture2D> ParticlesMaterial::get_color_ramp() const {
return color_ramp;
}
+void ParticlesMaterial::set_color_initial_ramp(const Ref<Texture2D> &p_texture) {
+ color_initial_ramp = p_texture;
+ RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_initial_ramp, tex_rid);
+ _queue_shader_change();
+ notify_property_list_changed();
+}
+
+Ref<Texture2D> ParticlesMaterial::get_color_initial_ramp() const {
+ return color_initial_ramp;
+}
+
void ParticlesMaterial::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) {
ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX);
particle_flags[p_particle_flag] = p_enable;
@@ -1135,7 +1175,7 @@ RID ParticlesMaterial::get_shader_rid() const {
}
void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
- if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) {
+ if (property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) {
property.usage = PROPERTY_USAGE_NONE;
}
@@ -1282,6 +1322,9 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticlesMaterial::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticlesMaterial::get_color_ramp);
+ ClassDB::bind_method(D_METHOD("set_color_initial_ramp", "ramp"), &ParticlesMaterial::set_color_initial_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_initial_ramp"), &ParticlesMaterial::get_color_initial_ramp);
+
ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &ParticlesMaterial::set_particle_flag);
ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &ParticlesMaterial::get_particle_flag);
@@ -1355,7 +1398,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
ADD_GROUP("Emission Shape", "emission_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring"), "set_emission_shape", "get_emission_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Sphere Surface,Box,Points,Directed Points,Ring"), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_point_texture", "get_emission_point_texture");
@@ -1414,6 +1457,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture1D"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_initial_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture1D"), "set_color_initial_ramp", "get_color_initial_ramp");
ADD_GROUP("Hue Variation", "hue_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION);
@@ -1462,6 +1506,7 @@ void ParticlesMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE_SURFACE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h
index 36bc456978..24341d964d 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -73,6 +73,7 @@ public:
enum EmissionShape {
EMISSION_SHAPE_POINT,
EMISSION_SHAPE_SPHERE,
+ EMISSION_SHAPE_SPHERE_SURFACE,
EMISSION_SHAPE_BOX,
EMISSION_SHAPE_POINTS,
EMISSION_SHAPE_DIRECTED_POINTS,
@@ -108,6 +109,14 @@ private:
uint32_t key = 0;
+ static uint32_t hash(const MaterialKey &p_key) {
+ return hash_djb2_one_32(p_key.key);
+ }
+
+ bool operator==(const MaterialKey &p_key) const {
+ return key == p_key.key;
+ }
+
bool operator<(const MaterialKey &p_key) const {
return key < p_key.key;
}
@@ -118,7 +127,7 @@ private:
int users = 0;
};
- static Map<MaterialKey, ShaderData> shader_map;
+ static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map;
MaterialKey current_key;
@@ -194,6 +203,7 @@ private:
StringName color;
StringName color_ramp;
+ StringName color_initial_ramp;
StringName emission_sphere_radius;
StringName emission_box_extents;
@@ -228,8 +238,8 @@ private:
bool is_initialized = false;
Vector3 direction;
- float spread;
- float flatness;
+ float spread = 0.0f;
+ float flatness = 0.0f;
float params_min[PARAM_MAX];
float params_max[PARAM_MAX];
@@ -237,38 +247,39 @@ private:
Ref<Texture2D> tex_parameters[PARAM_MAX];
Color color;
Ref<Texture2D> color_ramp;
+ Ref<Texture2D> color_initial_ramp;
bool particle_flags[PARTICLE_FLAG_MAX];
EmissionShape emission_shape;
- float emission_sphere_radius;
+ float emission_sphere_radius = 0.0f;
Vector3 emission_box_extents;
Ref<Texture2D> emission_point_texture;
Ref<Texture2D> emission_normal_texture;
Ref<Texture2D> emission_color_texture;
Vector3 emission_ring_axis;
- real_t emission_ring_height;
- real_t emission_ring_radius;
- real_t emission_ring_inner_radius;
+ real_t emission_ring_height = 0.0f;
+ real_t emission_ring_radius = 0.0f;
+ real_t emission_ring_inner_radius = 0.0f;
int emission_point_count = 1;
- bool anim_loop;
+ bool anim_loop = false;
Vector3 gravity;
- double lifetime_randomness;
+ double lifetime_randomness = 0.0;
SubEmitterMode sub_emitter_mode;
- double sub_emitter_frequency;
- int sub_emitter_amount_at_end;
- bool sub_emitter_keep_velocity;
+ double sub_emitter_frequency = 0.0;
+ int sub_emitter_amount_at_end = 0;
+ bool sub_emitter_keep_velocity = false;
//do not save emission points here
- bool attractor_interaction_enabled;
- bool collision_enabled;
- bool collision_scale;
- float collision_friction;
- float collision_bounce;
+ bool attractor_interaction_enabled = false;
+ bool collision_enabled = false;
+ bool collision_scale = false;
+ float collision_friction = 0.0f;
+ float collision_bounce = 0.0f;
protected:
static void _bind_methods();
@@ -299,6 +310,9 @@ public:
void set_color_ramp(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_color_ramp() const;
+ void set_color_initial_ramp(const Ref<Texture2D> &p_texture);
+ Ref<Texture2D> get_color_initial_ramp() const;
+
void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable);
bool get_particle_flag(ParticleFlags p_particle_flag) const;
diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp
index 31df35aa51..c1b868cb97 100644
--- a/scene/resources/physics_material.cpp
+++ b/scene/resources/physics_material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h
index d302800823..f352e66189 100644
--- a/scene/resources/physics_material.h
+++ b/scene/resources/physics_material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp
index ec2022ed2f..155a8522cf 100644
--- a/scene/resources/polygon_path_finder.cpp
+++ b/scene/resources/polygon_path_finder.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,7 @@
bool PolygonPathFinder::_is_point_inside(const Vector2 &p_point) const {
int crosses = 0;
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
const Edge &e = E->get();
Vector2 a = points[e.points[0]].pos;
@@ -105,7 +105,7 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int>
bool valid = true;
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
const Edge &e = E->get();
if (e.points[0] == i || e.points[1] == i || e.points[0] == j || e.points[1] == j) {
continue;
@@ -137,10 +137,10 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
Edge ignore_to_edge(-1, -1);
if (!_is_point_inside(from)) {
- float closest_dist = 1e20;
+ float closest_dist = 1e20f;
Vector2 closest_point;
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
const Edge &e = E->get();
Vector2 seg[2] = {
points[e.points[0]].pos,
@@ -161,10 +161,10 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
};
if (!_is_point_inside(to)) {
- float closest_dist = 1e20;
+ float closest_dist = 1e20f;
Vector2 closest_point;
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
const Edge &e = E->get();
Vector2 seg[2] = {
points[e.points[0]].pos,
@@ -188,7 +188,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
{
bool can_see_eachother = true;
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
const Edge &e = E->get();
if (e.points[0] == ignore_from_edge.points[0] && e.points[1] == ignore_from_edge.points[1]) {
continue;
@@ -240,7 +240,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
valid_b = false;
}
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
const Edge &e = E->get();
if (e.points[0] == i || e.points[1] == i) {
@@ -289,11 +289,11 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
}
//solve graph
- Set<int> open_list;
+ RBSet<int> open_list;
points.write[aidx].distance = 0;
points.write[aidx].prev = aidx;
- for (Set<int>::Element *E = points[aidx].connections.front(); E; E = E->next()) {
+ for (RBSet<int>::Element *E = points[aidx].connections.front(); E; E = E->next()) {
open_list.insert(E->get());
points.write[E->get()].distance = from.distance_to(points[E->get()].pos);
points.write[E->get()].prev = aidx;
@@ -312,7 +312,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
float least_cost = 1e30;
//this could be faster (cache previous results)
- for (Set<int>::Element *E = open_list.front(); E; E = E->next()) {
+ for (RBSet<int>::Element *E = open_list.front(); E; E = E->next()) {
const Point &p = points[E->get()];
float cost = p.distance;
cost += p.pos.distance_to(to);
@@ -327,7 +327,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector
const Point &np = points[least_cost_point];
//open the neighbours for search
- for (Set<int>::Element *E = np.connections.front(); E; E = E->next()) {
+ for (RBSet<int>::Element *E = np.connections.front(); E; E = E->next()) {
Point &p = points.write[E->get()];
float distance = np.pos.distance_to(p.pos) + np.distance;
@@ -459,7 +459,7 @@ Dictionary PolygonPathFinder::_get_data() const {
{
int *cw = c.ptrw();
int idx = 0;
- for (Set<int>::Element *E = points[i].connections.front(); E; E = E->next()) {
+ for (RBSet<int>::Element *E = points[i].connections.front(); E; E = E->next()) {
cw[idx++] = E->get();
}
}
@@ -469,7 +469,7 @@ Dictionary PolygonPathFinder::_get_data() const {
{
int *iw = ind.ptrw();
int idx = 0;
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
iw[idx++] = E->get().points[0];
iw[idx++] = E->get().points[1];
}
@@ -489,10 +489,10 @@ bool PolygonPathFinder::is_point_inside(const Vector2 &p_point) const {
}
Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const {
- float closest_dist = 1e20;
+ float closest_dist = 1e20f;
Vector2 closest_point;
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
const Edge &e = E->get();
Vector2 seg[2] = {
points[e.points[0]].pos,
@@ -508,7 +508,7 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const {
}
}
- ERR_FAIL_COND_V(closest_dist == 1e20, Vector2());
+ ERR_FAIL_COND_V(Math::is_equal_approx(closest_dist, 1e20f), Vector2());
return closest_point;
}
@@ -516,7 +516,7 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const {
Vector<Vector2> PolygonPathFinder::get_intersections(const Vector2 &p_from, const Vector2 &p_to) const {
Vector<Vector2> inters;
- for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) {
+ for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) {
Vector2 a = points[E->get().points[0]].pos;
Vector2 b = points[E->get().points[1]].pos;
diff --git a/scene/resources/polygon_path_finder.h b/scene/resources/polygon_path_finder.h
index 2f3cb634fb..71ad77eb6e 100644
--- a/scene/resources/polygon_path_finder.h
+++ b/scene/resources/polygon_path_finder.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,7 +38,7 @@ class PolygonPathFinder : public Resource {
struct Point {
Vector2 pos;
- Set<int> connections;
+ RBSet<int> connections;
float distance = 0.0;
float penalty = 0.0;
int prev = 0;
@@ -68,7 +68,7 @@ class PolygonPathFinder : public Resource {
Rect2 bounds;
Vector<Point> points;
- Set<Edge> edges;
+ RBSet<Edge> edges;
bool _is_point_inside(const Vector2 &p_point) const;
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index f8be00f5fb..3009bdb449 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,18 +29,29 @@
/*************************************************************************/
#include "primitive_meshes.h"
+
+#include "core/core_string_names.h"
+#include "scene/resources/theme.h"
#include "servers/rendering_server.h"
+#include "thirdparty/misc/clipper.hpp"
+#include "thirdparty/misc/polypartition.h"
/**
PrimitiveMesh
*/
void PrimitiveMesh::_update() const {
Array arr;
- arr.resize(RS::ARRAY_MAX);
- _create_mesh_array(arr);
+ if (GDVIRTUAL_CALL(_create_mesh_array, arr)) {
+ ERR_FAIL_COND_MSG(arr.size() != RS::ARRAY_MAX, "_create_mesh_array must return an array of Mesh.ARRAY_MAX elements.");
+ } else {
+ arr.resize(RS::ARRAY_MAX);
+ _create_mesh_array(arr);
+ }
Vector<Vector3> points = arr[RS::ARRAY_VERTEX];
+ ERR_FAIL_COND_MSG(points.size() == 0, "_create_mesh_array must return at least a vertex array.");
+
aabb = AABB();
int pc = points.size();
@@ -210,6 +221,8 @@ void PrimitiveMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");
+
+ GDVIRTUAL_BIND(_create_mesh_array);
}
void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
@@ -262,6 +275,10 @@ PrimitiveMesh::~PrimitiveMesh() {
*/
void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
+ create_mesh_array(p_arr, radius, height, radial_segments, rings);
+}
+
+void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings) {
int i, j, prevrow, thisrow, point;
float x, y, z, u, v, w;
float onethird = 1.0 / 3.0;
@@ -302,7 +319,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
points.push_back(p + Vector3(0.0, 0.5 * height - radius, 0.0));
normals.push_back(p.normalized());
- ADD_TANGENT(z, 0.0, x, 1.0)
+ ADD_TANGENT(-z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, v * onethird));
point++;
@@ -341,7 +358,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * radius, y, -z * radius);
points.push_back(p);
normals.push_back(Vector3(x, 0.0, -z));
- ADD_TANGENT(z, 0.0, x, 1.0)
+ ADD_TANGENT(-z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, onethird + (v * onethird)));
point++;
@@ -381,7 +398,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0));
normals.push_back(p.normalized());
- ADD_TANGENT(z, 0.0, x, 1.0)
+ ADD_TANGENT(-z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird)));
point++;
@@ -473,6 +490,10 @@ CapsuleMesh::CapsuleMesh() {}
*/
void BoxMesh::_create_mesh_array(Array &p_arr) const {
+ BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d);
+}
+
+void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d) {
int i, j, prevrow, thisrow, point;
float x, y, z;
float onethird = 1.0 / 3.0;
@@ -724,6 +745,10 @@ BoxMesh::BoxMesh() {}
*/
void CylinderMesh::_create_mesh_array(Array &p_arr) const {
+ create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings);
+}
+
+void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings) {
int i, j, prevrow, thisrow, point;
float x, y, z, u, v, radius;
@@ -1423,6 +1448,10 @@ Vector3 QuadMesh::get_center_offset() const {
*/
void SphereMesh::_create_mesh_array(Array &p_arr) const {
+ create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere);
+}
+
+void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere) {
int i, j, prevrow, thisrow, point;
float x, y, z;
@@ -2127,3 +2156,819 @@ void RibbonTrailMesh::_bind_methods() {
RibbonTrailMesh::RibbonTrailMesh() {
}
+
+/*************************************************************************/
+/* TextMesh */
+/*************************************************************************/
+
+void TextMesh::_generate_glyph_mesh_data(uint32_t p_hash, const Glyph &p_gl) const {
+ if (cache.has(p_hash)) {
+ return;
+ }
+
+ GlyphMeshData &gl_data = cache[p_hash];
+
+ Dictionary d = TS->font_get_glyph_contours(p_gl.font_rid, p_gl.font_size, p_gl.index);
+ Vector2 origin = Vector2(p_gl.x_off, p_gl.y_off) * pixel_size;
+
+ PackedVector3Array points = d["points"];
+ PackedInt32Array contours = d["contours"];
+ bool orientation = d["orientation"];
+
+ if (points.size() < 3 || contours.size() < 1) {
+ return; // No full contours, only glyph control points (or nothing), ignore.
+ }
+
+ // Approximate Bezier curves as polygons.
+ // See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html, for more info.
+ for (int i = 0; i < contours.size(); i++) {
+ int32_t start = (i == 0) ? 0 : (contours[i - 1] + 1);
+ int32_t end = contours[i];
+ Vector<ContourPoint> polygon;
+
+ for (int32_t j = start; j <= end; j++) {
+ if (points[j].z == TextServer::CONTOUR_CURVE_TAG_ON) {
+ // Point on the curve.
+ Vector2 p = Vector2(points[j].x, points[j].y) * pixel_size + origin;
+ polygon.push_back(ContourPoint(p, true));
+ } else if (points[j].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {
+ // Conic Bezier arc.
+ int32_t next = (j == end) ? start : (j + 1);
+ int32_t prev = (j == start) ? end : (j - 1);
+ Vector2 p0;
+ Vector2 p1 = Vector2(points[j].x, points[j].y);
+ Vector2 p2;
+
+ // For successive conic OFF points add a virtual ON point in the middle.
+ if (points[prev].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {
+ p0 = (Vector2(points[prev].x, points[prev].y) + Vector2(points[j].x, points[j].y)) / 2.0;
+ } else if (points[prev].z == TextServer::CONTOUR_CURVE_TAG_ON) {
+ p0 = Vector2(points[prev].x, points[prev].y);
+ } else {
+ ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j));
+ }
+ if (points[next].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {
+ p2 = (Vector2(points[j].x, points[j].y) + Vector2(points[next].x, points[next].y)) / 2.0;
+ } else if (points[next].z == TextServer::CONTOUR_CURVE_TAG_ON) {
+ p2 = Vector2(points[next].x, points[next].y);
+ } else {
+ ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j));
+ }
+
+ real_t step = CLAMP(curve_step / (p0 - p2).length(), 0.01, 0.5);
+ real_t t = step;
+ while (t < 1.0) {
+ real_t omt = (1.0 - t);
+ real_t omt2 = omt * omt;
+ real_t t2 = t * t;
+
+ Vector2 point = p1 + omt2 * (p0 - p1) + t2 * (p2 - p1);
+ Vector2 p = point * pixel_size + origin;
+ polygon.push_back(ContourPoint(p, false));
+ t += step;
+ }
+ } else if (points[j].z == TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC) {
+ // Cubic Bezier arc.
+ int32_t next1 = (j == end) ? start : (j + 1);
+ int32_t next2 = (next1 == end) ? start : (next1 + 1);
+ int32_t prev = (j == start) ? end : (j - 1);
+
+ // There must be exactly two OFF points and two ON points for each cubic arc.
+ ERR_FAIL_COND_MSG(points[prev].z != TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, prev));
+ ERR_FAIL_COND_MSG(points[next1].z != TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC, vformat("Invalid cubic arc point sequence at %d:%d", i, next1));
+ ERR_FAIL_COND_MSG(points[next2].z != TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, next2));
+
+ Vector2 p0 = Vector2(points[prev].x, points[prev].y);
+ Vector2 p1 = Vector2(points[j].x, points[j].y);
+ Vector2 p2 = Vector2(points[next1].x, points[next1].y);
+ Vector2 p3 = Vector2(points[next2].x, points[next2].y);
+
+ real_t step = CLAMP(curve_step / (p0 - p3).length(), 0.01, 0.5);
+ real_t t = step;
+ while (t < 1.0) {
+ real_t omt = (1.0 - t);
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ Vector2 point = p0 * omt3 + p1 * omt2 * t * 3.0 + p2 * omt * t2 * 3.0 + p3 * t3;
+ Vector2 p = point * pixel_size + origin;
+ polygon.push_back(ContourPoint(p, false));
+ t += step;
+ }
+ i++;
+ } else {
+ ERR_FAIL_MSG(vformat("Unknown point tag at %d:%d", i, j));
+ }
+ }
+
+ if (polygon.size() < 3) {
+ continue; // Skip glyph control points.
+ }
+
+ if (!orientation) {
+ polygon.reverse();
+ }
+
+ gl_data.contours.push_back(polygon);
+ }
+
+ // Calculate bounds.
+ List<TPPLPoly> in_poly;
+ for (int i = 0; i < gl_data.contours.size(); i++) {
+ TPPLPoly inp;
+ inp.Init(gl_data.contours[i].size());
+ real_t length = 0.0;
+ for (int j = 0; j < gl_data.contours[i].size(); j++) {
+ int next = (j + 1 == gl_data.contours[i].size()) ? 0 : (j + 1);
+
+ gl_data.min_p.x = MIN(gl_data.min_p.x, gl_data.contours[i][j].point.x);
+ gl_data.min_p.y = MIN(gl_data.min_p.y, gl_data.contours[i][j].point.y);
+ gl_data.max_p.x = MAX(gl_data.max_p.x, gl_data.contours[i][j].point.x);
+ gl_data.max_p.y = MAX(gl_data.max_p.y, gl_data.contours[i][j].point.y);
+ length += (gl_data.contours[i][next].point - gl_data.contours[i][j].point).length();
+
+ inp.GetPoint(j) = gl_data.contours[i][j].point;
+ }
+ TPPLOrientation poly_orient = inp.GetOrientation();
+ if (poly_orient == TPPL_ORIENTATION_CW) {
+ inp.SetHole(true);
+ }
+ in_poly.push_back(inp);
+ gl_data.contours_info.push_back(ContourInfo(length, poly_orient == TPPL_ORIENTATION_CCW));
+ }
+
+ TPPLPartition tpart;
+
+ //Decompose and triangulate.
+ List<TPPLPoly> out_poly;
+ if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) {
+ ERR_FAIL_MSG("Convex decomposing failed!");
+ }
+ List<TPPLPoly> out_tris;
+ for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) {
+ if (tpart.Triangulate_OPT(&(I->get()), &out_tris) == 0) {
+ ERR_FAIL_MSG("Triangulation failed!");
+ }
+ }
+
+ for (List<TPPLPoly>::Element *I = out_tris.front(); I; I = I->next()) {
+ TPPLPoly &tp = I->get();
+ ERR_FAIL_COND(tp.GetNumPoints() != 3); // Trianges only.
+
+ for (int i = 0; i < 3; i++) {
+ gl_data.triangles.push_back(Vector2(tp.GetPoint(i).x, tp.GetPoint(i).y));
+ }
+ }
+}
+
+void TextMesh::_create_mesh_array(Array &p_arr) const {
+ Ref<Font> font = _get_font_or_default();
+ ERR_FAIL_COND(font.is_null());
+
+ if (dirty_cache) {
+ cache.clear();
+ dirty_cache = false;
+ }
+
+ // Update text buffer.
+ if (dirty_text) {
+ TS->shaped_text_clear(text_rid);
+ TS->shaped_text_set_direction(text_rid, text_direction);
+
+ String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
+ TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, language);
+
+ Array stt;
+ if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
+ GDVIRTUAL_CALL(_structured_text_parser, st_args, text, stt);
+ } else {
+ stt = TS->parse_structured_text(st_parser, st_args, text);
+ }
+ TS->shaped_text_set_bidi_override(text_rid, stt);
+
+ dirty_text = false;
+ dirty_font = false;
+ } else if (dirty_font) {
+ int spans = TS->shaped_get_span_count(text_rid);
+ for (int i = 0; i < spans; i++) {
+ TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features);
+ }
+
+ dirty_font = false;
+ }
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
+ TS->shaped_text_fit_to_width(text_rid, width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
+ } else {
+ TS->shaped_text_fit_to_width(text_rid, -1, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA);
+ }
+
+ Vector2 offset;
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid);
+ int gl_size = TS->shaped_text_get_glyph_count(text_rid);
+ float line_width = TS->shaped_text_get_width(text_rid) * pixel_size;
+
+ switch (horizontal_alignment) {
+ case HORIZONTAL_ALIGNMENT_LEFT:
+ offset.x = 0.0;
+ break;
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ offset.x = -line_width / 2.0;
+ } break;
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
+ offset.x = -line_width;
+ } break;
+ }
+
+ bool has_depth = !Math::is_zero_approx(depth);
+
+ // Generate glyph data, precalculate size of the arrays and mesh bounds for UV.
+ int64_t p_size = 0;
+ int64_t i_size = 0;
+
+ Vector2 min_p = Vector2(INFINITY, INFINITY);
+ Vector2 max_p = Vector2(-INFINITY, -INFINITY);
+
+ Vector2 offset_pre = offset;
+ for (int i = 0; i < gl_size; i++) {
+ if (glyphs[i].font_rid != RID()) {
+ uint32_t hash = hash_one_uint64(glyphs[i].font_rid.get_id());
+ hash = hash_djb2_one_32(glyphs[i].index, hash);
+
+ _generate_glyph_mesh_data(hash, glyphs[i]);
+ GlyphMeshData &gl_data = cache[hash];
+
+ p_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1);
+ i_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1);
+
+ if (has_depth) {
+ for (int j = 0; j < gl_data.contours.size(); j++) {
+ p_size += glyphs[i].repeat * gl_data.contours[j].size() * 4;
+ i_size += glyphs[i].repeat * gl_data.contours[j].size() * 6;
+ }
+ }
+
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ min_p.x = MIN(gl_data.min_p.x + offset_pre.x, min_p.x);
+ min_p.y = MIN(gl_data.min_p.y + offset_pre.y, min_p.y);
+ max_p.x = MAX(gl_data.max_p.x + offset_pre.x, max_p.x);
+ max_p.y = MAX(gl_data.max_p.y + offset_pre.y, max_p.y);
+
+ offset_pre.x += glyphs[i].advance * pixel_size;
+ }
+ } else {
+ p_size += glyphs[i].repeat * 4;
+ i_size += glyphs[i].repeat * 6;
+
+ offset_pre.x += glyphs[i].advance * pixel_size * glyphs[i].repeat;
+ }
+ }
+
+ Vector<Vector3> vertices;
+ Vector<Vector3> normals;
+ Vector<float> tangents;
+ Vector<Vector2> uvs;
+ Vector<int32_t> indices;
+
+ vertices.resize(p_size);
+ normals.resize(p_size);
+ uvs.resize(p_size);
+ tangents.resize(p_size * 4);
+ indices.resize(i_size);
+
+ Vector3 *vertices_ptr = vertices.ptrw();
+ Vector3 *normals_ptr = normals.ptrw();
+ float *tangents_ptr = tangents.ptrw();
+ Vector2 *uvs_ptr = uvs.ptrw();
+ int32_t *indices_ptr = indices.ptrw();
+
+ // Generate mesh.
+ int32_t p_idx = 0;
+ int32_t i_idx = 0;
+ for (int i = 0; i < gl_size; i++) {
+ if (glyphs[i].font_rid != RID()) {
+ uint32_t hash = hash_one_uint64(glyphs[i].font_rid.get_id());
+ hash = hash_djb2_one_32(glyphs[i].index, hash);
+
+ const GlyphMeshData &gl_data = cache[hash];
+
+ int64_t ts = gl_data.triangles.size();
+ const Vector2 *ts_ptr = gl_data.triangles.ptr();
+
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ for (int k = 0; k < ts; k += 3) {
+ // Add front face.
+ for (int l = 0; l < 3; l++) {
+ Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, depth / 2.0);
+ vertices_ptr[p_idx] = point;
+ normals_ptr[p_idx] = Vector3(0.0, 0.0, 1.0);
+ if (has_depth) {
+ uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.0), real_t(0.4)));
+ } else {
+ uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.0), real_t(1.0)));
+ }
+ tangents_ptr[p_idx * 4 + 0] = 1.0;
+ tangents_ptr[p_idx * 4 + 1] = 0.0;
+ tangents_ptr[p_idx * 4 + 2] = 0.0;
+ tangents_ptr[p_idx * 4 + 3] = 1.0;
+ indices_ptr[i_idx++] = p_idx;
+ p_idx++;
+ }
+ if (has_depth) {
+ // Add back face.
+ for (int l = 2; l >= 0; l--) {
+ Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, -depth / 2.0);
+ vertices_ptr[p_idx] = point;
+ normals_ptr[p_idx] = Vector3(0.0, 0.0, -1.0);
+ uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.4), real_t(0.8)));
+ tangents_ptr[p_idx * 4 + 0] = -1.0;
+ tangents_ptr[p_idx * 4 + 1] = 0.0;
+ tangents_ptr[p_idx * 4 + 2] = 0.0;
+ tangents_ptr[p_idx * 4 + 3] = 1.0;
+ indices_ptr[i_idx++] = p_idx;
+ p_idx++;
+ }
+ }
+ }
+ // Add sides.
+ if (has_depth) {
+ for (int k = 0; k < gl_data.contours.size(); k++) {
+ int64_t ps = gl_data.contours[k].size();
+ const ContourPoint *ps_ptr = gl_data.contours[k].ptr();
+ const ContourInfo &ps_info = gl_data.contours_info[k];
+ real_t length = 0.0;
+ for (int l = 0; l < ps; l++) {
+ int prev = (l == 0) ? (ps - 1) : (l - 1);
+ int next = (l + 1 == ps) ? 0 : (l + 1);
+ Vector2 d1;
+ Vector2 d2 = (ps_ptr[next].point - ps_ptr[l].point).normalized();
+ if (ps_ptr[l].sharp) {
+ d1 = d2;
+ } else {
+ d1 = (ps_ptr[l].point - ps_ptr[prev].point).normalized();
+ }
+ real_t seg_len = (ps_ptr[next].point - ps_ptr[l].point).length();
+
+ Vector3 quad_faces[4] = {
+ Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, -depth / 2.0),
+ Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, -depth / 2.0),
+ Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, depth / 2.0),
+ Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, depth / 2.0),
+ };
+ for (int m = 0; m < 4; m++) {
+ const Vector2 &d = ((m % 2) == 0) ? d1 : d2;
+ real_t u_pos = ((m % 2) == 0) ? length : length + seg_len;
+ vertices_ptr[p_idx + m] = quad_faces[m];
+ normals_ptr[p_idx + m] = Vector3(d.y, d.x, 0.0);
+ if (m < 2) {
+ uvs_ptr[p_idx + m] = Vector2(Math::range_lerp(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.8 : 0.9);
+ } else {
+ uvs_ptr[p_idx + m] = Vector2(Math::range_lerp(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.9 : 1.0);
+ }
+ tangents_ptr[(p_idx + m) * 4 + 0] = d.x;
+ tangents_ptr[(p_idx + m) * 4 + 1] = -d.y;
+ tangents_ptr[(p_idx + m) * 4 + 2] = 0.0;
+ tangents_ptr[(p_idx + m) * 4 + 3] = 1.0;
+ }
+
+ indices_ptr[i_idx++] = p_idx;
+ indices_ptr[i_idx++] = p_idx + 1;
+ indices_ptr[i_idx++] = p_idx + 2;
+
+ indices_ptr[i_idx++] = p_idx + 1;
+ indices_ptr[i_idx++] = p_idx + 3;
+ indices_ptr[i_idx++] = p_idx + 2;
+
+ length += seg_len;
+ p_idx += 4;
+ }
+ }
+ }
+ offset.x += glyphs[i].advance * pixel_size;
+ }
+ } else {
+ // Add fallback quad for missing glyphs.
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ Size2 sz = TS->get_hex_code_box_size(glyphs[i].font_size, glyphs[i].index) * pixel_size;
+ Vector3 quad_faces[4] = {
+ Vector3(offset.x, offset.y, 0.0),
+ Vector3(offset.x, sz.y + offset.y, 0.0),
+ Vector3(sz.x + offset.x, sz.y + offset.y, 0.0),
+ Vector3(sz.x + offset.x, offset.y, 0.0),
+ };
+ for (int k = 0; k < 4; k++) {
+ vertices_ptr[p_idx + k] = quad_faces[k];
+ normals_ptr[p_idx + k] = Vector3(0.0, 0.0, 1.0);
+ if (has_depth) {
+ uvs_ptr[p_idx + k] = Vector2(Math::range_lerp(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(quad_faces[k].y, -min_p.y, -max_p.y, real_t(0.0), real_t(0.4)));
+ } else {
+ uvs_ptr[p_idx + k] = Vector2(Math::range_lerp(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(quad_faces[k].y, -min_p.y, -max_p.y, real_t(0.0), real_t(1.0)));
+ }
+ tangents_ptr[(p_idx + k) * 4 + 0] = 1.0;
+ tangents_ptr[(p_idx + k) * 4 + 1] = 0.0;
+ tangents_ptr[(p_idx + k) * 4 + 2] = 0.0;
+ tangents_ptr[(p_idx + k) * 4 + 3] = 1.0;
+ }
+
+ indices_ptr[i_idx++] = p_idx;
+ indices_ptr[i_idx++] = p_idx + 1;
+ indices_ptr[i_idx++] = p_idx + 2;
+
+ indices_ptr[i_idx++] = p_idx + 0;
+ indices_ptr[i_idx++] = p_idx + 2;
+ indices_ptr[i_idx++] = p_idx + 3;
+ p_idx += 4;
+
+ offset.x += glyphs[i].advance * pixel_size;
+ }
+ }
+ }
+
+ if (p_size == 0) {
+ // If empty, add single trinagle to suppress errors.
+ vertices.push_back(Vector3());
+ normals.push_back(Vector3());
+ uvs.push_back(Vector2());
+ tangents.push_back(1.0);
+ tangents.push_back(0.0);
+ tangents.push_back(0.0);
+ tangents.push_back(1.0);
+ indices.push_back(0);
+ indices.push_back(0);
+ indices.push_back(0);
+ }
+
+ p_arr[RS::ARRAY_VERTEX] = vertices;
+ p_arr[RS::ARRAY_NORMAL] = normals;
+ p_arr[RS::ARRAY_TANGENT] = tangents;
+ p_arr[RS::ARRAY_TEX_UV] = uvs;
+ p_arr[RS::ARRAY_INDEX] = indices;
+}
+
+void TextMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &TextMesh::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &TextMesh::get_horizontal_alignment);
+
+ ClassDB::bind_method(D_METHOD("set_text", "text"), &TextMesh::set_text);
+ ClassDB::bind_method(D_METHOD("get_text"), &TextMesh::get_text);
+
+ ClassDB::bind_method(D_METHOD("set_font", "font"), &TextMesh::set_font);
+ ClassDB::bind_method(D_METHOD("get_font"), &TextMesh::get_font);
+
+ ClassDB::bind_method(D_METHOD("set_font_size", "font_size"), &TextMesh::set_font_size);
+ ClassDB::bind_method(D_METHOD("get_font_size"), &TextMesh::get_font_size);
+
+ ClassDB::bind_method(D_METHOD("set_depth", "depth"), &TextMesh::set_depth);
+ ClassDB::bind_method(D_METHOD("get_depth"), &TextMesh::get_depth);
+
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &TextMesh::set_width);
+ ClassDB::bind_method(D_METHOD("get_width"), &TextMesh::get_width);
+
+ ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &TextMesh::set_pixel_size);
+ ClassDB::bind_method(D_METHOD("get_pixel_size"), &TextMesh::get_pixel_size);
+
+ ClassDB::bind_method(D_METHOD("set_curve_step", "curve_step"), &TextMesh::set_curve_step);
+ ClassDB::bind_method(D_METHOD("get_curve_step"), &TextMesh::get_curve_step);
+
+ ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextMesh::set_text_direction);
+ ClassDB::bind_method(D_METHOD("get_text_direction"), &TextMesh::get_text_direction);
+
+ ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &TextMesh::set_opentype_feature);
+ ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &TextMesh::get_opentype_feature);
+ ClassDB::bind_method(D_METHOD("clear_opentype_features"), &TextMesh::clear_opentype_features);
+
+ ClassDB::bind_method(D_METHOD("set_language", "language"), &TextMesh::set_language);
+ ClassDB::bind_method(D_METHOD("get_language"), &TextMesh::get_language);
+
+ ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextMesh::set_structured_text_bidi_override);
+ ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &TextMesh::get_structured_text_bidi_override);
+
+ ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &TextMesh::set_structured_text_bidi_override_options);
+ ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &TextMesh::get_structured_text_bidi_override_options);
+
+ ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &TextMesh::set_uppercase);
+ ClassDB::bind_method(D_METHOD("is_uppercase"), &TextMesh::is_uppercase);
+
+ ClassDB::bind_method(D_METHOD("_font_changed"), &TextMesh::_font_changed);
+ ClassDB::bind_method(D_METHOD("_request_update"), &TextMesh::_request_update);
+
+ ADD_GROUP("Text", "");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,127,1"), "set_font_size", "get_font_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
+
+ ADD_GROUP("Mesh", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001"), "set_pixel_size", "get_pixel_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_step", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "set_curve_step", "get_curve_step");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
+
+ ADD_GROUP("Locale", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
+}
+
+void TextMesh::_notification(int p_what) {
+ switch (p_what) {
+ case MainLoop::NOTIFICATION_TRANSLATION_CHANGED: {
+ String new_text = tr(text);
+ if (new_text == xl_text) {
+ return; // Nothing new.
+ }
+ xl_text = new_text;
+ dirty_text = true;
+ _request_update();
+ } break;
+ }
+}
+
+bool TextMesh::_set(const StringName &p_name, const Variant &p_value) {
+ String str = p_name;
+ if (str.begins_with("opentype_features/")) {
+ String name = str.get_slicec('/', 1);
+ int32_t tag = TS->name_to_tag(name);
+ int value = p_value;
+ if (value == -1) {
+ if (opentype_features.has(tag)) {
+ opentype_features.erase(tag);
+ dirty_font = true;
+ _request_update();
+ }
+ } else {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
+ opentype_features[tag] = value;
+ dirty_font = true;
+ _request_update();
+ }
+ }
+ notify_property_list_changed();
+ return true;
+ }
+
+ return false;
+}
+
+bool TextMesh::_get(const StringName &p_name, Variant &r_ret) const {
+ String str = p_name;
+ if (str.begins_with("opentype_features/")) {
+ String name = str.get_slicec('/', 1);
+ int32_t tag = TS->name_to_tag(name);
+ if (opentype_features.has(tag)) {
+ r_ret = opentype_features[tag];
+ return true;
+ } else {
+ r_ret = -1;
+ return true;
+ }
+ }
+ return false;
+}
+
+void TextMesh::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
+ String name = TS->tag_to_name(*ftr);
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
+ }
+ p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+}
+
+TextMesh::TextMesh() {
+ primitive_type = PRIMITIVE_TRIANGLES;
+ text_rid = TS->create_shaped_text();
+}
+
+TextMesh::~TextMesh() {
+ TS->free_rid(text_rid);
+}
+
+void TextMesh::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ if (horizontal_alignment != p_alignment) {
+ horizontal_alignment = p_alignment;
+ _request_update();
+ }
+}
+
+HorizontalAlignment TextMesh::get_horizontal_alignment() const {
+ return horizontal_alignment;
+}
+
+void TextMesh::set_text(const String &p_string) {
+ if (text != p_string) {
+ text = p_string;
+ xl_text = tr(text);
+ dirty_text = true;
+ _request_update();
+ }
+}
+
+String TextMesh::get_text() const {
+ return text;
+}
+
+void TextMesh::_font_changed() {
+ dirty_font = true;
+ dirty_cache = true;
+ call_deferred(SNAME("_request_update"));
+}
+
+void TextMesh::set_font(const Ref<Font> &p_font) {
+ if (font_override != p_font) {
+ if (font_override.is_valid()) {
+ font_override->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed"));
+ }
+ font_override = p_font;
+ dirty_font = true;
+ dirty_cache = true;
+ if (font_override.is_valid()) {
+ font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed"));
+ }
+ _request_update();
+ }
+}
+
+Ref<Font> TextMesh::get_font() const {
+ return font_override;
+}
+
+Ref<Font> TextMesh::_get_font_or_default() const {
+ if (font_override.is_valid() && font_override->get_data_count() > 0) {
+ return font_override;
+ }
+
+ // 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)) {
+ return Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+ }
+ }
+ }
+
+ // Lastly, fall back on the items defined in the default Theme, if they exist.
+ {
+ 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)) {
+ return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+ }
+ }
+ }
+
+ // If they don't exist, use any type to return the default/empty value.
+ return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
+}
+
+void TextMesh::set_font_size(int p_size) {
+ if (font_size != p_size) {
+ font_size = CLAMP(p_size, 1, 127);
+ dirty_font = true;
+ dirty_cache = true;
+ _request_update();
+ }
+}
+
+int TextMesh::get_font_size() const {
+ return font_size;
+}
+
+void TextMesh::set_depth(real_t p_depth) {
+ if (depth != p_depth) {
+ depth = MAX(p_depth, 0.0);
+ _request_update();
+ }
+}
+
+real_t TextMesh::get_depth() const {
+ return depth;
+}
+
+void TextMesh::set_width(real_t p_width) {
+ if (width != p_width) {
+ width = p_width;
+ _request_update();
+ }
+}
+
+real_t TextMesh::get_width() const {
+ return width;
+}
+
+void TextMesh::set_pixel_size(real_t p_amount) {
+ if (pixel_size != p_amount) {
+ pixel_size = CLAMP(p_amount, 0.0001, 128.0);
+ dirty_cache = true;
+ _request_update();
+ }
+}
+
+real_t TextMesh::get_pixel_size() const {
+ return pixel_size;
+}
+
+void TextMesh::set_curve_step(real_t p_step) {
+ if (curve_step != p_step) {
+ curve_step = CLAMP(p_step, 0.1, 10.0);
+ dirty_cache = true;
+ _request_update();
+ }
+}
+
+real_t TextMesh::get_curve_step() const {
+ return curve_step;
+}
+
+void TextMesh::set_text_direction(TextServer::Direction p_text_direction) {
+ ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+ if (text_direction != p_text_direction) {
+ text_direction = p_text_direction;
+ dirty_text = true;
+ _request_update();
+ }
+}
+
+TextServer::Direction TextMesh::get_text_direction() const {
+ return text_direction;
+}
+
+void TextMesh::clear_opentype_features() {
+ opentype_features.clear();
+ dirty_font = true;
+ _request_update();
+}
+
+void TextMesh::set_opentype_feature(const String &p_name, int p_value) {
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
+ opentype_features[tag] = p_value;
+ dirty_font = true;
+ _request_update();
+ }
+}
+
+int TextMesh::get_opentype_feature(const String &p_name) const {
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!opentype_features.has(tag)) {
+ return -1;
+ }
+ return opentype_features[tag];
+}
+
+void TextMesh::set_language(const String &p_language) {
+ if (language != p_language) {
+ language = p_language;
+ dirty_text = true;
+ _request_update();
+ }
+}
+
+String TextMesh::get_language() const {
+ return language;
+}
+
+void TextMesh::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
+ if (st_parser != p_parser) {
+ st_parser = p_parser;
+ dirty_text = true;
+ _request_update();
+ }
+}
+
+TextServer::StructuredTextParser TextMesh::get_structured_text_bidi_override() const {
+ return st_parser;
+}
+
+void TextMesh::set_structured_text_bidi_override_options(Array p_args) {
+ if (st_args != p_args) {
+ st_args = p_args;
+ dirty_text = true;
+ _request_update();
+ }
+}
+
+Array TextMesh::get_structured_text_bidi_override_options() const {
+ return st_args;
+}
+
+void TextMesh::set_uppercase(bool p_uppercase) {
+ if (uppercase != p_uppercase) {
+ uppercase = p_uppercase;
+ dirty_text = true;
+ _request_update();
+ }
+}
+
+bool TextMesh::is_uppercase() const {
+ return uppercase;
+}
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index d447dad97a..3849c92a7b 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,16 +31,17 @@
#ifndef PRIMITIVE_MESHES_H
#define PRIMITIVE_MESHES_H
+#include "scene/resources/font.h"
#include "scene/resources/mesh.h"
+#include "servers/text_server.h"
///@TODO probably should change a few integers to unsigned integers...
/**
- @author Bastiaan Olij <mux213@gmail.com>
-
Base class for all the classes in this file, handles a number of code functions that are shared among all meshes.
This class is set apart that it assumes a single surface is always generated for our mesh.
*/
+
class PrimitiveMesh : public Mesh {
GDCLASS(PrimitiveMesh, Mesh);
@@ -65,8 +66,9 @@ protected:
static void _bind_methods();
- virtual void _create_mesh_array(Array &p_arr) const = 0;
+ virtual void _create_mesh_array(Array &p_arr) const {}
void _request_update();
+ GDVIRTUAL0RC(Array, _create_mesh_array)
public:
virtual int get_surface_count() const override;
@@ -107,8 +109,8 @@ class CapsuleMesh : public PrimitiveMesh {
GDCLASS(CapsuleMesh, PrimitiveMesh);
private:
- float radius = 1.0;
- float height = 3.0;
+ float radius = 0.5;
+ float height = 2.0;
int radial_segments = 64;
int rings = 8;
@@ -117,6 +119,8 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
+ static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8);
+
void set_radius(const float p_radius);
float get_radius() const;
@@ -139,7 +143,7 @@ class BoxMesh : public PrimitiveMesh {
GDCLASS(BoxMesh, PrimitiveMesh);
private:
- Vector3 size = Vector3(2.0, 2.0, 2.0);
+ Vector3 size = Vector3(1, 1, 1);
int subdivide_w = 0;
int subdivide_h = 0;
int subdivide_d = 0;
@@ -149,6 +153,8 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
+ static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0);
+
void set_size(const Vector3 &p_size);
Vector3 get_size() const;
@@ -172,8 +178,8 @@ class CylinderMesh : public PrimitiveMesh {
GDCLASS(CylinderMesh, PrimitiveMesh);
private:
- float top_radius = 1.0;
- float bottom_radius = 1.0;
+ float top_radius = 0.5;
+ float bottom_radius = 0.5;
float height = 2.0;
int radial_segments = 64;
int rings = 4;
@@ -183,6 +189,8 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
+ static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4);
+
void set_top_radius(const float p_radius);
float get_top_radius() const;
@@ -241,7 +249,7 @@ class PrismMesh : public PrimitiveMesh {
private:
float left_to_right = 0.5;
- Vector3 size = Vector3(2.0, 2.0, 2.0);
+ Vector3 size = Vector3(1.0, 1.0, 1.0);
int subdivide_w = 0;
int subdivide_h = 0;
int subdivide_d = 0;
@@ -303,8 +311,8 @@ class SphereMesh : public PrimitiveMesh {
GDCLASS(SphereMesh, PrimitiveMesh);
private:
- float radius = 1.0;
- float height = 2.0;
+ float radius = 0.5;
+ float height = 1.0;
int radial_segments = 64;
int rings = 32;
bool is_hemisphere = false;
@@ -314,6 +322,8 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
+ static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false);
+
void set_radius(const float p_radius);
float get_radius() const;
@@ -350,7 +360,7 @@ class TubeTrailMesh : public PrimitiveMesh {
GDCLASS(TubeTrailMesh, PrimitiveMesh);
private:
- float radius = 1.0;
+ float radius = 0.5;
int radial_steps = 8;
int sections = 5;
float section_length = 0.2;
@@ -439,5 +449,129 @@ public:
RibbonTrailMesh();
};
+/**
+ Text...
+*/
+
+class TextMesh : public PrimitiveMesh {
+ GDCLASS(TextMesh, PrimitiveMesh);
+
+private:
+ struct ContourPoint {
+ Vector2 point;
+ bool sharp = false;
+
+ ContourPoint(){};
+ ContourPoint(const Vector2 &p_pt, bool p_sharp) {
+ point = p_pt;
+ sharp = p_sharp;
+ };
+ };
+ struct ContourInfo {
+ real_t length = 0.0;
+ bool ccw = true;
+ ContourInfo(){};
+ ContourInfo(real_t p_len, bool p_ccw) {
+ length = p_len;
+ ccw = p_ccw;
+ }
+ };
+ struct GlyphMeshData {
+ Vector<Vector2> triangles;
+ Vector<Vector<ContourPoint>> contours;
+ Vector<ContourInfo> contours_info;
+ Vector2 min_p = Vector2(INFINITY, INFINITY);
+ Vector2 max_p = Vector2(-INFINITY, -INFINITY);
+ };
+ mutable HashMap<uint32_t, GlyphMeshData> cache;
+
+ RID text_rid;
+ String text;
+ String xl_text;
+
+ int font_size = 16;
+ Ref<Font> font_override;
+ float width = 500.0;
+
+ HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER;
+ bool uppercase = false;
+ Dictionary opentype_features;
+ String language;
+ TextServer::Direction text_direction = TextServer::DIRECTION_AUTO;
+ TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
+ Array st_args;
+
+ real_t depth = 0.05;
+ real_t pixel_size = 0.01;
+ real_t curve_step = 0.5;
+
+ mutable bool dirty_text = true;
+ mutable bool dirty_font = true;
+ mutable bool dirty_cache = true;
+
+ void _generate_glyph_mesh_data(uint32_t p_hash, const Glyph &p_glyph) const;
+ void _font_changed();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+ virtual void _create_mesh_array(Array &p_arr) const override;
+
+ 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:
+ GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
+
+ TextMesh();
+ ~TextMesh();
+
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
+
+ void set_text(const String &p_string);
+ String get_text() const;
+
+ void set_font(const Ref<Font> &p_font);
+ Ref<Font> get_font() const;
+ Ref<Font> _get_font_or_default() const;
+
+ void set_font_size(int p_size);
+ int get_font_size() const;
+
+ void set_text_direction(TextServer::Direction p_text_direction);
+ TextServer::Direction get_text_direction() const;
+
+ void set_opentype_feature(const String &p_name, int p_value);
+ int get_opentype_feature(const String &p_name) const;
+ void clear_opentype_features();
+
+ void set_language(const String &p_language);
+ String get_language() const;
+
+ void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
+ TextServer::StructuredTextParser get_structured_text_bidi_override() const;
+
+ void set_structured_text_bidi_override_options(Array p_args);
+ Array get_structured_text_bidi_override_options() const;
+
+ void set_uppercase(bool p_uppercase);
+ bool is_uppercase() const;
+
+ void set_width(real_t p_width);
+ real_t get_width() const;
+
+ void set_depth(real_t p_depth);
+ real_t get_depth() const;
+
+ void set_curve_step(real_t p_step);
+ real_t get_curve_step() const;
+
+ void set_pixel_size(real_t p_amount);
+ real_t get_pixel_size() const;
+};
+
VARIANT_ENUM_CAST(RibbonTrailMesh::Shape)
#endif
diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp
index 17ce0b34ac..5e88c9974c 100644
--- a/scene/resources/rectangle_shape_2d.cpp
+++ b/scene/resources/rectangle_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -58,6 +58,7 @@ bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const
#endif // DISABLE_DEPRECATED
void RectangleShape2D::set_size(const Vector2 &p_size) {
+ ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0, "RectangleShape2D size cannot be negative.");
size = p_size;
_update_shape();
}
diff --git a/scene/resources/rectangle_shape_2d.h b/scene/resources/rectangle_shape_2d.h
index f1e8be4c5b..fa85aef428 100644
--- a/scene/resources/rectangle_shape_2d.h
+++ b/scene/resources/rectangle_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index ea3b72af1b..193bd0ac05 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
+#include "core/io/missing_resource.h"
#include "core/io/resource_format_binary.h"
#include "core/version.h"
@@ -150,10 +151,10 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R
if (ext_resources[id].cache.is_valid()) {
r_res = ext_resources[id].cache;
} else if (use_sub_threads) {
- RES res = ResourceLoader::load_threaded_get(path);
+ Ref<Resource> res = ResourceLoader::load_threaded_get(path);
if (res.is_null()) {
if (ResourceLoader::get_abort_on_missing_resources()) {
- error = ERR_FILE_CORRUPT;
+ error = ERR_FILE_MISSING_DEPENDENCIES;
error_text = "[ext_resource] referenced nonexistent resource at: " + path;
_printerr();
return error;
@@ -165,13 +166,13 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R
r_res = res;
}
} else {
- error = ERR_FILE_CORRUPT;
+ error = ERR_FILE_MISSING_DEPENDENCIES;
error_text = "[ext_resource] referenced non-loaded resource at: " + path;
_printerr();
return error;
}
} else {
- r_res = RES();
+ r_res = Ref<Resource>();
}
VariantParser::get_token(p_stream, token, line, r_err_str);
@@ -265,7 +266,9 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &parser);
if (error) {
- if (error != ERR_FILE_EOF) {
+ if (error == ERR_FILE_MISSING_DEPENDENCIES) {
+ // Resource loading error, just skip it.
+ } else if (error != ERR_FILE_EOF) {
_printerr();
return Ref<PackedScene>();
} else {
@@ -274,12 +277,12 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
}
}
- if (assign != String()) {
+ if (!assign.is_empty()) {
int nameidx = packed_scene->get_state()->add_name(assign);
int valueidx = packed_scene->get_state()->add_value(value);
packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx);
//it's assignment
- } else if (next_tag.name != String()) {
+ } else if (!next_tag.name.is_empty()) {
break;
}
}
@@ -313,6 +316,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
StringName method = next_tag.fields["method"];
StringName signal = next_tag.fields["signal"];
int flags = Object::CONNECT_PERSIST;
+ int unbinds = 0;
Array binds;
if (next_tag.fields.has("flags")) {
@@ -323,6 +327,10 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
binds = next_tag.fields["binds"];
}
+ if (next_tag.fields.has("unbinds")) {
+ unbinds = next_tag.fields["unbinds"];
+ }
+
Vector<int> bind_ints;
for (int i = 0; i < binds.size(); i++) {
bind_ints.push_back(packed_scene->get_state()->add_value(binds[i]));
@@ -334,6 +342,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
packed_scene->get_state()->add_name(signal),
packed_scene->get_state()->add_name(method),
flags,
+ unbinds,
bind_ints);
error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser);
@@ -350,7 +359,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
} else if (next_tag.name == "editable") {
if (!next_tag.fields.has("path")) {
error = ERR_FILE_CORRUPT;
- error_text = "missing 'path' field from connection tag";
+ error_text = "missing 'path' field from editable tag";
_printerr();
return Ref<PackedScene>();
}
@@ -424,7 +433,7 @@ Error ResourceLoaderText::load() {
}
}
- if (path.find("://") == -1 && path.is_relative_path()) {
+ if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
}
@@ -452,7 +461,7 @@ Error ResourceLoaderText::load() {
}
} else {
- RES res = ResourceLoader::load(path, type);
+ Ref<Resource> res = ResourceLoader::load(path, type);
if (res.is_null()) {
if (ResourceLoader::get_abort_on_missing_resources()) {
@@ -527,6 +536,8 @@ Error ResourceLoaderText::load() {
}
}
+ MissingResource *missing_resource = nullptr;
+
if (res.is_null()) { //not reuse
if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && ResourceCache::has(path)) { //only if it doesn't exist
//cached, do not assign
@@ -537,10 +548,17 @@ Error ResourceLoaderText::load() {
Object *obj = ClassDB::instantiate(type);
if (!obj) {
- error_text += "Can't create sub resource of type: " + type;
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
+ if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) {
+ missing_resource = memnew(MissingResource);
+ missing_resource->set_original_class(type);
+ missing_resource->set_recording_properties(true);
+ obj = missing_resource;
+ } else {
+ error_text += "Can't create sub resource of type: " + type;
+ _printerr();
+ error = ERR_FILE_CORRUPT;
+ return error;
+ }
}
Resource *r = Object::cast_to<Resource>(obj);
@@ -564,6 +582,8 @@ Error ResourceLoaderText::load() {
res->set_scene_unique_id(id);
}
+ Dictionary missing_resource_properties;
+
while (true) {
String assign;
Variant value;
@@ -575,12 +595,28 @@ Error ResourceLoaderText::load() {
return error;
}
- if (assign != String()) {
+ if (!assign.is_empty()) {
if (do_assign) {
- res->set(assign, value);
+ bool set_valid = true;
+
+ if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) {
+ // If the property being set is a missing resource (and the parent is not),
+ // then setting it will most likely not work.
+ // Instead, save it as metadata.
+
+ Ref<MissingResource> mr = value;
+ if (mr.is_valid()) {
+ missing_resource_properties[assign] = mr;
+ set_valid = false;
+ }
+ }
+
+ if (set_valid) {
+ res->set(assign, value);
+ }
}
//it's assignment
- } else if (next_tag.name != String()) {
+ } else if (!next_tag.name.is_empty()) {
error = OK;
break;
} else {
@@ -591,6 +627,14 @@ Error ResourceLoaderText::load() {
}
}
+ if (missing_resource) {
+ missing_resource->set_recording_properties(false);
+ }
+
+ if (!missing_resource_properties.is_empty()) {
+ res->set_meta(META_MISSING_RESOURCES, missing_resource_properties);
+ }
+
if (progress && resources_total > 0) {
*progress = resource_current / float(resources_total);
}
@@ -616,13 +660,22 @@ Error ResourceLoaderText::load() {
}
}
+ MissingResource *missing_resource = nullptr;
+
if (!resource.is_valid()) {
Object *obj = ClassDB::instantiate(res_type);
if (!obj) {
- error_text += "Can't create sub resource of type: " + res_type;
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
+ if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) {
+ missing_resource = memnew(MissingResource);
+ missing_resource->set_original_class(res_type);
+ missing_resource->set_recording_properties(true);
+ obj = missing_resource;
+ } else {
+ error_text += "Can't create sub resource of type: " + res_type;
+ _printerr();
+ error = ERR_FILE_CORRUPT;
+ return error;
+ }
}
Resource *r = Object::cast_to<Resource>(obj);
@@ -638,6 +691,8 @@ Error ResourceLoaderText::load() {
resource_current++;
+ Dictionary missing_resource_properties;
+
while (true) {
String assign;
Variant value;
@@ -659,23 +714,49 @@ Error ResourceLoaderText::load() {
return error;
}
- if (assign != String()) {
- resource->set(assign, value);
+ if (!assign.is_empty()) {
+ bool set_valid = true;
+
+ if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) {
+ // If the property being set is a missing resource (and the parent is not),
+ // then setting it will most likely not work.
+ // Instead, save it as metadata.
+
+ Ref<MissingResource> mr = value;
+ if (mr.is_valid()) {
+ missing_resource_properties[assign] = mr;
+ set_valid = false;
+ }
+ }
+
+ if (set_valid) {
+ resource->set(assign, value);
+ }
//it's assignment
- } else if (next_tag.name != String()) {
+ } else if (!next_tag.name.is_empty()) {
error = ERR_FILE_CORRUPT;
error_text = "Extra tag found when parsing main resource file";
_printerr();
return error;
} else {
- error = OK;
- if (progress && resources_total > 0) {
- *progress = resource_current / float(resources_total);
- }
-
- return error;
+ break;
}
}
+
+ if (missing_resource) {
+ missing_resource->set_recording_properties(false);
+ }
+
+ if (!missing_resource_properties.is_empty()) {
+ resource->set_meta(META_MISSING_RESOURCES, missing_resource_properties);
+ }
+
+ error = OK;
+ if (progress && resources_total > 0) {
+ *progress = resource_current / float(resources_total);
+ }
+
+ return error;
}
//for scene files
@@ -730,11 +811,7 @@ void ResourceLoaderText::set_translation_remapped(bool p_remapped) {
ResourceLoaderText::ResourceLoaderText() {}
-ResourceLoaderText::~ResourceLoaderText() {
- memdelete(f);
-}
-
-void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) {
+void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) {
open(p_f);
ignore_resource_parsing = true;
ERR_FAIL_COND(error != OK);
@@ -768,7 +845,7 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen
}
}
- if (!using_uid && path.find("://") == -1 && path.is_relative_path()) {
+ if (!using_uid && !path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
}
@@ -790,13 +867,13 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen
}
}
-Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map) {
+Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map) {
open(p_f, true);
ERR_FAIL_COND_V(error != OK, error);
ignore_resource_parsing = true;
//FileAccess
- FileAccess *fw = nullptr;
+ Ref<FileAccess> fw;
String base_path = local_path.get_base_dir();
@@ -806,23 +883,20 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p
Error err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
if (err != OK) {
- if (fw) {
- memdelete(fw);
- }
error = ERR_FILE_CORRUPT;
ERR_FAIL_V(error);
}
if (next_tag.name != "ext_resource") {
//nothing was done
- if (!fw) {
+ if (fw.is_null()) {
return OK;
}
break;
} else {
- if (!fw) {
+ if (fw.is_null()) {
fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE);
if (is_scene) {
fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n");
@@ -832,7 +906,6 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p
}
if (!next_tag.fields.has("path") || !next_tag.fields.has("id") || !next_tag.fields.has("type")) {
- memdelete(fw);
error = ERR_FILE_CORRUPT;
ERR_FAIL_V(error);
}
@@ -890,25 +963,17 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p
fw->store_8(c);
c = f->get_8();
}
- f->close();
bool all_ok = fw->get_error() == OK;
- memdelete(fw);
-
if (!all_ok) {
return ERR_CANT_CREATE;
}
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- da->remove(p_path);
- da->rename(p_path + ".depren", p_path);
- memdelete(da);
-
return OK;
}
-void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) {
+void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) {
error = OK;
lines = 1;
@@ -985,23 +1050,23 @@ void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) {
rp.userdata = this;
}
-static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false) {
+static void bs_save_unicode_string(Ref<FileAccess> p_f, const String &p_string, bool p_bit_on_len = false) {
CharString utf8 = p_string.utf8();
if (p_bit_on_len) {
- f->store_32((utf8.length() + 1) | 0x80000000);
+ p_f->store_32((utf8.length() + 1) | 0x80000000);
} else {
- f->store_32(utf8.length() + 1);
+ p_f->store_32(utf8.length() + 1);
}
- f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
+ p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
}
-Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) {
+Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_path) {
if (error) {
return error;
}
- FileAccessRef wf = FileAccess::open(p_path, FileAccess::WRITE);
- if (!wf) {
+ Ref<FileAccess> wf = FileAccess::open(p_path, FileAccess::WRITE);
+ if (wf.is_null()) {
return ERR_CANT_OPEN;
}
@@ -1016,7 +1081,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
static const int save_format_version = 3; //use format version 3 for saving
wf->store_32(save_format_version);
- bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type);
+ bs_save_unicode_string(wf, is_scene ? "PackedScene" : resource_type);
wf->store_64(0); //offset to import metadata, this is no longer used
wf->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS);
@@ -1071,8 +1136,8 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
uid = ResourceUID::get_singleton()->text_to_id(uidt);
}
- bs_save_unicode_string(wf.f, type);
- bs_save_unicode_string(wf.f, path);
+ bs_save_unicode_string(wf, type);
+ bs_save_unicode_string(wf, path);
wf->store_64(uid);
int lindex = dummy_read.external_resources.size();
@@ -1101,146 +1166,145 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
wf->store_32(0); //zero sub resources, still parsing them
String temp_file = p_path + ".temp";
- FileAccessRef wf2 = FileAccess::open(temp_file, FileAccess::WRITE);
- if (!wf2) {
- return ERR_CANT_OPEN;
- }
-
Vector<uint64_t> local_offsets;
Vector<uint64_t> local_pointers_pos;
+ {
+ Ref<FileAccess> wf2 = FileAccess::open(temp_file, FileAccess::WRITE);
+ if (wf2.is_null()) {
+ return ERR_CANT_OPEN;
+ }
- while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
- String type;
- int id = -1;
- bool main_res;
+ while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
+ String type;
+ int id = -1;
+ bool main_res;
- if (next_tag.name == "sub_resource") {
- if (!next_tag.fields.has("type")) {
- error = ERR_FILE_CORRUPT;
- error_text = "Missing 'type' in external resource tag";
- _printerr();
- return error;
- }
+ if (next_tag.name == "sub_resource") {
+ if (!next_tag.fields.has("type")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'type' in external resource tag";
+ _printerr();
+ return error;
+ }
- if (!next_tag.fields.has("id")) {
- error = ERR_FILE_CORRUPT;
- error_text = "Missing 'id' in external resource tag";
- _printerr();
- return error;
+ if (!next_tag.fields.has("id")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'id' in external resource tag";
+ _printerr();
+ return error;
+ }
+
+ type = next_tag.fields["type"];
+ id = next_tag.fields["id"];
+ main_res = false;
+ } else {
+ type = res_type;
+ id = 0; //used for last anyway
+ main_res = true;
}
- type = next_tag.fields["type"];
- id = next_tag.fields["id"];
- main_res = false;
- } else {
- type = res_type;
- id = 0; //used for last anyway
- main_res = true;
- }
+ local_offsets.push_back(wf2->get_position());
- local_offsets.push_back(wf2->get_position());
+ bs_save_unicode_string(wf, "local://" + itos(id));
+ local_pointers_pos.push_back(wf->get_position());
+ wf->store_64(0); //temp local offset
- bs_save_unicode_string(wf, "local://" + itos(id));
- local_pointers_pos.push_back(wf->get_position());
- wf->store_64(0); //temp local offset
+ bs_save_unicode_string(wf2, type);
+ uint64_t propcount_ofs = wf2->get_position();
+ wf2->store_32(0);
- bs_save_unicode_string(wf2, type);
- uint64_t propcount_ofs = wf2->get_position();
- wf2->store_32(0);
+ int prop_count = 0;
- int prop_count = 0;
+ while (true) {
+ String assign;
+ Variant value;
- while (true) {
- String assign;
- Variant value;
+ error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
- error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
+ if (error) {
+ if (main_res && error == ERR_FILE_EOF) {
+ next_tag.name = ""; //exit
+ break;
+ }
- if (error) {
- if (main_res && error == ERR_FILE_EOF) {
- next_tag.name = ""; //exit
- break;
+ _printerr();
+ return error;
}
- _printerr();
- return error;
+ if (!assign.is_empty()) {
+ HashMap<StringName, int> empty_string_map; //unused
+ bs_save_unicode_string(wf2, assign, true);
+ ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map);
+ prop_count++;
+
+ } else if (!next_tag.name.is_empty()) {
+ error = OK;
+ break;
+ } else {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Premature end of file while parsing [sub_resource]";
+ _printerr();
+ return error;
+ }
}
- if (assign != String()) {
- Map<StringName, int> empty_string_map; //unused
- bs_save_unicode_string(wf2, assign, true);
- ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map);
- prop_count++;
+ wf2->seek(propcount_ofs);
+ wf2->store_32(prop_count);
+ wf2->seek_end();
+ }
- } else if (next_tag.name != String()) {
- error = OK;
- break;
- } else {
- error = ERR_FILE_CORRUPT;
- error_text = "Premature end of file while parsing [sub_resource]";
+ if (next_tag.name == "node") {
+ //this is a node, must save one more!
+
+ if (!is_scene) {
+ error_text += "found the 'node' tag on a resource file!";
_printerr();
+ error = ERR_FILE_CORRUPT;
return error;
}
- }
- wf2->seek(propcount_ofs);
- wf2->store_32(prop_count);
- wf2->seek_end();
- }
-
- if (next_tag.name == "node") {
- //this is a node, must save one more!
+ Ref<PackedScene> packed_scene = _parse_node_tag(rp);
- if (!is_scene) {
- error_text += "found the 'node' tag on a resource file!";
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
- }
+ if (!packed_scene.is_valid()) {
+ return error;
+ }
- Ref<PackedScene> packed_scene = _parse_node_tag(rp);
+ error = OK;
+ //get it here
+ List<PropertyInfo> props;
+ packed_scene->get_property_list(&props);
- if (!packed_scene.is_valid()) {
- return error;
- }
+ bs_save_unicode_string(wf, "local://0");
+ local_pointers_pos.push_back(wf->get_position());
+ wf->store_64(0); //temp local offset
- error = OK;
- //get it here
- List<PropertyInfo> props;
- packed_scene->get_property_list(&props);
+ local_offsets.push_back(wf2->get_position());
+ bs_save_unicode_string(wf2, "PackedScene");
+ uint64_t propcount_ofs = wf2->get_position();
+ wf2->store_32(0);
- bs_save_unicode_string(wf, "local://0");
- local_pointers_pos.push_back(wf->get_position());
- wf->store_64(0); //temp local offset
+ int prop_count = 0;
- local_offsets.push_back(wf2->get_position());
- bs_save_unicode_string(wf2, "PackedScene");
- uint64_t propcount_ofs = wf2->get_position();
- wf2->store_32(0);
+ for (const PropertyInfo &E : props) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
+ continue;
+ }
- int prop_count = 0;
+ String name = E.name;
+ Variant value = packed_scene->get(name);
- for (const PropertyInfo &E : props) {
- if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
- continue;
+ HashMap<StringName, int> empty_string_map; //unused
+ bs_save_unicode_string(wf2, name, true);
+ ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map);
+ prop_count++;
}
- String name = E.name;
- Variant value = packed_scene->get(name);
-
- Map<StringName, int> empty_string_map; //unused
- bs_save_unicode_string(wf2, name, true);
- ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map);
- prop_count++;
+ wf2->seek(propcount_ofs);
+ wf2->store_32(prop_count);
+ wf2->seek_end();
}
-
- wf2->seek(propcount_ofs);
- wf2->store_32(prop_count);
- wf2->seek_end();
}
- wf2->close();
-
uint64_t offset_from = wf->get_position();
wf->seek(sub_res_count_pos); //plus one because the saved one
wf->store_32(local_offsets.size());
@@ -1255,18 +1319,16 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path)
Vector<uint8_t> data = FileAccess::get_file_as_array(temp_file);
wf->store_buffer(data.ptr(), data.size());
{
- DirAccessRef dar = DirAccess::open(temp_file.get_base_dir());
+ Ref<DirAccess> dar = DirAccess::open(temp_file.get_base_dir());
dar->remove(temp_file);
}
wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end
- wf->close();
-
return OK;
}
-String ResourceLoaderText::recognize(FileAccess *p_f) {
+String ResourceLoaderText::recognize(Ref<FileAccess> p_f) {
error = OK;
lines = 1;
@@ -1310,7 +1372,7 @@ String ResourceLoaderText::recognize(FileAccess *p_f) {
return tag.fields["type"];
}
-ResourceUID::ID ResourceLoaderText::get_uid(FileAccess *p_f) {
+ResourceUID::ID ResourceLoaderText::get_uid(Ref<FileAccess> p_f) {
error = OK;
lines = 1;
@@ -1338,19 +1400,19 @@ ResourceUID::ID ResourceLoaderText::get_uid(FileAccess *p_f) {
/////////////////////
-RES ResourceFormatLoaderText::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+Ref<Resource> ResourceFormatLoaderText::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_CANT_OPEN;
}
Error err;
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
- ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot open file '" + p_path + "'.");
+ ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot open file '" + p_path + "'.");
ResourceLoaderText loader;
- String path = p_original_path != "" ? p_original_path : p_path;
+ String path = !p_original_path.is_empty() ? p_original_path : p_path;
loader.cache_mode = p_cache_mode;
loader.use_sub_threads = p_use_sub_threads;
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
@@ -1364,19 +1426,22 @@ RES ResourceFormatLoaderText::load(const String &p_path, const String &p_origina
if (err == OK) {
return loader.get_resource();
} else {
- return RES();
+ return Ref<Resource>();
}
}
void ResourceFormatLoaderText::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
- if (p_type == "") {
+ if (p_type.is_empty()) {
get_recognized_extensions(p_extensions);
return;
}
- if (p_type == "PackedScene") {
+ if (ClassDB::is_parent_class("PackedScene", p_type)) {
p_extensions->push_back("tscn");
- } else {
+ }
+
+ // Don't allow .tres for PackedScenes.
+ if (p_type != "PackedScene") {
p_extensions->push_back("tres");
}
}
@@ -1400,8 +1465,8 @@ String ResourceFormatLoaderText::get_resource_type(const String &p_path) const {
// ...for anything else must test...
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
return ""; //could not read
}
@@ -1419,8 +1484,8 @@ ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path)
return ResourceUID::INVALID_ID;
}
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
return ResourceUID::INVALID_ID; //could not read
}
@@ -1431,8 +1496,8 @@ ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path)
}
void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- if (!f) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
ERR_FAIL();
}
@@ -1442,23 +1507,34 @@ void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<Strin
loader.get_dependencies(f, p_dependencies, p_add_types);
}
-Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const Map<String, String> &p_map) {
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- if (!f) {
- ERR_FAIL_V(ERR_CANT_OPEN);
+Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) {
+ Error err = OK;
+ {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
+ ERR_FAIL_V(ERR_CANT_OPEN);
+ }
+
+ ResourceLoaderText loader;
+ loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ loader.res_path = loader.local_path;
+ err = loader.rename_dependencies(f, p_path, p_map);
}
- ResourceLoaderText loader;
- loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- loader.res_path = loader.local_path;
- return loader.rename_dependencies(f, p_path, p_map);
+ if (err == OK) {
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ da->remove(p_path);
+ da->rename(p_path + ".depren", p_path);
+ }
+
+ return err;
}
ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = nullptr;
Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) {
Error err;
- FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err);
+ Ref<FileAccess> f = FileAccess::open(p_src_path, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_src_path + "'.");
@@ -1481,12 +1557,12 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path,
/*****************************************************************************************************/
/*****************************************************************************************************/
-String ResourceFormatSaverTextInstance::_write_resources(void *ud, const RES &p_resource) {
- ResourceFormatSaverTextInstance *rsi = (ResourceFormatSaverTextInstance *)ud;
+String ResourceFormatSaverTextInstance::_write_resources(void *ud, const Ref<Resource> &p_resource) {
+ ResourceFormatSaverTextInstance *rsi = static_cast<ResourceFormatSaverTextInstance *>(ud);
return rsi->_write_resource(p_resource);
}
-String ResourceFormatSaverTextInstance::_write_resource(const RES &res) {
+String ResourceFormatSaverTextInstance::_write_resource(const Ref<Resource> &res) {
if (external_resources.has(res)) {
return "ExtResource( \"" + external_resources[res] + "\" )";
} else {
@@ -1509,7 +1585,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) {
void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, bool p_main) {
switch (p_variant.get_type()) {
case Variant::OBJECT: {
- RES res = p_variant;
+ Ref<Resource> res = p_variant;
if (res.is_null() || external_resources.has(res)) {
return;
@@ -1546,7 +1622,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
Variant v = res->get(I->get().name);
if (pi.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
- RES sres = v;
+ Ref<Resource> sres = v;
if (sres.is_valid()) {
NonPersistentKey npk;
npk.base = res;
@@ -1590,15 +1666,24 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
}
}
-Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
+static String _resource_get_class(Ref<Resource> p_resource) {
+ Ref<MissingResource> missing_resource = p_resource;
+ if (missing_resource.is_valid()) {
+ return missing_resource->get_original_class();
+ } else {
+ return p_resource->get_class();
+ }
+}
+
+Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
if (p_path.ends_with(".tscn")) {
packed_scene = p_resource;
}
Error err;
- f = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err, ERR_CANT_OPEN, "Cannot save file '" + p_path + "'.");
- FileAccessRef _fref(f);
+ Ref<FileAccess> _fref(f);
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
@@ -1631,7 +1716,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
{
String title = packed_scene.is_valid() ? "[gd_scene " : "[gd_resource ";
if (packed_scene.is_null()) {
- title += "type=\"" + p_resource->get_class() + "\" ";
+ title += "type=\"" + _resource_get_class(p_resource) + "\" ";
}
int load_steps = saved_resources.size() + external_resources.size();
@@ -1652,10 +1737,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
#ifdef TOOLS_ENABLED
// Keep order from cached ids.
- Set<String> cached_ids_found;
- for (KeyValue<RES, String> &E : external_resources) {
+ RBSet<String> cached_ids_found;
+ for (KeyValue<Ref<Resource>, String> &E : external_resources) {
String cached_id = E.key->get_id_for_path(local_path);
- if (cached_id == "" || cached_ids_found.has(cached_id)) {
+ if (cached_id.is_empty() || cached_ids_found.has(cached_id)) {
int sep_pos = E.value.find("_");
if (sep_pos != -1) {
E.value = E.value.substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance.
@@ -1669,7 +1754,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
}
// Create IDs for non cached resources.
- for (KeyValue<RES, String> &E : external_resources) {
+ for (KeyValue<Ref<Resource>, String> &E : external_resources) {
if (cached_ids_found.has(E.value)) { // Already cached, go on.
continue;
}
@@ -1691,14 +1776,14 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
#else
// Make sure to start from one, as it makes format more readable.
int counter = 1;
- for (KeyValue<RES, String> &E : external_resources) {
+ for (KeyValue<Ref<Resource>, String> &E : external_resources) {
E.value = itos(counter++);
}
#endif
Vector<ResourceSort> sorted_er;
- for (const KeyValue<RES, String> &E : external_resources) {
+ for (const KeyValue<Ref<Resource>, String> &E : external_resources) {
ResourceSort rs;
rs.resource = E.key;
rs.id = E.value;
@@ -1724,12 +1809,12 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_line(String()); // Separate.
}
- Set<String> used_unique_ids;
+ RBSet<String> used_unique_ids;
- for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
- RES res = E->get();
+ for (List<Ref<Resource>>::Element *E = saved_resources.front(); E; E = E->next()) {
+ Ref<Resource> res = E->get();
if (E->next() && res->is_built_in()) {
- if (res->get_scene_unique_id() != "") {
+ if (!res->get_scene_unique_id().is_empty()) {
if (used_unique_ids.has(res->get_scene_unique_id())) {
res->set_scene_unique_id(""); // Repeated.
} else {
@@ -1739,8 +1824,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
}
- for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
- RES res = E->get();
+ for (List<Ref<Resource>>::Element *E = saved_resources.front(); E; E = E->next()) {
+ Ref<Resource> res = E->get();
ERR_CONTINUE(!resource_set.has(res));
bool main = (E->next() == nullptr);
@@ -1752,10 +1837,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_line("[resource]");
} else {
String line = "[sub_resource ";
- if (res->get_scene_unique_id() == "") {
+ if (res->get_scene_unique_id().is_empty()) {
String new_id;
while (true) {
- new_id = res->get_class() + "_" + Resource::generate_scene_unique_id();
+ new_id = _resource_get_class(res) + "_" + Resource::generate_scene_unique_id();
if (!used_unique_ids.has(new_id)) {
break;
@@ -1767,7 +1852,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
String id = res->get_scene_unique_id();
- line += "type=\"" + res->get_class() + "\" id=\"" + id;
+ line += "type=\"" + _resource_get_class(res) + "\" id=\"" + id;
f->store_line(line + "\"]");
if (takeover_paths) {
res->set_path(p_path + "::" + id, true);
@@ -1779,12 +1864,17 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
#endif
}
+ Dictionary missing_resource_properties = p_resource->get_meta(META_MISSING_RESOURCES, Dictionary());
+
List<PropertyInfo> property_list;
res->get_property_list(&property_list);
for (List<PropertyInfo>::Element *PE = property_list.front(); PE; PE = PE->next()) {
if (skip_editor && PE->get().name.begins_with("__editor")) {
continue;
}
+ if (PE->get().name == META_PROPERTY_MISSING_RESOURCES) {
+ continue;
+ }
if (PE->get().usage & PROPERTY_USAGE_STORAGE) {
String name = PE->get().name;
@@ -1799,6 +1889,15 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
} else {
value = res->get(name);
}
+
+ if (PE->get().type == Variant::OBJECT && missing_resource_properties.has(PE->get().name)) {
+ // Was this missing resource overriden? If so do not save the old value.
+ Ref<Resource> ures = value;
+ if (ures.is_null()) {
+ value = missing_resource_properties[PE->get().name];
+ }
+ }
+
Variant default_value = ClassDB::class_get_default_property_value(res->get_class(), name);
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
@@ -1866,7 +1965,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_string(header);
- if (instance_placeholder != String()) {
+ if (!instance_placeholder.is_empty()) {
String vars;
f->store_string(" instance_placeholder=");
VariantWriter::write_to_string(instance_placeholder, vars, _write_resources, this);
@@ -1909,6 +2008,11 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
connstr += " flags=" + itos(flags);
}
+ int unbinds = state->get_connection_unbinds(i);
+ if (unbinds > 0) {
+ connstr += " unbinds=" + itos(unbinds);
+ }
+
Array binds = state->get_connection_binds(i);
f->store_string(connstr);
if (binds.size()) {
@@ -1930,17 +2034,14 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) {
- f->close();
return ERR_CANT_CREATE;
}
- f->close();
-
return OK;
}
-Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
- if (p_path.ends_with(".sct") && p_resource->get_class() != "PackedScene") {
+Error ResourceFormatSaverText::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
+ if (p_path.ends_with(".tscn") && !Ref<PackedScene>(p_resource).is_valid()) {
return ERR_FILE_UNRECOGNIZED;
}
@@ -1948,15 +2049,15 @@ Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource,
return saver.save(p_path, p_resource, p_flags);
}
-bool ResourceFormatSaverText::recognize(const RES &p_resource) const {
- return true; // all recognized!
+bool ResourceFormatSaverText::recognize(const Ref<Resource> &p_resource) const {
+ return true; // All resources recognized!
}
-void ResourceFormatSaverText::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
- if (p_resource->get_class() == "PackedScene") {
- p_extensions->push_back("tscn"); //text scene
+void ResourceFormatSaverText::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
+ if (Ref<PackedScene>(p_resource).is_valid()) {
+ p_extensions->push_back("tscn"); // Text scene.
} else {
- p_extensions->push_back("tres"); //text resource
+ p_extensions->push_back("tres"); // Text resource.
}
}
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index 373e71b2c4..adab503599 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,12 +43,12 @@ class ResourceLoaderText {
String res_path;
String error_text;
- FileAccess *f = nullptr;
+ Ref<FileAccess> f;
VariantParser::StreamFile stream;
struct ExtResource {
- RES cache;
+ Ref<Resource> cache;
String path;
String type;
};
@@ -58,8 +58,8 @@ class ResourceLoaderText {
bool ignore_resource_parsing = false;
- Map<String, ExtResource> ext_resources;
- Map<String, RES> int_resources;
+ HashMap<String, ExtResource> ext_resources;
+ HashMap<String, Ref<Resource>> int_resources;
int resources_total = 0;
int resource_current = 0;
@@ -76,7 +76,7 @@ class ResourceLoaderText {
ResourceUID::ID res_uid = ResourceUID::INVALID_ID;
- Map<String, String> remaps;
+ HashMap<String, String> remaps;
static Error _parse_sub_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_sub_resource(p_stream, r_res, line, r_err_str); }
static Error _parse_ext_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_ext_resource(p_stream, r_res, line, r_err_str); }
@@ -90,14 +90,14 @@ class ResourceLoaderText {
};
struct DummyReadData {
- Map<RES, int> external_resources;
- Map<String, RES> rev_external_resources;
- Map<RES, int> resource_index_map;
- Map<String, RES> resource_map;
+ HashMap<Ref<Resource>, int> external_resources;
+ HashMap<String, Ref<Resource>> rev_external_resources;
+ HashMap<Ref<Resource>, int> resource_index_map;
+ HashMap<String, Ref<Resource>> resource_map;
};
- static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); }
- static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); }
+ static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy(static_cast<DummyReadData *>(p_self), p_stream, r_res, line, r_err_str); }
+ static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy(static_cast<DummyReadData *>(p_self), p_stream, r_res, line, r_err_str); }
static Error _parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
static Error _parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
@@ -108,7 +108,7 @@ class ResourceLoaderText {
Error error = OK;
- RES resource;
+ Ref<Resource> resource;
Ref<PackedScene> _parse_node_tag(VariantParser::ResourceParser &parser);
@@ -120,28 +120,27 @@ public:
int get_stage_count() const;
void set_translation_remapped(bool p_remapped);
- void open(FileAccess *p_f, bool p_skip_first_tag = false);
- String recognize(FileAccess *p_f);
- ResourceUID::ID get_uid(FileAccess *p_f);
- void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types);
- Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map);
+ void open(Ref<FileAccess> p_f, bool p_skip_first_tag = false);
+ String recognize(Ref<FileAccess> p_f);
+ ResourceUID::ID get_uid(Ref<FileAccess> p_f);
+ void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types);
+ Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map);
- Error save_as_binary(FileAccess *p_f, const String &p_path);
+ Error save_as_binary(Ref<FileAccess> p_f, const String &p_path);
ResourceLoaderText();
- ~ResourceLoaderText();
};
class ResourceFormatLoaderText : public ResourceFormatLoader {
public:
static ResourceFormatLoaderText *singleton;
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
- virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
+ virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path);
@@ -157,23 +156,22 @@ class ResourceFormatSaverTextInstance {
bool relative_paths = false;
bool bundle_resources = false;
bool skip_editor = false;
- FileAccess *f = nullptr;
struct NonPersistentKey { //for resource properties generated on the fly
- RES base;
+ Ref<Resource> base;
StringName property;
bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; }
};
- Map<NonPersistentKey, RES> non_persistent_map;
+ RBMap<NonPersistentKey, Ref<Resource>> non_persistent_map;
- Set<RES> resource_set;
- List<RES> saved_resources;
- Map<RES, String> external_resources;
- Map<RES, String> internal_resources;
+ RBSet<Ref<Resource>> resource_set;
+ List<Ref<Resource>> saved_resources;
+ HashMap<Ref<Resource>, String> external_resources;
+ HashMap<Ref<Resource>, String> internal_resources;
struct ResourceSort {
- RES resource;
+ Ref<Resource> resource;
String id;
bool operator<(const ResourceSort &p_right) const {
return id.naturalnocasecmp_to(p_right.id) < 0;
@@ -182,19 +180,19 @@ class ResourceFormatSaverTextInstance {
void _find_resources(const Variant &p_variant, bool p_main = false);
- static String _write_resources(void *ud, const RES &p_resource);
- String _write_resource(const RES &res);
+ static String _write_resources(void *ud, const Ref<Resource> &p_resource);
+ String _write_resource(const Ref<Resource> &res);
public:
- Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
+ Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0);
};
class ResourceFormatSaverText : public ResourceFormatSaver {
public:
static ResourceFormatSaverText *singleton;
- virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
- virtual bool recognize(const RES &p_resource) const;
- virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
+ virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0);
+ virtual bool recognize(const Ref<Resource> &p_resource) const;
+ virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
ResourceFormatSaverText();
};
diff --git a/scene/resources/scene_replication_config.cpp b/scene/resources/scene_replication_config.cpp
new file mode 100644
index 0000000000..2acc0f1922
--- /dev/null
+++ b/scene/resources/scene_replication_config.cpp
@@ -0,0 +1,187 @@
+/*************************************************************************/
+/* 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;
+ sync_props.push_back(prop.name);
+ return true;
+ } else if (what == "spawn") {
+ prop.spawn = p_value;
+ spawn_props.push_back(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);
+}
+
+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("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
new file mode 100644
index 0000000000..b791be9414
--- /dev/null
+++ b/scene/resources/scene_replication_config.h
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* 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);
+
+ 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/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp
index 35439634f8..cea8ca1b29 100644
--- a/scene/resources/segment_shape_2d.cpp
+++ b/scene/resources/segment_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/segment_shape_2d.h b/scene/resources/segment_shape_2d.h
index f218955061..6ade0618e3 100644
--- a/scene/resources/segment_shape_2d.h
+++ b/scene/resources/segment_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/separation_ray_shape_2d.cpp b/scene/resources/separation_ray_shape_2d.cpp
index 0acd6d268d..df7b0d969a 100644
--- a/scene/resources/separation_ray_shape_2d.cpp
+++ b/scene/resources/separation_ray_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -59,15 +59,13 @@ void SeparationRayShape2D::draw(const RID &p_to_rid, const Color &p_color) {
xf.rotate(target_position.angle());
xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
- Vector<Vector2> pts;
- pts.push_back(xf.xform(Vector2(arrow_size, 0)));
- pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size)));
- pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size)));
+ Vector<Vector2> pts = {
+ xf.xform(Vector2(arrow_size, 0)),
+ xf.xform(Vector2(0, 0.5 * arrow_size)),
+ xf.xform(Vector2(0, -0.5 * arrow_size))
+ };
- Vector<Color> cols;
- for (int i = 0; i < 3; i++) {
- cols.push_back(p_color);
- }
+ Vector<Color> cols = { p_color, p_color, p_color };
RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID());
}
@@ -91,7 +89,7 @@ void SeparationRayShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape2D::set_slide_on_slope);
ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape2D::get_slide_on_slope);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_length", "get_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope");
}
diff --git a/scene/resources/separation_ray_shape_2d.h b/scene/resources/separation_ray_shape_2d.h
index 5b74e6c727..7c35d53133 100644
--- a/scene/resources/separation_ray_shape_2d.h
+++ b/scene/resources/separation_ray_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/separation_ray_shape_3d.cpp b/scene/resources/separation_ray_shape_3d.cpp
index 376e04c844..736cb60c1c 100644
--- a/scene/resources/separation_ray_shape_3d.cpp
+++ b/scene/resources/separation_ray_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,9 +33,10 @@
#include "servers/physics_server_3d.h"
Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const {
- Vector<Vector3> points;
- points.push_back(Vector3());
- points.push_back(Vector3(0, 0, get_length()));
+ Vector<Vector3> points = {
+ Vector3(),
+ Vector3(0, 0, get_length())
+ };
return points;
}
@@ -79,7 +80,7 @@ void SeparationRayShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape3D::set_slide_on_slope);
ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape3D::get_slide_on_slope);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_length", "get_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope");
}
diff --git a/scene/resources/separation_ray_shape_3d.h b/scene/resources/separation_ray_shape_3d.h
index 54058b6095..0e750a48e6 100644
--- a/scene/resources/separation_ray_shape_3d.h
+++ b/scene/resources/separation_ray_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 84fa07e4f4..d49157b1b8 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -100,7 +100,7 @@ RID Shader::get_rid() const {
void Shader::set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index) {
if (p_texture.is_valid()) {
if (!default_textures.has(p_param)) {
- default_textures[p_param] = Map<int, Ref<Texture2D>>();
+ default_textures[p_param] = HashMap<int, Ref<Texture2D>>();
}
default_textures[p_param][p_index] = p_texture;
RS::get_singleton()->shader_set_default_texture_param(shader, p_param, p_texture->get_rid(), p_index);
@@ -126,7 +126,7 @@ Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param, int
}
void Shader::get_default_texture_param_list(List<StringName> *r_textures) const {
- for (const KeyValue<StringName, Map<int, Ref<Texture2D>>> &E : default_textures) {
+ for (const KeyValue<StringName, HashMap<int, Ref<Texture2D>>> &E : default_textures) {
r_textures->push_back(E.key);
}
}
@@ -172,7 +172,7 @@ Shader::~Shader() {
////////////
-RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+Ref<Resource> ResourceFormatLoaderShader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -210,29 +210,26 @@ String ResourceFormatLoaderShader::get_resource_type(const String &p_path) const
return "";
}
-Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
+Error ResourceFormatSaverShader::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
Ref<Shader> shader = p_resource;
ERR_FAIL_COND_V(shader.is_null(), ERR_INVALID_PARAMETER);
String source = shader->get_code();
Error err;
- FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot save shader '" + p_path + "'.");
file->store_string(source);
if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
- memdelete(file);
return ERR_CANT_CREATE;
}
- file->close();
- memdelete(file);
return OK;
}
-void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
+void ResourceFormatSaverShader::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
if (const Shader *shader = Object::cast_to<Shader>(*p_resource)) {
if (shader->is_text_shader()) {
p_extensions->push_back("gdshader");
@@ -240,6 +237,6 @@ void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource,
}
}
-bool ResourceFormatSaverShader::recognize(const RES &p_resource) const {
+bool ResourceFormatSaverShader::recognize(const Ref<Resource> &p_resource) const {
return p_resource->get_class_name() == "Shader"; //only shader, not inherited
}
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index c688dc1bab..11c9f60ce8 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -58,8 +58,8 @@ private:
// shaders keep a list of ShaderMaterial -> RenderingServer name translations, to make
// conversion fast and save memory.
mutable bool params_cache_dirty = true;
- mutable Map<StringName, StringName> params_cache; //map a shader param to a material param..
- Map<StringName, Map<int, Ref<Texture2D>>> default_textures;
+ mutable HashMap<StringName, StringName> params_cache; //map a shader param to a material param..
+ HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures;
virtual void _update_shader() const; //used for visual shader
protected:
@@ -86,9 +86,9 @@ public:
get_param_list(nullptr);
}
- const Map<StringName, StringName>::Element *E = params_cache.find(p_param);
+ const HashMap<StringName, StringName>::Iterator E = params_cache.find(p_param);
if (E) {
- return E->get();
+ return E->value;
}
return StringName();
}
@@ -103,7 +103,7 @@ VARIANT_ENUM_CAST(Shader::Mode);
class ResourceFormatLoaderShader : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
@@ -111,9 +111,9 @@ public:
class ResourceFormatSaverShader : public ResourceFormatSaver {
public:
- virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
- virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
- virtual bool recognize(const RES &p_resource) const;
+ virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0);
+ virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+ virtual bool recognize(const Ref<Resource> &p_resource) const;
};
#endif // SHADER_H
diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp
index 013b1ef1a9..16ef45829f 100644
--- a/scene/resources/shape_2d.cpp
+++ b/scene/resources/shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h
index 7c5d1344e8..e9dc10eeae 100644
--- a/scene/resources/shape_2d.h
+++ b/scene/resources/shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp
index a02a0e5488..ffb2b27644 100644
--- a/scene/resources/shape_3d.cpp
+++ b/scene/resources/shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -48,6 +48,15 @@ void Shape3D::add_vertices_to_array(Vector<Vector3> &array, const Transform3D &p
}
}
+void Shape3D::set_custom_solver_bias(real_t p_bias) {
+ custom_bias = p_bias;
+ PhysicsServer3D::get_singleton()->shape_set_custom_solver_bias(shape, custom_bias);
+}
+
+real_t Shape3D::get_custom_solver_bias() const {
+ return custom_bias;
+}
+
real_t Shape3D::get_margin() const {
return margin;
}
@@ -99,11 +108,15 @@ void Shape3D::_update_shape() {
}
void Shape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_custom_solver_bias", "bias"), &Shape3D::set_custom_solver_bias);
+ ClassDB::bind_method(D_METHOD("get_custom_solver_bias"), &Shape3D::get_custom_solver_bias);
+
ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape3D::set_margin);
ClassDB::bind_method(D_METHOD("get_margin"), &Shape3D::get_margin);
ClassDB::bind_method(D_METHOD("get_debug_mesh"), &Shape3D::get_debug_mesh);
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_solver_bias", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_custom_solver_bias", "get_custom_solver_bias");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin");
}
diff --git a/scene/resources/shape_3d.h b/scene/resources/shape_3d.h
index b8e529cd3c..77e79a269d 100644
--- a/scene/resources/shape_3d.h
+++ b/scene/resources/shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,6 +40,7 @@ class Shape3D : public Resource {
OBJ_SAVE_TYPE(Shape3D);
RES_BASE_EXTENSION("shape");
RID shape;
+ real_t custom_bias = 0.0;
real_t margin = 0.04;
Ref<ArrayMesh> debug_mesh_cache;
@@ -62,6 +63,9 @@ public:
void add_vertices_to_array(Vector<Vector3> &array, const Transform3D &p_xform);
+ void set_custom_solver_bias(real_t p_bias);
+ real_t get_custom_solver_bias() const;
+
real_t get_margin() const;
void set_margin(real_t p_margin);
diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp
index 7ac40b497d..885cf0f1b8 100644
--- a/scene/resources/skeleton_modification_2d.cpp
+++ b/scene/resources/skeleton_modification_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h
index aaddb9136e..3b9235ffd8 100644
--- a/scene/resources/skeleton_modification_2d.h
+++ b/scene/resources/skeleton_modification_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,7 +49,7 @@ class SkeletonModification2D : public Resource {
protected:
static void _bind_methods();
- SkeletonModificationStack2D *stack;
+ SkeletonModificationStack2D *stack = nullptr;
int execution_mode = 0; // 0 = process
bool enabled = true;
diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp
index bea42109cb..7adaf1452c 100644
--- a/scene/resources/skeleton_modification_2d_ccdik.cpp
+++ b/scene/resources/skeleton_modification_2d_ccdik.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/resources/skeleton_modification_2d_ccdik.h
index dc48291f62..31485fa31f 100644
--- a/scene/resources/skeleton_modification_2d_ccdik.h
+++ b/scene/resources/skeleton_modification_2d_ccdik.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp
index 3b5c555f89..393d404e9b 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_2d_fabrik.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h
index 79e0106e26..d5077084ef 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.h
+++ b/scene/resources/skeleton_modification_2d_fabrik.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp
index 31045455a3..0921417656 100644
--- a/scene/resources/skeleton_modification_2d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_2d_jiggle.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,9 @@
/*************************************************************************/
#include "skeleton_modification_2d_jiggle.h"
+
#include "scene/2d/skeleton_2d.h"
+#include "scene/resources/world_2d.h"
bool SkeletonModification2DJiggle::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;
diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h
index e24038a1db..a1abca6564 100644
--- a/scene/resources/skeleton_modification_2d_jiggle.h
+++ b/scene/resources/skeleton_modification_2d_jiggle.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp
index 740937fc44..23e1c579dc 100644
--- a/scene/resources/skeleton_modification_2d_lookat.cpp
+++ b/scene/resources/skeleton_modification_2d_lookat.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h
index 6aff30b826..ff91b92e7d 100644
--- a/scene/resources/skeleton_modification_2d_lookat.h
+++ b/scene/resources/skeleton_modification_2d_lookat.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_physicalbones.cpp b/scene/resources/skeleton_modification_2d_physicalbones.cpp
index 9dedb93f36..ddfd1d06b3 100644
--- a/scene/resources/skeleton_modification_2d_physicalbones.cpp
+++ b/scene/resources/skeleton_modification_2d_physicalbones.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h
index cdf6a5f570..373ff666ee 100644
--- a/scene/resources/skeleton_modification_2d_physicalbones.h
+++ b/scene/resources/skeleton_modification_2d_physicalbones.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -52,7 +52,7 @@ private:
bool _simulation_state_dirty = false;
TypedArray<StringName> _simulation_state_dirty_names;
- bool _simulation_state_dirty_process;
+ bool _simulation_state_dirty_process = false;
void _update_simulation_state();
protected:
diff --git a/scene/resources/skeleton_modification_2d_stackholder.cpp b/scene/resources/skeleton_modification_2d_stackholder.cpp
index 9436092cd9..9ec3434704 100644
--- a/scene/resources/skeleton_modification_2d_stackholder.cpp
+++ b/scene/resources/skeleton_modification_2d_stackholder.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h
index 9cc38e3942..99d9f6f381 100644
--- a/scene/resources/skeleton_modification_2d_stackholder.h
+++ b/scene/resources/skeleton_modification_2d_stackholder.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp
index 4f752896a9..b08fd82381 100644
--- a/scene/resources/skeleton_modification_2d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h
index c7e545a488..fc14d35f70 100644
--- a/scene/resources/skeleton_modification_2d_twoboneik.h
+++ b/scene/resources/skeleton_modification_2d_twoboneik.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
index b476952d86..2c0f6e779e 100644
--- a/scene/resources/skeleton_modification_3d.cpp
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,8 +34,9 @@
void SkeletonModification3D::_execute(real_t p_delta) {
GDVIRTUAL_CALL(_execute, p_delta);
- if (!enabled)
+ if (!enabled) {
return;
+ }
}
void SkeletonModification3D::_setup_modification(SkeletonModificationStack3D *p_stack) {
diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h
index fb1f3d33d1..ab736fcbd2 100644
--- a/scene/resources/skeleton_modification_3d.h
+++ b/scene/resources/skeleton_modification_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,7 +44,7 @@ class SkeletonModification3D : public Resource {
protected:
static void _bind_methods();
- SkeletonModificationStack3D *stack;
+ SkeletonModificationStack3D *stack = nullptr;
int execution_mode = 0; // 0 = process
bool enabled = true;
diff --git a/scene/resources/skeleton_modification_3d_ccdik.cpp b/scene/resources/skeleton_modification_3d_ccdik.cpp
index 6409022563..f19be47db2 100644
--- a/scene/resources/skeleton_modification_3d_ccdik.cpp
+++ b/scene/resources/skeleton_modification_3d_ccdik.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/resources/skeleton_modification_3d_ccdik.h
index e7537cc5b0..873ab8aa5a 100644
--- a/scene/resources/skeleton_modification_3d_ccdik.h
+++ b/scene/resources/skeleton_modification_3d_ccdik.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
index dedea3e282..b62dda3f4f 100644
--- a/scene/resources/skeleton_modification_3d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_3d_fabrik.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h
index 6c58b8a07a..cc4d3a5e20 100644
--- a/scene/resources/skeleton_modification_3d_fabrik.h
+++ b/scene/resources/skeleton_modification_3d_fabrik.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp
index 2535f2b987..3e36c241f7 100644
--- a/scene/resources/skeleton_modification_3d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_3d_jiggle.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -168,7 +168,7 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D
}
if (_print_execution_error(
jiggle_data_chain[p_joint_idx].bone_idx < 0 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(),
- "Jiggle joint " + itos(p_joint_idx) + " bone index is invald. Cannot execute modification!")) {
+ "Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification!")) {
return;
}
diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/resources/skeleton_modification_3d_jiggle.h
index c210c8fa73..7ec5ed4f11 100644
--- a/scene/resources/skeleton_modification_3d_jiggle.h
+++ b/scene/resources/skeleton_modification_3d_jiggle.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/resources/skeleton_modification_3d_lookat.cpp
index f3b0f41d60..3e8c1e3a77 100644
--- a/scene/resources/skeleton_modification_3d_lookat.cpp
+++ b/scene/resources/skeleton_modification_3d_lookat.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_lookat.h b/scene/resources/skeleton_modification_3d_lookat.h
index 5971e3f647..9f5120a0fd 100644
--- a/scene/resources/skeleton_modification_3d_lookat.h
+++ b/scene/resources/skeleton_modification_3d_lookat.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_stackholder.cpp b/scene/resources/skeleton_modification_3d_stackholder.cpp
index 56035a4def..fb2e80d217 100644
--- a/scene/resources/skeleton_modification_3d_stackholder.cpp
+++ b/scene/resources/skeleton_modification_3d_stackholder.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_stackholder.h b/scene/resources/skeleton_modification_3d_stackholder.h
index c765cd8de3..5780d7d43f 100644
--- a/scene/resources/skeleton_modification_3d_stackholder.h
+++ b/scene/resources/skeleton_modification_3d_stackholder.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp
index 93ec155a88..acc5ff716c 100644
--- a/scene/resources/skeleton_modification_3d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/resources/skeleton_modification_3d_twoboneik.h
index e62d6cc497..a4ddc6cee7 100644
--- a/scene/resources/skeleton_modification_3d_twoboneik.h
+++ b/scene/resources/skeleton_modification_3d_twoboneik.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp
index 25c3e9d2ef..38ec19828f 100644
--- a/scene/resources/skeleton_modification_stack_2d.cpp
+++ b/scene/resources/skeleton_modification_stack_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -172,7 +172,7 @@ void SkeletonModificationStack2D::add_modification(Ref<SkeletonModification2D> p
void SkeletonModificationStack2D::delete_modification(int p_mod_idx) {
ERR_FAIL_INDEX(p_mod_idx, modifications.size());
- modifications.remove(p_mod_idx);
+ modifications.remove_at(p_mod_idx);
#ifdef TOOLS_ENABLED
set_editor_gizmos_dirty(true);
@@ -263,7 +263,7 @@ void SkeletonModificationStack2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count");
}
SkeletonModificationStack2D::SkeletonModificationStack2D() {
diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h
index 58855701a1..7b5f8e0322 100644
--- a/scene/resources/skeleton_modification_stack_2d.h
+++ b/scene/resources/skeleton_modification_stack_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
index 301811f0b6..44fbfc934e 100644
--- a/scene/resources/skeleton_modification_stack_3d.cpp
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -133,7 +133,7 @@ void SkeletonModificationStack3D::add_modification(Ref<SkeletonModification3D> p
void SkeletonModificationStack3D::delete_modification(int p_mod_idx) {
const int modifications_size = modifications.size();
ERR_FAIL_INDEX(p_mod_idx, modifications_size);
- modifications.remove(p_mod_idx);
+ modifications.remove_at(p_mod_idx);
}
void SkeletonModificationStack3D::set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod) {
@@ -141,7 +141,7 @@ void SkeletonModificationStack3D::set_modification(int p_mod_idx, Ref<SkeletonMo
ERR_FAIL_INDEX(p_mod_idx, modifications_size);
if (p_mod == nullptr) {
- modifications.remove(p_mod_idx);
+ modifications.remove_at(p_mod_idx);
} else {
p_mod->_setup_modification(this);
modifications[p_mod_idx] = p_mod;
@@ -217,7 +217,7 @@ void SkeletonModificationStack3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count");
}
SkeletonModificationStack3D::SkeletonModificationStack3D() {
diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h
index cbc8d4e0b9..2c17fba2c5 100644
--- a/scene/resources/skeleton_modification_stack_3d.h
+++ b/scene/resources/skeleton_modification_stack_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp
index 15cdb86bab..54ed71999c 100644
--- a/scene/resources/skin.cpp
+++ b/scene/resources/skin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -143,6 +143,7 @@ void Skin::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bind_count"), &Skin::get_bind_count);
ClassDB::bind_method(D_METHOD("add_bind", "bone", "pose"), &Skin::add_bind);
+ ClassDB::bind_method(D_METHOD("add_named_bind", "name", "pose"), &Skin::add_named_bind);
ClassDB::bind_method(D_METHOD("set_bind_pose", "bind_index", "pose"), &Skin::set_bind_pose);
ClassDB::bind_method(D_METHOD("get_bind_pose", "bind_index"), &Skin::get_bind_pose);
diff --git a/scene/resources/skin.h b/scene/resources/skin.h
index 6857bf743a..6ade9dbed1 100644
--- a/scene/resources/skin.h
+++ b/scene/resources/skin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -67,23 +67,17 @@ public:
void set_bind_name(int p_index, const StringName &p_name);
inline int get_bind_bone(int p_index) const {
-#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(p_index, bind_count, -1);
-#endif
return binds_ptr[p_index].bone;
}
inline StringName get_bind_name(int p_index) const {
-#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(p_index, bind_count, StringName());
-#endif
return binds_ptr[p_index].name;
}
inline Transform3D get_bind_pose(int p_index) const {
-#ifdef DEBUG_ENABLED
ERR_FAIL_INDEX_V(p_index, bind_count, Transform3D());
-#endif
return binds_ptr[p_index].pose;
}
diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp
index 71424ba8ac..735134e27b 100644
--- a/scene/resources/sky.cpp
+++ b/scene/resources/sky.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -83,7 +83,7 @@ void Sky::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_material"), &Sky::get_material);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,PanoramaSkyMaterial,ProceduralSkyMaterial,PhysicalSkyMaterial"), "set_material", "get_material");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Automatic,HighQuality,HighQualityIncremental,RealTime"), "set_process_mode", "get_process_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Automatic,High-Quality,High-Quality Incremental,Real-Time"), "set_process_mode", "get_process_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "radiance_size", PROPERTY_HINT_ENUM, "32,64,128,256,512,1024,2048"), "set_radiance_size", "get_radiance_size");
BIND_ENUM_CONSTANT(RADIANCE_SIZE_32);
diff --git a/scene/resources/sky.h b/scene/resources/sky.h
index f0226d321d..5e52239032 100644
--- a/scene/resources/sky.h
+++ b/scene/resources/sky.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index de94c92cac..7874d77298 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,7 +37,7 @@ RID ProceduralSkyMaterial::shader;
void ProceduralSkyMaterial::set_sky_top_color(const Color &p_sky_top) {
sky_top_color = p_sky_top;
- RS::get_singleton()->material_set_param(_get_material(), "sky_top_color", sky_top_color.to_linear());
+ RS::get_singleton()->material_set_param(_get_material(), "sky_top_color", sky_top_color);
}
Color ProceduralSkyMaterial::get_sky_top_color() const {
@@ -46,7 +46,7 @@ Color ProceduralSkyMaterial::get_sky_top_color() const {
void ProceduralSkyMaterial::set_sky_horizon_color(const Color &p_sky_horizon) {
sky_horizon_color = p_sky_horizon;
- RS::get_singleton()->material_set_param(_get_material(), "sky_horizon_color", sky_horizon_color.to_linear());
+ RS::get_singleton()->material_set_param(_get_material(), "sky_horizon_color", sky_horizon_color);
}
Color ProceduralSkyMaterial::get_sky_horizon_color() const {
@@ -71,9 +71,28 @@ float ProceduralSkyMaterial::get_sky_energy() const {
return sky_energy;
}
+void ProceduralSkyMaterial::set_sky_cover(const Ref<Texture2D> &p_sky_cover) {
+ sky_cover = p_sky_cover;
+ RID tex_rid = p_sky_cover.is_valid() ? p_sky_cover->get_rid() : RID();
+ RS::get_singleton()->material_set_param(_get_material(), "sky_cover", tex_rid);
+}
+
+Ref<Texture2D> ProceduralSkyMaterial::get_sky_cover() const {
+ return sky_cover;
+}
+
+void ProceduralSkyMaterial::set_sky_cover_modulate(const Color &p_sky_cover_modulate) {
+ sky_cover_modulate = p_sky_cover_modulate;
+ RS::get_singleton()->material_set_param(_get_material(), "sky_cover_modulate", sky_cover_modulate);
+}
+
+Color ProceduralSkyMaterial::get_sky_cover_modulate() const {
+ return sky_cover_modulate;
+}
+
void ProceduralSkyMaterial::set_ground_bottom_color(const Color &p_ground_bottom) {
ground_bottom_color = p_ground_bottom;
- RS::get_singleton()->material_set_param(_get_material(), "ground_bottom_color", ground_bottom_color.to_linear());
+ RS::get_singleton()->material_set_param(_get_material(), "ground_bottom_color", ground_bottom_color);
}
Color ProceduralSkyMaterial::get_ground_bottom_color() const {
@@ -82,7 +101,7 @@ Color ProceduralSkyMaterial::get_ground_bottom_color() const {
void ProceduralSkyMaterial::set_ground_horizon_color(const Color &p_ground_horizon) {
ground_horizon_color = p_ground_horizon;
- RS::get_singleton()->material_set_param(_get_material(), "ground_horizon_color", ground_horizon_color.to_linear());
+ RS::get_singleton()->material_set_param(_get_material(), "ground_horizon_color", ground_horizon_color);
}
Color ProceduralSkyMaterial::get_ground_horizon_color() const {
@@ -125,6 +144,15 @@ float ProceduralSkyMaterial::get_sun_curve() const {
return sun_curve;
}
+void ProceduralSkyMaterial::set_dither_strength(float p_dither_strength) {
+ dither_strength = p_dither_strength;
+ RS::get_singleton()->material_set_param(_get_material(), "dither_strength", dither_strength);
+}
+
+float ProceduralSkyMaterial::get_dither_strength() const {
+ return dither_strength;
+}
+
Shader::Mode ProceduralSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
@@ -156,6 +184,12 @@ void ProceduralSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sky_energy", "energy"), &ProceduralSkyMaterial::set_sky_energy);
ClassDB::bind_method(D_METHOD("get_sky_energy"), &ProceduralSkyMaterial::get_sky_energy);
+ ClassDB::bind_method(D_METHOD("set_sky_cover", "sky_cover"), &ProceduralSkyMaterial::set_sky_cover);
+ ClassDB::bind_method(D_METHOD("get_sky_cover"), &ProceduralSkyMaterial::get_sky_cover);
+
+ ClassDB::bind_method(D_METHOD("set_sky_cover_modulate", "color"), &ProceduralSkyMaterial::set_sky_cover_modulate);
+ ClassDB::bind_method(D_METHOD("get_sky_cover_modulate"), &ProceduralSkyMaterial::get_sky_cover_modulate);
+
ClassDB::bind_method(D_METHOD("set_ground_bottom_color", "color"), &ProceduralSkyMaterial::set_ground_bottom_color);
ClassDB::bind_method(D_METHOD("get_ground_bottom_color"), &ProceduralSkyMaterial::get_ground_bottom_color);
@@ -174,11 +208,16 @@ void ProceduralSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sun_curve", "curve"), &ProceduralSkyMaterial::set_sun_curve);
ClassDB::bind_method(D_METHOD("get_sun_curve"), &ProceduralSkyMaterial::get_sun_curve);
+ ClassDB::bind_method(D_METHOD("set_dither_strength", "strength"), &ProceduralSkyMaterial::set_dither_strength);
+ ClassDB::bind_method(D_METHOD("get_dither_strength"), &ProceduralSkyMaterial::get_dither_strength);
+
ADD_GROUP("Sky", "sky_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_cover", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_sky_cover", "get_sky_cover");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_cover_modulate"), "set_sky_cover_modulate", "get_sky_cover_modulate");
ADD_GROUP("Ground", "ground_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color");
@@ -189,6 +228,9 @@ void ProceduralSkyMaterial::_bind_methods() {
ADD_GROUP("Sun", "sun_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_angle_max", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_angle_max", "get_sun_angle_max");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_curve", PROPERTY_HINT_EXP_EASING), "set_sun_curve", "get_sun_curve");
+
+ ADD_GROUP("", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dither_strength", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_dither_strength", "get_dither_strength");
}
void ProceduralSkyMaterial::cleanup_shader() {
@@ -208,16 +250,26 @@ void ProceduralSkyMaterial::_update_shader() {
shader_type sky;
-uniform vec4 sky_top_color : hint_color = vec4(0.35, 0.46, 0.71, 1.0);
-uniform vec4 sky_horizon_color : hint_color = vec4(0.55, 0.69, 0.81, 1.0);
-uniform float sky_curve : hint_range(0, 1) = 0.09;
+uniform vec4 sky_top_color : hint_color = vec4(0.385, 0.454, 0.55, 1.0);
+uniform vec4 sky_horizon_color : hint_color = vec4(0.646, 0.656, 0.67, 1.0);
+uniform float sky_curve : hint_range(0, 1) = 0.15;
uniform float sky_energy = 1.0;
-uniform vec4 ground_bottom_color : hint_color = vec4(0.12, 0.12, 0.13, 1.0);
-uniform vec4 ground_horizon_color : hint_color = vec4(0.37, 0.33, 0.31, 1.0);
+uniform sampler2D sky_cover : hint_black_albedo;
+uniform vec4 sky_cover_modulate : hint_color = vec4(1.0, 1.0, 1.0, 1.0);
+uniform vec4 ground_bottom_color : hint_color = vec4(0.2, 0.169, 0.133, 1.0);
+uniform vec4 ground_horizon_color : hint_color = vec4(0.646, 0.656, 0.67, 1.0);
uniform float ground_curve : hint_range(0, 1) = 0.02;
uniform float ground_energy = 1.0;
-uniform float sun_angle_max = 1.74;
-uniform float sun_curve : hint_range(0, 1) = 0.05;
+uniform float sun_angle_max = 30.0;
+uniform float sun_curve : hint_range(0, 1) = 0.15;
+uniform float dither_strength : hint_range(0, 10) = 1.0;
+
+// From: https://www.shadertoy.com/view/4sfGzS credit to iq
+float hash(vec3 p) {
+ p = fract( p * 0.3183099 + 0.1 );
+ p *= 17.0;
+ return fract(p.x * p.y * p.z * (p.x + p.y + p.z));
+}
void sky() {
float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0));
@@ -265,11 +317,17 @@ void sky() {
}
}
+ vec4 sky_cover_texture = texture(sky_cover, SKY_COORDS);
+ sky += (sky_cover_texture.rgb * sky_cover_modulate.rgb) * sky_cover_texture.a * sky_cover_modulate.a * sky_energy;
+
c = (v_angle - (PI * 0.5)) / (PI * 0.5);
vec3 ground = mix(ground_horizon_color.rgb, ground_bottom_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / ground_curve), 0.0, 1.0));
ground *= ground_energy;
COLOR = mix(ground, sky, step(0.0, EYEDIR.y));
+
+ // Make optional, eliminates banding.
+ COLOR += (hash(EYEDIR * 1741.9782) * 0.08 - 0.04) * 0.016 * dither_strength;
}
)");
}
@@ -277,22 +335,23 @@ void sky() {
}
ProceduralSkyMaterial::ProceduralSkyMaterial() {
- set_sky_top_color(Color(0.35, 0.46, 0.71));
- set_sky_horizon_color(Color(0.55, 0.69, 0.81));
- set_sky_curve(0.09);
+ set_sky_top_color(Color(0.385, 0.454, 0.55));
+ set_sky_horizon_color(Color(0.6463, 0.6558, 0.6708));
+ set_sky_curve(0.15);
set_sky_energy(1.0);
+ set_sky_cover_modulate(Color(1, 1, 1));
- set_ground_bottom_color(Color(0.12, 0.12, 0.13));
- set_ground_horizon_color(Color(0.37, 0.33, 0.31));
+ set_ground_bottom_color(Color(0.2, 0.169, 0.133));
+ set_ground_horizon_color(Color(0.6463, 0.6558, 0.6708));
set_ground_curve(0.02);
set_ground_energy(1.0);
- set_sun_angle_max(100.0);
- set_sun_curve(0.05);
+ set_sun_angle_max(30.0);
+ set_sun_curve(0.15);
+ set_dither_strength(1.0);
}
ProceduralSkyMaterial::~ProceduralSkyMaterial() {
- RS::get_singleton()->material_set_shader(_get_material(), RID());
}
/////////////////////////////////////////
@@ -308,14 +367,30 @@ Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const {
return panorama;
}
+void PanoramaSkyMaterial::set_filtering_enabled(bool p_enabled) {
+ filter = p_enabled;
+ notify_property_list_changed();
+ _update_shader();
+ // Only set if shader already compiled
+ if (shader_set) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(filter)]);
+ }
+}
+
+bool PanoramaSkyMaterial::is_filtering_enabled() const {
+ return filter;
+}
+
Shader::Mode PanoramaSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
RID PanoramaSkyMaterial::get_rid() const {
_update_shader();
+ // Don't compile shaders until first use, then compile both
if (!shader_set) {
- RS::get_singleton()->material_set_shader(_get_material(), shader);
+ RS::get_singleton()->material_set_shader(_get_material(), shader_cache[1 - int(filter)]);
+ RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(filter)]);
shader_set = true;
}
return _get_material();
@@ -323,42 +398,50 @@ RID PanoramaSkyMaterial::get_rid() const {
RID PanoramaSkyMaterial::get_shader_rid() const {
_update_shader();
- return shader;
+ return shader_cache[int(filter)];
}
void PanoramaSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_panorama", "texture"), &PanoramaSkyMaterial::set_panorama);
ClassDB::bind_method(D_METHOD("get_panorama"), &PanoramaSkyMaterial::get_panorama);
+ ClassDB::bind_method(D_METHOD("set_filtering_enabled", "enabled"), &PanoramaSkyMaterial::set_filtering_enabled);
+ ClassDB::bind_method(D_METHOD("is_filtering_enabled"), &PanoramaSkyMaterial::is_filtering_enabled);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "panorama", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_panorama", "get_panorama");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter"), "set_filtering_enabled", "is_filtering_enabled");
}
Mutex PanoramaSkyMaterial::shader_mutex;
-RID PanoramaSkyMaterial::shader;
+RID PanoramaSkyMaterial::shader_cache[2];
void PanoramaSkyMaterial::cleanup_shader() {
- if (shader.is_valid()) {
- RS::get_singleton()->free(shader);
+ if (shader_cache[0].is_valid()) {
+ RS::get_singleton()->free(shader_cache[0]);
+ RS::get_singleton()->free(shader_cache[1]);
}
}
void PanoramaSkyMaterial::_update_shader() {
shader_mutex.lock();
- if (shader.is_null()) {
- shader = RS::get_singleton()->shader_create();
+ if (shader_cache[0].is_null()) {
+ for (int i = 0; i < 2; i++) {
+ shader_cache[i] = RS::get_singleton()->shader_create();
- // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
- RS::get_singleton()->shader_set_code(shader, R"(
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ RS::get_singleton()->shader_set_code(shader_cache[i], vformat(R"(
// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PanoramaSkyMaterial.
shader_type sky;
-uniform sampler2D source_panorama : filter_linear, hint_albedo;
+uniform sampler2D source_panorama : %s, hint_black_albedo;
void sky() {
COLOR = texture(source_panorama, SKY_COORDS).rgb;
}
-)");
+)",
+ i ? "filter_linear" : "filter_nearest"));
+ }
}
shader_mutex.unlock();
@@ -368,7 +451,6 @@ PanoramaSkyMaterial::PanoramaSkyMaterial() {
}
PanoramaSkyMaterial::~PanoramaSkyMaterial() {
- RS::get_singleton()->material_set_shader(_get_material(), RID());
}
//////////////////////////////////
@@ -564,14 +646,14 @@ void PhysicalSkyMaterial::_update_shader() {
shader_type sky;
uniform float rayleigh : hint_range(0, 64) = 2.0;
-uniform vec4 rayleigh_color : hint_color = vec4(0.056, 0.14, 0.3, 1.0);
+uniform vec4 rayleigh_color : hint_color = vec4(0.3, 0.405, 0.6, 1.0);
uniform float mie : hint_range(0, 1) = 0.005;
uniform float mie_eccentricity : hint_range(-1, 1) = 0.8;
-uniform vec4 mie_color : hint_color = vec4(0.36, 0.56, 0.82, 1.0);
+uniform vec4 mie_color : hint_color = vec4(0.69, 0.729, 0.812, 1.0);
uniform float turbidity : hint_range(0, 1000) = 10.0;
uniform float sun_disk_scale : hint_range(0, 360) = 1.0;
-uniform vec4 ground_color : hint_color = vec4(1.0);
+uniform vec4 ground_color : hint_color = vec4(0.1, 0.07, 0.034, 1.0);
uniform float exposure : hint_range(0, 128) = 0.1;
uniform float dither_strength : hint_range(0, 10) = 1.0;
@@ -661,13 +743,13 @@ void sky() {
PhysicalSkyMaterial::PhysicalSkyMaterial() {
set_rayleigh_coefficient(2.0);
- set_rayleigh_color(Color(0.056, 0.14, 0.3));
+ set_rayleigh_color(Color(0.3, 0.405, 0.6));
set_mie_coefficient(0.005);
set_mie_eccentricity(0.8);
- set_mie_color(Color(0.36, 0.56, 0.82));
+ set_mie_color(Color(0.69, 0.729, 0.812));
set_turbidity(10.0);
set_sun_disk_scale(1.0);
- set_ground_color(Color(1.0, 1.0, 1.0));
+ set_ground_color(Color(0.1, 0.07, 0.034));
set_exposure(0.1);
set_dither_strength(1.0);
}
diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h
index daeda212d4..8163a42519 100644
--- a/scene/resources/sky_material.h
+++ b/scene/resources/sky_material.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,16 +40,19 @@ class ProceduralSkyMaterial : public Material {
private:
Color sky_top_color;
Color sky_horizon_color;
- float sky_curve;
- float sky_energy;
+ float sky_curve = 0.0f;
+ float sky_energy = 0.0f;
+ Ref<Texture2D> sky_cover;
+ Color sky_cover_modulate;
Color ground_bottom_color;
Color ground_horizon_color;
- float ground_curve;
- float ground_energy;
+ float ground_curve = 0.0f;
+ float ground_energy = 0.0f;
- float sun_angle_max;
- float sun_curve;
+ float sun_angle_max = 0.0f;
+ float sun_curve = 0.0f;
+ float dither_strength = 0.0f;
static Mutex shader_mutex;
static RID shader;
@@ -72,6 +75,12 @@ public:
void set_sky_energy(float p_energy);
float get_sky_energy() const;
+ void set_sky_cover(const Ref<Texture2D> &p_sky_cover);
+ Ref<Texture2D> get_sky_cover() const;
+
+ void set_sky_cover_modulate(const Color &p_sky_cover_modulate);
+ Color get_sky_cover_modulate() const;
+
void set_ground_bottom_color(const Color &p_ground_bottom);
Color get_ground_bottom_color() const;
@@ -90,6 +99,9 @@ public:
void set_sun_curve(float p_curve);
float get_sun_curve() const;
+ void set_dither_strength(float p_dither_strength);
+ float get_dither_strength() const;
+
virtual Shader::Mode get_shader_mode() const override;
virtual RID get_shader_rid() const override;
virtual RID get_rid() const override;
@@ -110,10 +122,12 @@ private:
Ref<Texture2D> panorama;
static Mutex shader_mutex;
- static RID shader;
+ static RID shader_cache[2];
static void _update_shader();
mutable bool shader_set = false;
+ bool filter = true;
+
protected:
static void _bind_methods();
@@ -121,6 +135,9 @@ public:
void set_panorama(const Ref<Texture2D> &p_panorama);
Ref<Texture2D> get_panorama() const;
+ void set_filtering_enabled(bool p_enabled);
+ bool is_filtering_enabled() const;
+
virtual Shader::Mode get_shader_mode() const override;
virtual RID get_shader_rid() const override;
virtual RID get_rid() const override;
@@ -141,16 +158,16 @@ private:
static Mutex shader_mutex;
static RID shader;
- float rayleigh;
+ float rayleigh = 0.0f;
Color rayleigh_color;
- float mie;
- float mie_eccentricity;
+ float mie = 0.0f;
+ float mie_eccentricity = 0.0f;
Color mie_color;
- float turbidity;
- float sun_disk_scale;
+ float turbidity = 0.0f;
+ float sun_disk_scale = 0.0f;
Color ground_color;
- float exposure;
- float dither_strength;
+ float exposure = 0.0f;
+ float dither_strength = 0.0f;
Ref<Texture2D> night_sky;
static void _update_shader();
mutable bool shader_set = false;
diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp
index e4b4398063..8de0dc1650 100644
--- a/scene/resources/sphere_shape_3d.cpp
+++ b/scene/resources/sphere_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "sphere_shape_3d.h"
+
#include "servers/physics_server_3d.h"
Vector<Vector3> SphereShape3D::get_debug_mesh_lines() const {
@@ -63,6 +64,7 @@ void SphereShape3D::_update_shape() {
}
void SphereShape3D::set_radius(float p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0, "SphereShape3D radius cannot be negative.");
radius = p_radius;
_update_shape();
notify_change_to_owners();
@@ -76,7 +78,7 @@ void SphereShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereShape3D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &SphereShape3D::get_radius);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_radius", "get_radius");
}
SphereShape3D::SphereShape3D() :
diff --git a/scene/resources/sphere_shape_3d.h b/scene/resources/sphere_shape_3d.h
index eddd2a2132..20887dd092 100644
--- a/scene/resources/sphere_shape_3d.h
+++ b/scene/resources/sphere_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,7 @@
class SphereShape3D : public Shape3D {
GDCLASS(SphereShape3D, Shape3D);
- float radius;
+ float radius = 1.0f;
protected:
static void _bind_methods();
diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp
index 5524d59dc7..ba21b9fd17 100644
--- a/scene/resources/sprite_frames.cpp
+++ b/scene/resources/sprite_frames.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,38 +33,38 @@
#include "scene/scene_string_names.h"
void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos) {
- Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::Iterator E = animations.find(p_anim);
ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
- if (p_at_pos >= 0 && p_at_pos < E->get().frames.size()) {
- E->get().frames.insert(p_at_pos, p_frame);
+ if (p_at_pos >= 0 && p_at_pos < E->value.frames.size()) {
+ E->value.frames.insert(p_at_pos, p_frame);
} else {
- E->get().frames.push_back(p_frame);
+ E->value.frames.push_back(p_frame);
}
emit_changed();
}
int SpriteFrames::get_frame_count(const StringName &p_anim) const {
- const Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim);
ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
- return E->get().frames.size();
+ return E->value.frames.size();
}
void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) {
- Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::Iterator E = animations.find(p_anim);
ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
- E->get().frames.remove(p_idx);
+ E->value.frames.remove_at(p_idx);
emit_changed();
}
void SpriteFrames::clear(const StringName &p_anim) {
- Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::Iterator E = animations.find(p_anim);
ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
- E->get().frames.clear();
+ E->value.frames.clear();
emit_changed();
}
@@ -124,37 +124,37 @@ Vector<String> SpriteFrames::get_animation_names() const {
void SpriteFrames::set_animation_speed(const StringName &p_anim, double p_fps) {
ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ").");
- Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::Iterator E = animations.find(p_anim);
ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
- E->get().speed = p_fps;
+ E->value.speed = p_fps;
}
double SpriteFrames::get_animation_speed(const StringName &p_anim) const {
- const Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim);
ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
- return E->get().speed;
+ return E->value.speed;
}
void SpriteFrames::set_animation_loop(const StringName &p_anim, bool p_loop) {
- Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::Iterator E = animations.find(p_anim);
ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
- E->get().loop = p_loop;
+ E->value.loop = p_loop;
}
bool SpriteFrames::get_animation_loop(const StringName &p_anim) const {
- const Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim);
ERR_FAIL_COND_V_MSG(!E, false, "Animation '" + String(p_anim) + "' doesn't exist.");
- return E->get().loop;
+ return E->value.loop;
}
void SpriteFrames::_set_frames(const Array &p_frames) {
clear_all();
- Map<StringName, Anim>::Element *E = animations.find(SceneStringNames::get_singleton()->_default);
+ HashMap<StringName, Anim>::Iterator E = animations.find(SceneStringNames::get_singleton()->_default);
ERR_FAIL_COND(!E);
- E->get().frames.resize(p_frames.size());
- for (int i = 0; i < E->get().frames.size(); i++) {
- E->get().frames.write[i] = p_frames[i];
+ E->value.frames.resize(p_frames.size());
+ for (int i = 0; i < E->value.frames.size(); i++) {
+ E->value.frames.write[i] = p_frames[i];
}
}
@@ -195,7 +195,7 @@ void SpriteFrames::_set_animations(const Array &p_animations) {
anim.loop = d["loop"];
Array frames = d["frames"];
for (int j = 0; j < frames.size(); j++) {
- RES res = frames[j];
+ Ref<Resource> res = frames[j];
anim.frames.push_back(res);
}
diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h
index fdfd6af5ab..e32ccc1336 100644
--- a/scene/resources/sprite_frames.h
+++ b/scene/resources/sprite_frames.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,7 +42,7 @@ class SpriteFrames : public Resource {
Vector<Ref<Texture2D>> frames;
};
- Map<StringName, Anim> animations;
+ HashMap<StringName, Anim> animations;
Array _get_frames() const;
void _set_frames(const Array &p_frames);
@@ -73,24 +73,24 @@ public:
void add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos = -1);
int get_frame_count(const StringName &p_anim) const;
_FORCE_INLINE_ Ref<Texture2D> get_frame(const StringName &p_anim, int p_idx) const {
- const Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim);
ERR_FAIL_COND_V_MSG(!E, Ref<Texture2D>(), "Animation '" + String(p_anim) + "' doesn't exist.");
ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>());
- if (p_idx >= E->get().frames.size()) {
+ if (p_idx >= E->value.frames.size()) {
return Ref<Texture2D>();
}
- return E->get().frames[p_idx];
+ return E->value.frames[p_idx];
}
void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_frame) {
- Map<StringName, Anim>::Element *E = animations.find(p_anim);
+ HashMap<StringName, Anim>::Iterator E = animations.find(p_anim);
ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
ERR_FAIL_COND(p_idx < 0);
- if (p_idx >= E->get().frames.size()) {
+ if (p_idx >= E->value.frames.size()) {
return;
}
- E->get().frames.write[p_idx] = p_frame;
+ E->value.frames.write[p_idx] = p_frame;
}
void remove_frame(const StringName &p_anim, int p_idx);
void clear(const StringName &p_anim);
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index b960944d99..f3cb2b9ea7 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,10 +34,28 @@
#include <limits.h>
+float StyleBox::get_style_margin(Side p_side) const {
+ float ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_style_margin, p_side, ret)) {
+ return ret;
+ }
+ return 0;
+}
bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const {
+ bool ret;
+ if (GDVIRTUAL_CALL(_test_mask, p_point, p_rect, ret)) {
+ return ret;
+ }
+
return true;
}
+void StyleBox::draw(RID p_canvas_item, const Rect2 &p_rect) const {
+ if (GDVIRTUAL_REQUIRED_CALL(_draw, p_canvas_item, p_rect)) {
+ return;
+ }
+}
+
void StyleBox::set_default_margin(Side p_side, float p_value) {
ERR_FAIL_INDEX((int)p_side, 4);
@@ -74,10 +92,19 @@ Point2 StyleBox::get_offset() const {
}
Size2 StyleBox::get_center_size() const {
+ Size2 ret;
+ if (GDVIRTUAL_CALL(_get_center_size, ret)) {
+ return ret;
+ }
+
return Size2();
}
Rect2 StyleBox::get_draw_rect(const Rect2 &p_rect) const {
+ Rect2 ret;
+ if (GDVIRTUAL_CALL(_get_draw_rect, p_rect, ret)) {
+ return ret;
+ }
return p_rect;
}
@@ -97,9 +124,15 @@ void StyleBox::_bind_methods() {
ADD_GROUP("Content Margin", "content_margin_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_left", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_top", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_bottom", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_BOTTOM);
+
+ GDVIRTUAL_BIND(_get_style_margin, "side")
+ GDVIRTUAL_BIND(_test_mask, "point", "rect")
+ GDVIRTUAL_BIND(_get_center_size)
+ GDVIRTUAL_BIND(_get_draw_rect, "rect")
+ GDVIRTUAL_BIND(_draw, "to_canvas_item", "rect")
}
StyleBox::StyleBox() {
@@ -283,19 +316,23 @@ void StyleBoxTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
+
ADD_GROUP("Margin", "margin_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_BOTTOM);
+
ADD_GROUP("Expand Margin", "expand_margin_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_BOTTOM);
+
ADD_GROUP("Axis Stretch", "axis_stretch_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
+
ADD_GROUP("Modulate", "modulate_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate_color"), "set_modulate", "get_modulate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
@@ -424,6 +461,15 @@ bool StyleBoxFlat::is_draw_center_enabled() const {
return draw_center;
}
+void StyleBoxFlat::set_skew(Vector2 p_skew) {
+ skew = p_skew;
+ emit_changed();
+}
+
+Vector2 StyleBoxFlat::get_skew() const {
+ return skew;
+}
+
void StyleBoxFlat::set_shadow_color(const Color &p_color) {
shadow_color = p_color;
emit_changed();
@@ -509,7 +555,7 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re
}
inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4],
- const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const bool fill_center = false) {
+ const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool fill_center = false) {
int vert_offset = verts.size();
if (!vert_offset) {
vert_offset = 0;
@@ -521,21 +567,22 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius);
// Corner radius center points.
- Vector<Point2> outer_points;
- outer_points.push_back(ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0])); //tl
- outer_points.push_back(Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1])); //tr
- outer_points.push_back(ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2])); //br
- outer_points.push_back(Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3])); //bl
+ Vector<Point2> outer_points = {
+ ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0]), //tl
+ Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1]), //tr
+ ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2]), //br
+ Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3]) //bl
+ };
real_t inner_corner_radius[4];
set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius);
- Vector<Point2> inner_points;
- inner_points.push_back(inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0])); //tl
- inner_points.push_back(Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1])); //tr
- inner_points.push_back(inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2])); //br
- inner_points.push_back(Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3])); //bl
-
+ Vector<Point2> inner_points = {
+ inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0]), //tl
+ Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1]), //tr
+ inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2]), //br
+ Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3]) //bl
+ };
// Calculate the vertices.
for (int corner_index = 0; corner_index < 4; corner_index++) {
for (int detail = 0; detail <= adapted_corner_detail; detail++) {
@@ -552,9 +599,12 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
color = outer_color;
corner_point = outer_points[corner_index];
}
- real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x;
- real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y;
- verts.push_back(Vector2(x, y));
+
+ const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x;
+ const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y;
+ const float x_skew = -skew.x * (y - ring_rect.get_center().y);
+ const float y_skew = -skew.y * (x - ring_rect.get_center().x);
+ verts.push_back(Vector2(x + x_skew, y + y_skew));
colors.push_back(color);
}
}
@@ -632,10 +682,12 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
return;
}
- bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
- bool aa_on = rounded_corners && anti_aliased;
+ const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
+ // Only enable antialiasing if it is actually needed. This improve performances
+ // and maximizes sharpness for non-skewed StyleBoxes with sharp corners.
+ const bool aa_on = (rounded_corners || !skew.is_equal_approx(Vector2())) && anti_aliased;
- bool blend_on = blend_border && draw_border;
+ const bool blend_on = blend_border && draw_border;
Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0);
Color border_color_blend = (draw_center ? bg_color : border_color_alpha);
@@ -682,24 +734,24 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0);
draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner,
- shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail);
+ shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail, skew);
if (draw_center) {
draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner,
- shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, true);
+ shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, skew, true);
}
}
// Create border (no AA).
if (draw_border && !aa_on) {
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- border_style_rect, infill_rect, border_color_inner, border_color, corner_detail);
+ border_style_rect, infill_rect, border_color_inner, border_color, corner_detail, skew);
}
// Create infill (no AA).
if (draw_center && (!aa_on || blend_on || !draw_border)) {
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- infill_rect, infill_rect, bg_color, bg_color, corner_detail, true);
+ infill_rect, infill_rect, bg_color, bg_color, corner_detail, skew, true);
}
if (aa_on) {
@@ -731,7 +783,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
// Create infill within AA border.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- infill_inner_rect_aa, infill_inner_rect_aa, bg_color, bg_color, corner_detail, true);
+ infill_inner_rect_aa, infill_inner_rect_aa, bg_color, bg_color, corner_detail, skew, true);
}
if (!blend_on || !draw_border) {
@@ -742,7 +794,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
// Create infill fake AA gradient.
draw_ring(verts, indices, colors, style_rect, adapted_corner,
- infill_rect_aa, infill_rect, bg_color, alpha_bg, corner_detail);
+ infill_rect_aa, infill_rect, bg_color, alpha_bg, corner_detail, skew);
}
}
@@ -756,17 +808,17 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
// Create border.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- border_style_rect_aa, ((blend_on) ? infill_rect : infill_rect_aa), border_color_inner, border_color, corner_detail);
+ border_style_rect_aa, ((blend_on) ? infill_rect : infill_rect_aa), border_color_inner, border_color, corner_detail, skew);
if (!blend_on) {
// Create inner border fake AA gradient.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- infill_rect_aa, infill_rect, border_color_blend, border_color, corner_detail);
+ infill_rect_aa, infill_rect, border_color_blend, border_color, corner_detail, skew);
}
// Create outer border fake AA gradient.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- style_rect_aa, border_style_rect_aa, border_color, border_color_alpha, corner_detail);
+ style_rect_aa, border_style_rect_aa, border_color, border_color_alpha, corner_detail, skew);
}
}
@@ -824,6 +876,9 @@ void StyleBoxFlat::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &StyleBoxFlat::set_draw_center);
ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxFlat::is_draw_center_enabled);
+ ClassDB::bind_method(D_METHOD("set_skew", "skew"), &StyleBoxFlat::set_skew);
+ ClassDB::bind_method(D_METHOD("get_skew"), &StyleBoxFlat::get_skew);
+
ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &StyleBoxFlat::set_shadow_color);
ClassDB::bind_method(D_METHOD("get_shadow_color"), &StyleBoxFlat::get_shadow_color);
@@ -845,6 +900,7 @@ void StyleBoxFlat::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "bg_color"), "set_bg_color", "get_bg_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew");
ADD_GROUP("Border Width", "border_width_");
ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_LEFT);
@@ -867,8 +923,8 @@ void StyleBoxFlat::_bind_methods() {
ADD_GROUP("Expand Margin", "expand_margin_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
ADD_GROUP("Shadow", "shadow_");
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index a6cd5b7fb7..3b3654775f 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,9 +44,15 @@ class StyleBox : public Resource {
float margin[4];
protected:
- virtual float get_style_margin(Side p_side) const = 0;
+ virtual float get_style_margin(Side p_side) const;
static void _bind_methods();
+ GDVIRTUAL1RC(float, _get_style_margin, Side)
+ GDVIRTUAL2RC(bool, _test_mask, Point2, Rect2)
+ GDVIRTUAL0RC(Size2, _get_center_size)
+ GDVIRTUAL1RC(Rect2, _get_draw_rect, Rect2)
+ GDVIRTUAL2C(_draw, RID, Rect2)
+
public:
virtual bool test_mask(const Point2 &p_point, const Rect2 &p_rect) const;
@@ -56,7 +62,7 @@ public:
virtual Size2 get_center_size() const;
virtual Rect2 get_draw_rect(const Rect2 &p_rect) const;
- virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const = 0;
+ virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const;
CanvasItem *get_current_item_drawn() const;
@@ -149,6 +155,7 @@ class StyleBoxFlat : public StyleBox {
bool draw_center = true;
bool blend_border = false;
+ Vector2 skew;
bool anti_aliased = true;
int corner_detail = 8;
@@ -194,6 +201,9 @@ public:
void set_draw_center(bool p_enabled);
bool is_draw_center_enabled() const;
+ void set_skew(Vector2 p_skew);
+ Vector2 get_skew() const;
+
void set_shadow_color(const Color &p_color);
Color get_shadow_color() const;
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 455af8a40c..0e41eed570 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,6 @@
#include "surface_tool.h"
-#define _VERTEX_SNAP 0.0001
#define EQ_VERTEX_DIST 0.00001
SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr;
@@ -38,6 +37,44 @@ SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr;
SurfaceTool::SimplifyWithAttribFunc SurfaceTool::simplify_with_attrib_func = nullptr;
SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr;
SurfaceTool::SimplifySloppyFunc SurfaceTool::simplify_sloppy_func = nullptr;
+SurfaceTool::GenerateRemapFunc SurfaceTool::generate_remap_func = nullptr;
+SurfaceTool::RemapVertexFunc SurfaceTool::remap_vertex_func = nullptr;
+SurfaceTool::RemapIndexFunc SurfaceTool::remap_index_func = nullptr;
+
+void SurfaceTool::strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ ERR_FAIL_COND_MSG(!generate_remap_func || !remap_vertex_func || !remap_index_func, "Meshoptimizer library is not initialized.");
+
+ Vector<uint32_t> remap;
+ remap.resize(r_vertices.size());
+ uint32_t new_vertex_count = generate_remap_func(remap.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), (float *)r_vertices.ptr(), r_vertices.size(), sizeof(Vector3));
+ remap_vertex_func(r_vertices.ptrw(), r_vertices.ptr(), r_vertices.size(), sizeof(Vector3), remap.ptr());
+ r_vertices.resize(new_vertex_count);
+ remap_index_func((unsigned int *)r_indices.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), remap.ptr());
+
+ HashMap<const int *, bool, TriangleHasher, TriangleHasher> found_triangles;
+ int *idx_ptr = r_indices.ptrw();
+
+ int filtered_indices_count = 0;
+ for (int i = 0; i < r_indices.size() / 3; i++) {
+ const int *tri = idx_ptr + (i * 3);
+
+ if (tri[0] == tri[1] || tri[1] == tri[2] || tri[2] == tri[0]) {
+ continue;
+ }
+
+ if (found_triangles.has(tri)) {
+ continue;
+ }
+
+ if (i != filtered_indices_count) {
+ memcpy(idx_ptr + (filtered_indices_count * 3), tri, sizeof(int) * 3);
+ }
+
+ found_triangles[tri] = true;
+ filtered_indices_count++;
+ }
+ r_indices.resize(filtered_indices_count * 3);
+}
bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const {
if (vertex != p_vertex.vertex) {
@@ -108,6 +145,56 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) {
return h;
}
+uint32_t SurfaceTool::TriangleHasher::hash(const int *p_triangle) {
+ int t0 = p_triangle[0];
+ int t1 = p_triangle[1];
+ int t2 = p_triangle[2];
+
+ if (t0 > t1) {
+ SWAP(t0, t1);
+ }
+ if (t1 > t2) {
+ SWAP(t1, t2);
+ }
+ if (t0 > t1) {
+ SWAP(t0, t1);
+ }
+
+ return (t0 * 73856093) ^ (t1 * 19349663) ^ (t2 * 83492791);
+}
+
+bool SurfaceTool::TriangleHasher::compare(const int *p_lhs, const int *p_rhs) {
+ int r0 = p_rhs[0];
+ int r1 = p_rhs[1];
+ int r2 = p_rhs[2];
+
+ if (r0 > r1) {
+ SWAP(r0, r1);
+ }
+ if (r1 > r2) {
+ SWAP(r1, r2);
+ }
+ if (r0 > r1) {
+ SWAP(r0, r1);
+ }
+
+ int l0 = p_lhs[0];
+ int l1 = p_lhs[1];
+ int l2 = p_lhs[2];
+
+ if (l0 > l1) {
+ SWAP(l0, l1);
+ }
+ if (l1 > l2) {
+ SWAP(l1, l2);
+ }
+ if (l0 > l1) {
+ SWAP(l0, l1);
+ }
+
+ return l0 == r0 && l1 == r1 && l2 == r2;
+}
+
void SurfaceTool::begin(Mesh::PrimitiveType p_primitive) {
clear();
@@ -1078,7 +1165,7 @@ void SurfaceTool::generate_normals(bool p_flip) {
for (int i = 0; i < 3; i++) {
Vector3 *lv = vertex_hash.getptr(v[i]);
if (!lv) {
- vertex_hash.set(v[i], normal);
+ vertex_hash.insert(v[i], normal);
} else {
(*lv) += normal;
}
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index bde6702759..bf4332ad2a 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -84,12 +84,24 @@ public:
static SimplifyScaleFunc simplify_scale_func;
typedef size_t (*SimplifySloppyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *out_result_error);
static SimplifySloppyFunc simplify_sloppy_func;
+ typedef size_t (*GenerateRemapFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const void *vertices, size_t vertex_count, size_t vertex_size);
+ static GenerateRemapFunc generate_remap_func;
+ typedef void (*RemapVertexFunc)(void *destination, const void *vertices, size_t vertex_count, size_t vertex_size, const unsigned int *remap);
+ static RemapVertexFunc remap_vertex_func;
+ typedef void (*RemapIndexFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const unsigned int *remap);
+ static RemapIndexFunc remap_index_func;
+ static void strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
private:
struct VertexHasher {
static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx);
};
+ struct TriangleHasher {
+ static _FORCE_INLINE_ uint32_t hash(const int *p_triangle);
+ static _FORCE_INLINE_ bool compare(const int *p_lhs, const int *p_rhs);
+ };
+
struct WeightSort {
int index = 0;
float weight = 0.0;
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index 52a3abf74d..f1eddd8ffc 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -116,14 +116,6 @@ void SyntaxHighlighter::_bind_methods() {
////////////////////////////////////////////////////////////////////////////////
-static bool _is_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-
-static bool _is_hex_symbol(char32_t c) {
- return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
Dictionary color_map;
@@ -166,17 +158,20 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
color = font_color;
bool is_char = !is_symbol(str[j]);
bool is_a_symbol = is_symbol(str[j]);
- bool is_number = (str[j] >= '0' && str[j] <= '9');
+ bool is_number = is_digit(str[j]);
/* color regions */
if (is_a_symbol || in_region != -1) {
int from = j;
- for (; from < line_length; from++) {
- if (str[from] == '\\') {
- from++;
- continue;
+
+ if (in_region == -1) {
+ for (; from < line_length; from++) {
+ if (str[from] == '\\') {
+ from++;
+ continue;
+ }
+ break;
}
- break;
}
if (from != line_length) {
@@ -208,6 +203,12 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
/* check if it's the whole line */
if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
+ if (from + end_key_length > line_length && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
+ // If it's key length and there is a '\', dont skip to highlight esc chars.
+ if (str.find("\\", from) >= 0) {
+ break;
+ }
+ }
prev_color = color_regions[in_region].color;
highlighter_info["color"] = color_regions[c].color;
color_map[j] = highlighter_info;
@@ -227,13 +228,23 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
/* if we are in one find the end key */
if (in_region != -1) {
+ bool is_string = (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'");
+
+ Color region_color = color_regions[in_region].color;
+ prev_color = region_color;
+ highlighter_info["color"] = region_color;
+ color_map[j] = highlighter_info;
+
/* search the line */
int region_end_index = -1;
int end_key_length = color_regions[in_region].end_key.length();
const char32_t *end_key = color_regions[in_region].end_key.get_data();
for (; from < line_length; from++) {
if (line_length - from < end_key_length) {
- break;
+ // Don't break if '\' to highlight esc chars.
+ if (!is_string || str.find("\\", from) < 0) {
+ break;
+ }
}
if (!is_symbol(str[from])) {
@@ -241,7 +252,20 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
if (str[from] == '\\') {
+ if (is_string) {
+ Dictionary escape_char_highlighter_info;
+ escape_char_highlighter_info["color"] = symbol_color;
+ color_map[from] = escape_char_highlighter_info;
+ }
+
from++;
+
+ if (is_string) {
+ Dictionary region_continue_highlighter_info;
+ prev_color = region_color;
+ region_continue_highlighter_info["color"] = region_color;
+ color_map[from + 1] = region_continue_highlighter_info;
+ }
continue;
}
@@ -258,10 +282,6 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
}
- prev_color = color_regions[in_region].color;
- highlighter_info["color"] = color_regions[in_region].color;
- color_map[j] = highlighter_info;
-
j = from + (end_key_length - 1);
if (region_end_index == -1) {
color_region_cache[p_line] = in_region;
@@ -276,7 +296,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
// Allow ABCDEF in hex notation.
- if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ if (is_hex_notation && (is_hex_digit(str[j]) || is_number)) {
is_number = true;
} else {
is_hex_notation = false;
@@ -293,7 +313,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
}
- if (!in_word && _is_char(str[j]) && !is_number) {
+ if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !is_number) {
in_word = true;
}
@@ -493,7 +513,7 @@ void CodeHighlighter::add_color_region(const String &p_start_key, const String &
color_region.color = p_color;
color_region.start_key = p_start_key;
color_region.end_key = p_end_key;
- color_region.line_only = p_line_only || p_end_key == "";
+ color_region.line_only = p_line_only || p_end_key.is_empty();
color_regions.insert(at, color_region);
clear_highlighting_cache();
}
@@ -501,7 +521,7 @@ void CodeHighlighter::add_color_region(const String &p_start_key, const String &
void CodeHighlighter::remove_color_region(const String &p_start_key) {
for (int i = 0; i < color_regions.size(); i++) {
if (color_regions[i].start_key == p_start_key) {
- color_regions.remove(i);
+ color_regions.remove_at(i);
break;
}
}
@@ -529,7 +549,7 @@ void CodeHighlighter::set_color_regions(const Dictionary &p_color_regions) {
String start_key = key.get_slice(" ", 0);
String end_key = key.get_slice_count(" ") > 1 ? key.get_slice(" ", 1) : String();
- add_color_region(start_key, end_key, p_color_regions[key], end_key == "");
+ add_color_region(start_key, end_key, p_color_regions[key], end_key.is_empty());
}
clear_highlighting_cache();
}
diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h
index 0fe39ccff6..1243a9dbf7 100644
--- a/scene/resources/syntax_highlighter.h
+++ b/scene/resources/syntax_highlighter.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,7 +41,7 @@ class SyntaxHighlighter : public Resource {
GDCLASS(SyntaxHighlighter, Resource)
private:
- Map<int, Dictionary> highlighting_cache;
+ RBMap<int, Dictionary> highlighting_cache;
void _lines_edited_from(int p_from_line, int p_to_line);
protected:
@@ -83,7 +83,7 @@ private:
bool line_only = false;
};
Vector<ColorRegion> color_regions;
- Map<int, int> color_region_cache;
+ HashMap<int, int> color_region_cache;
Dictionary keywords;
Dictionary member_keywords;
diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp
index 33bb0a83e9..96a47c37c4 100644
--- a/scene/resources/text_file.cpp
+++ b/scene/resources/text_file.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#include "core/io/file_access.h"
bool TextFile::has_text() const {
- return text != "";
+ return !text.is_empty();
}
String TextFile::get_text() const {
@@ -51,7 +51,7 @@ void TextFile::reload_from_file() {
Error TextFile::load_text(const String &p_path) {
Vector<uint8_t> sourcef;
Error err;
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot open TextFile '" + p_path + "'.");
@@ -59,8 +59,7 @@ Error TextFile::load_text(const String &p_path) {
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
uint64_t r = f->get_buffer(w, len);
- f->close();
- memdelete(f);
+
ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
w[len] = 0;
diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h
index 005075a218..0c8cf855f0 100644
--- a/scene/resources/text_file.h
+++ b/scene/resources/text_file.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index 0094518967..f9390ca528 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,7 +36,7 @@ void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextLine::set_direction);
ClassDB::bind_method(D_METHOD("get_direction"), &TextLine::get_direction);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Left-to-right,Right-to-left"), "set_direction", "get_direction");
ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextLine::set_orientation);
ClassDB::bind_method(D_METHOD("get_orientation"), &TextLine::get_orientation);
@@ -55,19 +55,19 @@ void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override);
- ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""));
- ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1));
- ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGN_CENTER));
+ ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
- ClassDB::bind_method(D_METHOD("set_align", "align"), &TextLine::set_align);
- ClassDB::bind_method(D_METHOD("get_align"), &TextLine::get_align);
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &TextLine::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &TextLine::get_horizontal_alignment);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextLine::tab_align);
@@ -135,14 +135,14 @@ void TextLine::_shape() {
break;
}
- if (align == HALIGN_FILL) {
+ if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(rid, width, flags);
overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags);
} else {
TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags);
}
- } else if (align == HALIGN_FILL) {
+ } else if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(rid, width, flags);
}
dirty = false;
@@ -200,22 +200,22 @@ void TextLine::set_bidi_override(const Array &p_override) {
dirty = true;
}
-bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ERR_FAIL_COND_V(p_fonts.is_null(), false);
- bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+ bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta);
spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
dirty = true;
return res;
}
-bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
+bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length);
dirty = true;
return res;
}
-bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
+bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
const_cast<TextLine *>(this)->_shape();
return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align);
}
@@ -228,19 +228,19 @@ Rect2 TextLine::get_object_rect(Variant p_key) const {
return TS->shaped_text_get_object_rect(rid, p_key);
}
-void TextLine::set_align(HAlign p_align) {
- if (align != p_align) {
- if (align == HALIGN_FILL || p_align == HALIGN_FILL) {
- align = p_align;
+void TextLine::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ if (alignment != p_alignment) {
+ if (alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
+ alignment = p_alignment;
dirty = true;
} else {
- align = p_align;
+ alignment = p_alignment;
}
}
}
-HAlign TextLine::get_align() const {
- return align;
+HorizontalAlignment TextLine::get_horizontal_alignment() const {
+ return alignment;
}
void TextLine::tab_align(const Vector<float> &p_tab_stops) {
@@ -272,7 +272,7 @@ TextLine::OverrunBehavior TextLine::get_text_overrun_behavior() const {
void TextLine::set_width(float p_width) {
width = p_width;
- if (align == HALIGN_FILL || overrun_behavior != OVERRUN_NO_TRIMMING) {
+ if (alignment == HORIZONTAL_ALIGNMENT_FILL || overrun_behavior != OVERRUN_NO_TRIMMING) {
dirty = true;
}
}
@@ -322,18 +322,26 @@ void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) co
float length = TS->shaped_text_get_width(rid);
if (width > 0) {
- switch (align) {
- case HALIGN_FILL:
- case HALIGN_LEFT:
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT:
break;
- case HALIGN_CENTER: {
- if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += Math::floor((width - length) / 2.0);
- } else {
- ofs.y += Math::floor((width - length) / 2.0);
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ if (length <= width) {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((width - length) / 2.0);
+ }
+ } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - length;
+ } else {
+ ofs.y += width - length;
+ }
}
} break;
- case HALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += width - length;
} else {
@@ -361,18 +369,26 @@ void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_si
float length = TS->shaped_text_get_width(rid);
if (width > 0) {
- switch (align) {
- case HALIGN_FILL:
- case HALIGN_LEFT:
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT:
break;
- case HALIGN_CENTER: {
- if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += Math::floor((width - length) / 2.0);
- } else {
- ofs.y += Math::floor((width - length) / 2.0);
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ if (length <= width) {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((width - length) / 2.0);
+ }
+ } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - length;
+ } else {
+ ofs.y += width - length;
+ }
}
} break;
- case HALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += width - length;
} else {
@@ -411,5 +427,5 @@ TextLine::TextLine() {
}
TextLine::~TextLine() {
- TS->free(rid);
+ TS->free_rid(rid);
}
diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h
index 43739f27ec..c5762db0f2 100644
--- a/scene/resources/text_line.h
+++ b/scene/resources/text_line.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,7 +57,7 @@ private:
float width = -1.0;
uint16_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
- HAlign align = HALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
OverrunBehavior overrun_behavior = OVERRUN_TRIM_ELLIPSIS;
Vector<float> tab_stops;
@@ -86,12 +86,12 @@ public:
void set_preserve_control(bool p_enabled);
bool get_preserve_control() const;
- bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
- bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1);
- bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER);
+ bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant());
+ bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1);
+ bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER);
- void set_align(HAlign p_align);
- HAlign get_align() const;
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
void tab_align(const Vector<float> &p_tab_stops);
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 1b7fc64267..61adaf43dd 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -63,14 +63,14 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL(""));
ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
- ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""));
- ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1));
- ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGN_CENTER));
+ ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
- ClassDB::bind_method(D_METHOD("set_align", "align"), &TextParagraph::set_align);
- ClassDB::bind_method(D_METHOD("get_align"), &TextParagraph::get_align);
+ ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &TextParagraph::set_alignment);
+ ClassDB::bind_method(D_METHOD("get_alignment"), &TextParagraph::get_alignment);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_alignment", "get_alignment");
ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextParagraph::tab_align);
@@ -140,7 +140,7 @@ void TextParagraph::_bind_methods() {
void TextParagraph::_shape_lines() {
if (lines_dirty) {
for (int i = 0; i < lines_rid.size(); i++) {
- TS->free(lines_rid[i]);
+ TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
@@ -168,7 +168,7 @@ void TextParagraph::_shape_lines() {
RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x;
if (v_offset < h) {
- TS->free(line);
+ TS->free_rid(line);
break;
}
if (!tab_stops.is_empty()) {
@@ -223,7 +223,7 @@ void TextParagraph::_shape_lines() {
if (lines_hidden) {
overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS;
}
- if (align == HALIGN_FILL) {
+ if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
for (int i = 0; i < lines_rid.size(); i++) {
if (i < visible_lines - 1 || lines_rid.size() == 1) {
TS->shaped_text_fit_to_width(lines_rid[i], width, flags);
@@ -239,7 +239,7 @@ void TextParagraph::_shape_lines() {
} else {
// Autowrap disabled.
for (int i = 0; i < lines_rid.size(); i++) {
- if (align == HALIGN_FILL) {
+ if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(lines_rid[i], width, flags);
overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
@@ -271,7 +271,7 @@ void TextParagraph::clear() {
spacing_top = 0;
spacing_bottom = 0;
for (int i = 0; i < lines_rid.size(); i++) {
- TS->free(lines_rid[i]);
+ TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
TS->shaped_text_clear(rid);
@@ -344,9 +344,9 @@ void TextParagraph::clear_dropcap() {
lines_dirty = true;
}
-bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ERR_FAIL_COND_V(p_fonts.is_null(), false);
- bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+ bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta);
spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
lines_dirty = true;
@@ -366,31 +366,31 @@ void TextParagraph::set_bidi_override(const Array &p_override) {
lines_dirty = true;
}
-bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
+bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length);
lines_dirty = true;
return res;
}
-bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
+bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align);
lines_dirty = true;
return res;
}
-void TextParagraph::set_align(HAlign p_align) {
- if (align != p_align) {
- if (align == HALIGN_FILL || p_align == HALIGN_FILL) {
- align = p_align;
+void TextParagraph::set_alignment(HorizontalAlignment p_alignment) {
+ if (alignment != p_alignment) {
+ if (alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
+ alignment = p_alignment;
lines_dirty = true;
} else {
- align = p_align;
+ alignment = p_alignment;
}
}
}
-HAlign TextParagraph::get_align() const {
- return align;
+HorizontalAlignment TextParagraph::get_alignment() const {
+ return alignment;
}
void TextParagraph::tab_align(const Vector<float> &p_tab_stops) {
@@ -561,7 +561,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
if (h_offset > 0) {
// Draw dropcap.
Vector2 dc_off = ofs;
- if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
dc_off.x += width - h_offset;
} else {
@@ -579,7 +579,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
ofs.x = p_pos.x;
ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
if (i <= dropcap_lines) {
- if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
}
l_width -= h_offset;
@@ -588,7 +588,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
ofs.y = p_pos.y;
ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
if (i <= dropcap_lines) {
- if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
}
l_width -= h_offset;
@@ -596,9 +596,9 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
}
float line_width = TS->shaped_text_get_width(lines_rid[i]);
if (width > 0) {
- switch (align) {
- case HALIGN_FILL:
- if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += l_width - line_width;
} else {
@@ -606,16 +606,24 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
}
}
break;
- case HALIGN_LEFT:
+ case HORIZONTAL_ALIGNMENT_LEFT:
break;
- case HALIGN_CENTER: {
- if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += Math::floor((l_width - line_width) / 2.0);
- } else {
- ofs.y += Math::floor((l_width - line_width) / 2.0);
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ if (line_width <= l_width) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((l_width - line_width) / 2.0);
+ } else {
+ ofs.y += Math::floor((l_width - line_width) / 2.0);
+ }
+ } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - line_width;
+ } else {
+ ofs.y += l_width - line_width;
+ }
}
} break;
- case HALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += l_width - line_width;
} else {
@@ -655,7 +663,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
if (h_offset > 0) {
// Draw dropcap.
Vector2 dc_off = ofs;
- if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
dc_off.x += width - h_offset;
} else {
@@ -671,7 +679,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
ofs.x = p_pos.x;
ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
if (i <= dropcap_lines) {
- if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
}
l_width -= h_offset;
@@ -680,7 +688,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
ofs.y = p_pos.y;
ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
if (i <= dropcap_lines) {
- if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
}
l_width -= h_offset;
@@ -688,9 +696,9 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
}
float length = TS->shaped_text_get_width(lines_rid[i]);
if (width > 0) {
- switch (align) {
- case HALIGN_FILL:
- if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += l_width - length;
} else {
@@ -698,16 +706,24 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
}
}
break;
- case HALIGN_LEFT:
+ case HORIZONTAL_ALIGNMENT_LEFT:
break;
- case HALIGN_CENTER: {
- if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += Math::floor((l_width - length) / 2.0);
- } else {
- ofs.y += Math::floor((l_width - length) / 2.0);
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ if (length <= l_width) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((l_width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((l_width - length) / 2.0);
+ }
+ } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - length;
+ } else {
+ ofs.y += l_width - length;
+ }
}
} break;
- case HALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += l_width - length;
} else {
@@ -772,7 +788,7 @@ void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color
if (h_offset > 0) {
// Draw dropcap.
- if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += width - h_offset;
} else {
@@ -794,7 +810,7 @@ void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int
if (h_offset > 0) {
// Draw dropcap.
- if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += width - h_offset;
} else {
@@ -847,9 +863,9 @@ TextParagraph::TextParagraph() {
TextParagraph::~TextParagraph() {
for (int i = 0; i < lines_rid.size(); i++) {
- TS->free(lines_rid[i]);
+ TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
- TS->free(rid);
- TS->free(dropcap_rid);
+ TS->free_rid(rid);
+ TS->free_rid(dropcap_rid);
}
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 4c4af43d14..8a8a53943b 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -66,7 +66,7 @@ private:
uint16_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
OverrunBehavior overrun_behavior = OVERRUN_NO_TRIMMING;
- HAlign align = HALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
Vector<float> tab_stops;
@@ -102,12 +102,12 @@ public:
bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
void clear_dropcap();
- bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
- bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1);
- bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER);
+ bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant());
+ bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1);
+ bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER);
- void set_align(HAlign p_align);
- HAlign get_align() const;
+ void set_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_alignment() const;
void tab_align(const Vector<float> &p_tab_stops);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 311bd9524b..5850d253e3 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,29 +32,67 @@
#include "core/core_string_names.h"
#include "core/io/image_loader.h"
+#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/os/os.h"
#include "mesh.h"
#include "scene/resources/bit_map.h"
#include "servers/camera/camera_feed.h"
+int Texture2D::get_width() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int Texture2D::get_height() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) {
+ return ret;
+ }
+ return 0;
+}
Size2 Texture2D::get_size() const {
return Size2(get_width(), get_height());
}
bool Texture2D::is_pixel_opaque(int p_x, int p_y) const {
+ bool ret;
+ if (GDVIRTUAL_CALL(_is_pixel_opaque, p_x, p_y, ret)) {
+ return ret;
+ }
+
+ return true;
+}
+bool Texture2D::has_alpha() const {
+ bool ret;
+ if (GDVIRTUAL_CALL(_has_alpha, ret)) {
+ return ret;
+ }
+
return true;
}
void Texture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if (GDVIRTUAL_CALL(_draw, p_canvas_item, p_pos, p_modulate, p_transpose)) {
+ return;
+ }
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, get_size()), get_rid(), false, p_modulate, p_transpose);
}
void Texture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if (GDVIRTUAL_CALL(_draw_rect, p_canvas_item, p_rect, p_tile, p_modulate, p_transpose)) {
+ return;
+ }
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, get_rid(), p_tile, p_modulate, p_transpose);
}
void Texture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ if (GDVIRTUAL_CALL(_draw_rect_region, p_canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_clip_uv)) {
+ return;
+ }
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, get_rid(), p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
@@ -75,6 +113,15 @@ void Texture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_image"), &Texture2D::get_image);
ADD_GROUP("", "");
+
+ GDVIRTUAL_BIND(_get_width);
+ GDVIRTUAL_BIND(_get_height);
+ GDVIRTUAL_BIND(_is_pixel_opaque, "x", "y");
+ GDVIRTUAL_BIND(_has_alpha);
+
+ GDVIRTUAL_BIND(_draw, "to_canvas_item", "pos", "modulate", "transpose")
+ GDVIRTUAL_BIND(_draw_rect, "to_canvas_item", "rect", "tile", "modulate", "transpose")
+ GDVIRTUAL_BIND(_draw_rect_region, "tp_canvas_item", "rect", "src_rect", "modulate", "transpose", "clip_uv");
}
Texture2D::Texture2D() {
@@ -165,7 +212,7 @@ void ImageTexture::update(const Ref<Image> &p_image) {
ERR_FAIL_COND_MSG(mipmaps != p_image->has_mipmaps(),
"The new image mipmaps configuration must match the texture's image mipmaps configuration");
- RenderingServer::get_singleton()->texture_2d_update(texture, p_image);
+ RS::get_singleton()->texture_2d_update(texture, p_image);
notify_property_list_changed();
emit_changed();
@@ -291,9 +338,315 @@ ImageTexture::~ImageTexture() {
}
}
+/////////////////////
+
+void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
+ if (p_data.size() == 0) {
+ return; //nothing to do
+ }
+
+ const uint8_t *data = p_data.ptr();
+ uint32_t data_size = p_data.size();
+ ERR_FAIL_COND(data_size < 20);
+ compression_mode = CompressionMode(decode_uint32(data + 0));
+ format = Image::Format(decode_uint32(data + 4));
+ uint32_t mipmap_count = decode_uint32(data + 8);
+ size.width = decode_uint32(data + 12);
+ size.height = decode_uint32(data + 16);
+ mipmaps = mipmap_count > 1;
+
+ data += 20;
+ data_size -= 20;
+
+ Ref<Image> image;
+
+ switch (compression_mode) {
+ case COMPRESSION_MODE_LOSSLESS:
+ case COMPRESSION_MODE_LOSSY: {
+ Vector<uint8_t> image_data;
+
+ ERR_FAIL_COND(data_size < 4);
+ for (uint32_t i = 0; i < mipmap_count; i++) {
+ uint32_t mipsize = decode_uint32(data);
+ data += 4;
+ data_size -= 4;
+ ERR_FAIL_COND(mipsize < data_size);
+ Ref<Image> img = memnew(Image(data, data_size));
+ ERR_FAIL_COND(img->is_empty());
+ if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps.
+ img->convert(format);
+ }
+ image_data.append_array(img->get_data());
+
+ data += mipsize;
+ data_size -= mipsize;
+ }
+
+ image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, image_data)));
+
+ } break;
+ case COMPRESSION_MODE_BASIS_UNIVERSAL: {
+ ERR_FAIL_COND(!Image::basis_universal_unpacker_ptr);
+ image = Image::basis_universal_unpacker_ptr(data, data_size);
+
+ } break;
+ case COMPRESSION_MODE_S3TC:
+ case COMPRESSION_MODE_ETC2:
+ case COMPRESSION_MODE_BPTC: {
+ image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, p_data.slice(20))));
+ } break;
+ }
+ ERR_FAIL_COND(image.is_null());
+
+ if (texture.is_null()) {
+ texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ } else {
+ RID new_texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ RenderingServer::get_singleton()->texture_replace(texture, new_texture);
+ }
+
+ image_stored = true;
+ RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
+ alpha_cache.unref();
+
+ if (keep_all_compressed_buffers || keep_compressed_buffer) {
+ compressed_buffer = p_data;
+ } else {
+ compressed_buffer.clear();
+ }
+}
+
+PortableCompressedTexture2D::CompressionMode PortableCompressedTexture2D::get_compression_mode() const {
+ return compression_mode;
+}
+Vector<uint8_t> PortableCompressedTexture2D::_get_data() const {
+ return compressed_buffer;
+}
+
+void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map, float p_lossy_quality) {
+ ERR_FAIL_COND(p_image.is_null() || p_image->is_empty());
+
+ Vector<uint8_t> buffer;
+
+ buffer.resize(20);
+ encode_uint32(p_compression_mode, buffer.ptrw());
+ encode_uint32(p_image->get_format(), buffer.ptrw() + 4);
+ encode_uint32(p_image->get_mipmap_count() + 1, buffer.ptrw() + 8);
+ encode_uint32(p_image->get_width(), buffer.ptrw() + 12);
+ encode_uint32(p_image->get_height(), buffer.ptrw() + 16);
+
+ switch (p_compression_mode) {
+ case COMPRESSION_MODE_LOSSLESS:
+ case COMPRESSION_MODE_LOSSY: {
+ for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
+ Vector<uint8_t> data;
+ if (p_compression_mode == COMPRESSION_MODE_LOSSY) {
+ data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
+ } else {
+ data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i));
+ }
+ int data_len = data.size();
+ buffer.resize(buffer.size() + 4);
+ encode_uint32(data_len, buffer.ptrw() + buffer.size() - 4);
+ buffer.append_array(data);
+ }
+ } break;
+ case COMPRESSION_MODE_BASIS_UNIVERSAL: {
+ Image::UsedChannels uc = p_image->detect_used_channels(p_normal_map ? Image::COMPRESS_SOURCE_NORMAL : Image::COMPRESS_SOURCE_GENERIC);
+ Vector<uint8_t> budata = Image::basis_universal_packer(p_image, uc);
+ buffer.append_array(budata);
+
+ } break;
+ case COMPRESSION_MODE_S3TC:
+ case COMPRESSION_MODE_ETC2:
+ case COMPRESSION_MODE_BPTC: {
+ Ref<Image> copy = p_image->duplicate();
+ switch (p_compression_mode) {
+ case COMPRESSION_MODE_S3TC:
+ copy->compress(Image::COMPRESS_S3TC);
+ break;
+ case COMPRESSION_MODE_ETC2:
+ copy->compress(Image::COMPRESS_ETC2);
+ break;
+ case COMPRESSION_MODE_BPTC:
+ copy->compress(Image::COMPRESS_BPTC);
+ break;
+ default: {
+ };
+ }
+
+ buffer.append_array(copy->get_data());
+
+ } break;
+ }
+
+ _set_data(buffer);
+}
+
+Image::Format PortableCompressedTexture2D::get_format() const {
+ return format;
+}
+
+Ref<Image> PortableCompressedTexture2D::get_image() const {
+ if (image_stored) {
+ return RenderingServer::get_singleton()->texture_2d_get(texture);
+ } else {
+ return Ref<Image>();
+ }
+}
+
+int PortableCompressedTexture2D::get_width() const {
+ return size.width;
+}
+
+int PortableCompressedTexture2D::get_height() const {
+ return size.height;
+}
+
+RID PortableCompressedTexture2D::get_rid() const {
+ if (texture.is_null()) {
+ //we are in trouble, create something temporary
+ texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+bool PortableCompressedTexture2D::has_alpha() const {
+ return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
+}
+
+void PortableCompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, size), texture, false, p_modulate, p_transpose);
+}
+
+void PortableCompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
+}
+
+void PortableCompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
+}
+
+bool PortableCompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
+ if (!alpha_cache.is_valid()) {
+ Ref<Image> img = get_image();
+ if (img.is_valid()) {
+ if (img->is_compressed()) { //must decompress, if compressed
+ Ref<Image> decom = img->duplicate();
+ decom->decompress();
+ img = decom;
+ }
+ alpha_cache.instantiate();
+ alpha_cache->create_from_image_alpha(img);
+ }
+ }
+
+ if (alpha_cache.is_valid()) {
+ int aw = int(alpha_cache->get_size().width);
+ int ah = int(alpha_cache->get_size().height);
+ if (aw == 0 || ah == 0) {
+ return true;
+ }
+
+ int x = p_x * aw / size.width;
+ int y = p_y * ah / size.height;
+
+ x = CLAMP(x, 0, aw);
+ y = CLAMP(y, 0, ah);
+
+ return alpha_cache->get_bit(Point2(x, y));
+ }
+
+ return true;
+}
+
+void PortableCompressedTexture2D::set_size_override(const Size2 &p_size) {
+ size_override = p_size;
+ RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
+}
+
+Size2 PortableCompressedTexture2D::get_size_override() const {
+ return size_override;
+}
+
+void PortableCompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+bool PortableCompressedTexture2D::keep_all_compressed_buffers = false;
+
+void PortableCompressedTexture2D::set_keep_all_compressed_buffers(bool p_keep) {
+ keep_all_compressed_buffers = p_keep;
+}
+
+bool PortableCompressedTexture2D::is_keeping_all_compressed_buffers() {
+ return keep_all_compressed_buffers;
+}
+
+void PortableCompressedTexture2D::set_keep_compressed_buffer(bool p_keep) {
+ keep_compressed_buffer = p_keep;
+ if (!p_keep) {
+ compressed_buffer.clear();
+ }
+}
+
+bool PortableCompressedTexture2D::is_keeping_compressed_buffer() const {
+ return keep_compressed_buffer;
+}
+
+void PortableCompressedTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_from_image", "image", "compression_mode", "normal_map", "lossy_quality"), &PortableCompressedTexture2D::create_from_image, DEFVAL(false), DEFVAL(0.8));
+ ClassDB::bind_method(D_METHOD("get_format"), &PortableCompressedTexture2D::get_format);
+ ClassDB::bind_method(D_METHOD("get_compression_mode"), &PortableCompressedTexture2D::get_compression_mode);
+
+ ClassDB::bind_method(D_METHOD("set_size_override", "size"), &PortableCompressedTexture2D::set_size_override);
+ ClassDB::bind_method(D_METHOD("get_size_override"), &PortableCompressedTexture2D::get_size_override);
+
+ ClassDB::bind_method(D_METHOD("set_keep_compressed_buffer", "keep"), &PortableCompressedTexture2D::set_keep_compressed_buffer);
+ ClassDB::bind_method(D_METHOD("is_keeping_compressed_buffer"), &PortableCompressedTexture2D::is_keeping_compressed_buffer);
+
+ ClassDB::bind_method(D_METHOD("_set_data", "data"), &PortableCompressedTexture2D::_set_data);
+ ClassDB::bind_method(D_METHOD("_get_data"), &PortableCompressedTexture2D::_get_data);
+
+ ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("set_keep_all_compressed_buffers", "keep"), &PortableCompressedTexture2D::set_keep_all_compressed_buffers);
+ ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("is_keeping_all_compressed_buffers"), &PortableCompressedTexture2D::is_keeping_all_compressed_buffers);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_override"), "set_size_override", "get_size_override");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_compressed_buffer"), "set_keep_compressed_buffer", "is_keeping_compressed_buffer");
+
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSLESS);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSY);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_BASIS_UNIVERSAL);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_S3TC);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_ETC2);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_BPTC);
+}
+
+PortableCompressedTexture2D::PortableCompressedTexture2D() {}
+
+PortableCompressedTexture2D::~PortableCompressedTexture2D() {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->free(texture);
+ }
+}
+
//////////////////////////////////////////
-Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit) {
+Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_size_limit) {
uint32_t data_format = f->get_32();
uint32_t w = f->get_16();
uint32_t h = f->get_16();
@@ -426,7 +779,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit
return Ref<Image>();
}
-void StreamTexture2D::set_path(const String &p_path, bool p_take_over) {
+void CompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
if (texture.is_valid()) {
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
@@ -434,55 +787,53 @@ void StreamTexture2D::set_path(const String &p_path, bool p_take_over) {
Resource::set_path(p_path, p_take_over);
}
-void StreamTexture2D::_requested_3d(void *p_ud) {
- StreamTexture2D *st = (StreamTexture2D *)p_ud;
- Ref<StreamTexture2D> stex(st);
+void CompressedTexture2D::_requested_3d(void *p_ud) {
+ CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
+ Ref<CompressedTexture2D> ctex(ct);
ERR_FAIL_COND(!request_3d_callback);
- request_3d_callback(stex);
+ request_3d_callback(ctex);
}
-void StreamTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {
- StreamTexture2D *st = (StreamTexture2D *)p_ud;
- Ref<StreamTexture2D> stex(st);
+void CompressedTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {
+ CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
+ Ref<CompressedTexture2D> ctex(ct);
ERR_FAIL_COND(!request_roughness_callback);
- request_roughness_callback(stex, p_normal_path, p_roughness_channel);
+ request_roughness_callback(ctex, p_normal_path, p_roughness_channel);
}
-void StreamTexture2D::_requested_normal(void *p_ud) {
- StreamTexture2D *st = (StreamTexture2D *)p_ud;
- Ref<StreamTexture2D> stex(st);
+void CompressedTexture2D::_requested_normal(void *p_ud) {
+ CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
+ Ref<CompressedTexture2D> ctex(ct);
ERR_FAIL_COND(!request_normal_callback);
- request_normal_callback(stex);
+ request_normal_callback(ctex);
}
-StreamTexture2D::TextureFormatRequestCallback StreamTexture2D::request_3d_callback = nullptr;
-StreamTexture2D::TextureFormatRoughnessRequestCallback StreamTexture2D::request_roughness_callback = nullptr;
-StreamTexture2D::TextureFormatRequestCallback StreamTexture2D::request_normal_callback = nullptr;
+CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_3d_callback = nullptr;
+CompressedTexture2D::TextureFormatRoughnessRequestCallback CompressedTexture2D::request_roughness_callback = nullptr;
+CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_normal_callback = nullptr;
-Image::Format StreamTexture2D::get_format() const {
+Image::Format CompressedTexture2D::get_format() const {
return format;
}
-Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
+Error CompressedTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
alpha_cache.unref();
ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') {
- memdelete(f);
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is corrupt (Bad header).");
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is corrupt (Bad header).");
}
uint32_t version = f->get_32();
if (version > FORMAT_VERSION) {
- memdelete(f);
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new.");
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
}
r_width = f->get_32();
r_height = f->get_32();
@@ -514,8 +865,6 @@ Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_hei
image = load_image_from_file(f, p_size_limit);
- memdelete(f);
-
if (image.is_null() || image->is_empty()) {
return ERR_CANT_OPEN;
}
@@ -523,7 +872,7 @@ Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_hei
return OK;
}
-Error StreamTexture2D::load(const String &p_path) {
+Error CompressedTexture2D::load(const String &p_path) {
int lw, lh;
Ref<Image> image;
image.instantiate();
@@ -553,7 +902,7 @@ Error StreamTexture2D::load(const String &p_path) {
path_to_file = p_path;
format = image->get_format();
- if (get_path() == String()) {
+ if (get_path().is_empty()) {
//temporarily set path if no path set for resource, helps find errors
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
@@ -590,51 +939,51 @@ Error StreamTexture2D::load(const String &p_path) {
return OK;
}
-String StreamTexture2D::get_load_path() const {
+String CompressedTexture2D::get_load_path() const {
return path_to_file;
}
-int StreamTexture2D::get_width() const {
+int CompressedTexture2D::get_width() const {
return w;
}
-int StreamTexture2D::get_height() const {
+int CompressedTexture2D::get_height() const {
return h;
}
-RID StreamTexture2D::get_rid() const {
+RID CompressedTexture2D::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_2d_placeholder_create();
}
return texture;
}
-void StreamTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+void CompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
}
-void StreamTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+void CompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
}
-void StreamTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+void CompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if ((w | h) == 0) {
return;
}
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
-bool StreamTexture2D::has_alpha() const {
+bool CompressedTexture2D::has_alpha() const {
return false;
}
-Ref<Image> StreamTexture2D::get_image() const {
+Ref<Image> CompressedTexture2D::get_image() const {
if (texture.is_valid()) {
return RS::get_singleton()->texture_2d_get(texture);
} else {
@@ -642,7 +991,7 @@ Ref<Image> StreamTexture2D::get_image() const {
}
}
-bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const {
+bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
if (!alpha_cache.is_valid()) {
Ref<Image> img = get_image();
if (img.is_valid()) {
@@ -676,7 +1025,7 @@ bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const {
return true;
}
-void StreamTexture2D::reload_from_file() {
+void CompressedTexture2D::reload_from_file() {
String path = get_path();
if (!path.is_resource_file()) {
return;
@@ -691,56 +1040,56 @@ void StreamTexture2D::reload_from_file() {
load(path);
}
-void StreamTexture2D::_validate_property(PropertyInfo &property) const {
+void CompressedTexture2D::_validate_property(PropertyInfo &property) const {
}
-void StreamTexture2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load", "path"), &StreamTexture2D::load);
- ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTexture2D::get_load_path);
+void CompressedTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture2D::load);
+ ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture2D::get_load_path);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
}
-StreamTexture2D::StreamTexture2D() {}
+CompressedTexture2D::CompressedTexture2D() {}
-StreamTexture2D::~StreamTexture2D() {
+CompressedTexture2D::~CompressedTexture2D() {
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
}
-RES ResourceFormatLoaderStreamTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- Ref<StreamTexture2D> st;
+Ref<Resource> ResourceFormatLoaderCompressedTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ Ref<CompressedTexture2D> st;
st.instantiate();
Error err = st->load(p_path);
if (r_error) {
*r_error = err;
}
if (err != OK) {
- return RES();
+ return Ref<Resource>();
}
return st;
}
-void ResourceFormatLoaderStreamTexture2D::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("stex");
+void ResourceFormatLoaderCompressedTexture2D::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("ctex");
}
-bool ResourceFormatLoaderStreamTexture2D::handles_type(const String &p_type) const {
- return p_type == "StreamTexture2D";
+bool ResourceFormatLoaderCompressedTexture2D::handles_type(const String &p_type) const {
+ return p_type == "CompressedTexture2D";
}
-String ResourceFormatLoaderStreamTexture2D::get_resource_type(const String &p_path) const {
- if (p_path.get_extension().to_lower() == "stex") {
- return "StreamTexture2D";
+String ResourceFormatLoaderCompressedTexture2D::get_resource_type(const String &p_path) const {
+ if (p_path.get_extension().to_lower() == "ctex") {
+ return "CompressedTexture2D";
}
return "";
}
////////////////////////////////////
-TypedArray<Image> Texture3D::_get_data() const {
+TypedArray<Image> Texture3D::_get_datai() const {
Vector<Ref<Image>> data = get_data();
TypedArray<Image> ret;
@@ -751,13 +1100,73 @@ TypedArray<Image> Texture3D::_get_data() const {
return ret;
}
+Image::Format Texture3D::get_format() const {
+ Image::Format ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_format, ret)) {
+ return ret;
+ }
+ return Image::FORMAT_MAX;
+}
+
+int Texture3D::get_width() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int Texture3D::get_height() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int Texture3D::get_depth() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_depth, ret)) {
+ return ret;
+ }
+
+ return 0;
+}
+
+bool Texture3D::has_mipmaps() const {
+ bool ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) {
+ return ret;
+ }
+ return false;
+}
+
+Vector<Ref<Image>> Texture3D::get_data() const {
+ TypedArray<Image> ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_data, ret)) {
+ Vector<Ref<Image>> data;
+ data.resize(ret.size());
+ for (int i = 0; i < data.size(); i++) {
+ data.write[i] = ret[i];
+ }
+ return data;
+ }
+ return Vector<Ref<Image>>();
+}
void Texture3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_format"), &Texture3D::get_format);
ClassDB::bind_method(D_METHOD("get_width"), &Texture3D::get_width);
ClassDB::bind_method(D_METHOD("get_height"), &Texture3D::get_height);
ClassDB::bind_method(D_METHOD("get_depth"), &Texture3D::get_depth);
ClassDB::bind_method(D_METHOD("has_mipmaps"), &Texture3D::has_mipmaps);
- ClassDB::bind_method(D_METHOD("get_data"), &Texture3D::_get_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &Texture3D::_get_datai);
+
+ GDVIRTUAL_BIND(_get_format);
+ GDVIRTUAL_BIND(_get_width);
+ GDVIRTUAL_BIND(_get_height);
+ GDVIRTUAL_BIND(_get_depth);
+ GDVIRTUAL_BIND(_has_mipmaps);
+ GDVIRTUAL_BIND(_get_data);
}
//////////////////////////////////////////
@@ -846,7 +1255,7 @@ ImageTexture3D::~ImageTexture3D() {
////////////////////////////////////////////
-void StreamTexture3D::set_path(const String &p_path, bool p_take_over) {
+void CompressedTexture3D::set_path(const String &p_path, bool p_take_over) {
if (texture.is_valid()) {
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
@@ -854,23 +1263,23 @@ void StreamTexture3D::set_path(const String &p_path, bool p_take_over) {
Resource::set_path(p_path, p_take_over);
}
-Image::Format StreamTexture3D::get_format() const {
+Image::Format CompressedTexture3D::get_format() const {
return format;
}
-Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) {
- FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
+Error CompressedTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L', ERR_FILE_UNRECOGNIZED);
- //stored as stream textures (used for lossless and lossy compression)
+ //stored as compressed textures (used for lossless and lossy compression)
uint32_t version = f->get_32();
if (version > FORMAT_VERSION) {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new.");
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
}
r_depth = f->get_32(); //depth
@@ -887,7 +1296,7 @@ Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_da
r_data.clear();
for (int i = 0; i < (r_depth + mipmaps); i++) {
- Ref<Image> image = StreamTexture2D::load_image_from_file(f, 0);
+ Ref<Image> image = CompressedTexture2D::load_image_from_file(f, 0);
ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
if (i == 0) {
r_format = image->get_format();
@@ -900,7 +1309,7 @@ Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_da
return OK;
}
-Error StreamTexture3D::load(const String &p_path) {
+Error CompressedTexture3D::load(const String &p_path) {
Vector<Ref<Image>> data;
int tw, th, td;
@@ -927,7 +1336,7 @@ Error StreamTexture3D::load(const String &p_path) {
path_to_file = p_path;
- if (get_path() == String()) {
+ if (get_path().is_empty()) {
//temporarily set path if no path set for resource, helps find errors
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
@@ -937,34 +1346,34 @@ Error StreamTexture3D::load(const String &p_path) {
return OK;
}
-String StreamTexture3D::get_load_path() const {
+String CompressedTexture3D::get_load_path() const {
return path_to_file;
}
-int StreamTexture3D::get_width() const {
+int CompressedTexture3D::get_width() const {
return w;
}
-int StreamTexture3D::get_height() const {
+int CompressedTexture3D::get_height() const {
return h;
}
-int StreamTexture3D::get_depth() const {
+int CompressedTexture3D::get_depth() const {
return d;
}
-bool StreamTexture3D::has_mipmaps() const {
+bool CompressedTexture3D::has_mipmaps() const {
return mipmaps;
}
-RID StreamTexture3D::get_rid() const {
+RID CompressedTexture3D::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_3d_placeholder_create();
}
return texture;
}
-Vector<Ref<Image>> StreamTexture3D::get_data() const {
+Vector<Ref<Image>> CompressedTexture3D::get_data() const {
if (texture.is_valid()) {
return RS::get_singleton()->texture_3d_get(texture);
} else {
@@ -972,7 +1381,7 @@ Vector<Ref<Image>> StreamTexture3D::get_data() const {
}
}
-void StreamTexture3D::reload_from_file() {
+void CompressedTexture3D::reload_from_file() {
String path = get_path();
if (!path.is_resource_file()) {
return;
@@ -987,19 +1396,19 @@ void StreamTexture3D::reload_from_file() {
load(path);
}
-void StreamTexture3D::_validate_property(PropertyInfo &property) const {
+void CompressedTexture3D::_validate_property(PropertyInfo &property) const {
}
-void StreamTexture3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load", "path"), &StreamTexture3D::load);
- ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTexture3D::get_load_path);
+void CompressedTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture3D::load);
+ ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture3D::get_load_path);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
}
-StreamTexture3D::StreamTexture3D() {}
+CompressedTexture3D::CompressedTexture3D() {}
-StreamTexture3D::~StreamTexture3D() {
+CompressedTexture3D::~CompressedTexture3D() {
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
@@ -1007,31 +1416,31 @@ StreamTexture3D::~StreamTexture3D() {
/////////////////////////////
-RES ResourceFormatLoaderStreamTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- Ref<StreamTexture3D> st;
+Ref<Resource> ResourceFormatLoaderCompressedTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ Ref<CompressedTexture3D> st;
st.instantiate();
Error err = st->load(p_path);
if (r_error) {
*r_error = err;
}
if (err != OK) {
- return RES();
+ return Ref<Resource>();
}
return st;
}
-void ResourceFormatLoaderStreamTexture3D::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("stex3d");
+void ResourceFormatLoaderCompressedTexture3D::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("ctex3d");
}
-bool ResourceFormatLoaderStreamTexture3D::handles_type(const String &p_type) const {
- return p_type == "StreamTexture3D";
+bool ResourceFormatLoaderCompressedTexture3D::handles_type(const String &p_type) const {
+ return p_type == "CompressedTexture3D";
}
-String ResourceFormatLoaderStreamTexture3D::get_resource_type(const String &p_path) const {
- if (p_path.get_extension().to_lower() == "stex3d") {
- return "StreamTexture3D";
+String ResourceFormatLoaderCompressedTexture3D::get_resource_type(const String &p_path) const {
+ if (p_path.get_extension().to_lower() == "ctex3d") {
+ return "CompressedTexture3D";
}
return "";
}
@@ -1156,7 +1565,7 @@ void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_m
rc.size.height = atlas->get_height();
}
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), atlas->get_rid(), rc, p_modulate, p_transpose, filter_clip);
+ atlas->draw_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), rc, p_modulate, p_transpose, filter_clip);
}
void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
@@ -1177,7 +1586,7 @@ void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile
Vector2 scale = p_rect.size / (region.size + margin.size);
Rect2 dr(p_rect.position + margin.position * scale, rc.size * scale);
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), rc, p_modulate, p_transpose, filter_clip);
+ atlas->draw_rect_region(p_canvas_item, dr, rc, p_modulate, p_transpose, filter_clip);
}
void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
@@ -1190,7 +1599,7 @@ void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
Rect2 src_c;
get_rect_region(p_rect, p_src_rect, dr, src_c);
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), src_c, p_modulate, p_transpose, filter_clip);
+ atlas->draw_rect_region(p_canvas_item, dr, src_c, p_modulate, p_transpose, filter_clip);
}
bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
@@ -1308,8 +1717,8 @@ void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_mo
Transform2D xform;
xform.set_origin(p_pos);
if (p_transpose) {
- SWAP(xform.elements[0][1], xform.elements[1][0]);
- SWAP(xform.elements[0][0], xform.elements[1][1]);
+ SWAP(xform.columns[0][1], xform.columns[1][0]);
+ SWAP(xform.columns[0][0], xform.columns[1][1]);
}
RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
@@ -1330,8 +1739,8 @@ void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile,
xform.set_scale(p_rect.size / size);
if (p_transpose) {
- SWAP(xform.elements[0][1], xform.elements[1][0]);
- SWAP(xform.elements[0][0], xform.elements[1][1]);
+ SWAP(xform.columns[0][1], xform.columns[1][0]);
+ SWAP(xform.columns[0][0], xform.columns[1][1]);
}
RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
@@ -1352,8 +1761,8 @@ void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const
xform.set_scale(p_rect.size / size);
if (p_transpose) {
- SWAP(xform.elements[0][1], xform.elements[1][0]);
- SWAP(xform.elements[0][0], xform.elements[1][1]);
+ SWAP(xform.columns[0][1], xform.columns[1][0]);
+ SWAP(xform.columns[0][0], xform.columns[1][1]);
}
RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
@@ -1869,6 +2278,8 @@ Ref<Image> GradientTexture1D::get_image() const {
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
+//////////////////
+
GradientTexture2D::GradientTexture2D() {
_queue_update();
}
@@ -1890,7 +2301,8 @@ void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) {
if (gradient.is_valid()) {
gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update));
}
- _queue_update();
+ _update();
+ emit_changed();
}
Ref<Gradient> GradientTexture2D::get_gradient() const {
@@ -1902,7 +2314,7 @@ void GradientTexture2D::_queue_update() {
return;
}
update_pending = true;
- call_deferred("_update");
+ call_deferred(SNAME("_update"));
}
void GradientTexture2D::_update() {
@@ -2103,8 +2515,8 @@ void GradientTexture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update"), &GradientTexture2D::_update);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
ADD_GROUP("Fill", "fill_");
@@ -2446,6 +2858,63 @@ AnimatedTexture::~AnimatedTexture() {
///////////////////////////////
+Image::Format TextureLayered::get_format() const {
+ Image::Format ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_format, ret)) {
+ return ret;
+ }
+ return Image::FORMAT_MAX;
+}
+
+TextureLayered::LayeredType TextureLayered::get_layered_type() const {
+ uint32_t ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_layered_type, ret)) {
+ return (LayeredType)ret;
+ }
+ return LAYERED_TYPE_2D_ARRAY;
+}
+
+int TextureLayered::get_width() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int TextureLayered::get_height() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int TextureLayered::get_layers() const {
+ int ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_layers, ret)) {
+ return ret;
+ }
+
+ return 0;
+}
+
+bool TextureLayered::has_mipmaps() const {
+ bool ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) {
+ return ret;
+ }
+ return false;
+}
+
+Ref<Image> TextureLayered::get_layer_data(int p_layer) const {
+ Ref<Image> ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_layer_data, p_layer, ret)) {
+ return ret;
+ }
+ return Ref<Image>();
+}
+
void TextureLayered::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_format"), &TextureLayered::get_format);
ClassDB::bind_method(D_METHOD("get_layered_type"), &TextureLayered::get_layered_type);
@@ -2458,6 +2927,14 @@ void TextureLayered::_bind_methods() {
BIND_ENUM_CONSTANT(LAYERED_TYPE_2D_ARRAY);
BIND_ENUM_CONSTANT(LAYERED_TYPE_CUBEMAP);
BIND_ENUM_CONSTANT(LAYERED_TYPE_CUBEMAP_ARRAY);
+
+ GDVIRTUAL_BIND(_get_format);
+ GDVIRTUAL_BIND(_get_layered_type);
+ GDVIRTUAL_BIND(_get_width);
+ GDVIRTUAL_BIND(_get_height);
+ GDVIRTUAL_BIND(_get_layers);
+ GDVIRTUAL_BIND(_has_mipmaps);
+ GDVIRTUAL_BIND(_get_layer_data, "layer_index");
}
///////////////////////////////
@@ -2599,7 +3076,7 @@ ImageTextureLayered::~ImageTextureLayered() {
///////////////////////////////////////////
-void StreamTextureLayered::set_path(const String &p_path, bool p_take_over) {
+void CompressedTextureLayered::set_path(const String &p_path, bool p_take_over) {
if (texture.is_valid()) {
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
@@ -2607,26 +3084,26 @@ void StreamTextureLayered::set_path(const String &p_path, bool p_take_over) {
Resource::set_path(p_path, p_take_over);
}
-Image::Format StreamTextureLayered::get_format() const {
+Image::Format CompressedTextureLayered::get_format() const {
return format;
}
-Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) {
+Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) {
ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);
- FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L') {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture layered file is corrupt (Bad header).");
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture layered file is corrupt (Bad header).");
}
uint32_t version = f->get_32();
if (version > FORMAT_VERSION) {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new.");
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
}
uint32_t layer_count = f->get_32(); //layer count
@@ -2647,7 +3124,7 @@ Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>>
images.resize(layer_count);
for (uint32_t i = 0; i < layer_count; i++) {
- Ref<Image> image = StreamTexture2D::load_image_from_file(f, p_size_limit);
+ Ref<Image> image = CompressedTexture2D::load_image_from_file(f, p_size_limit);
ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
images.write[i] = image;
}
@@ -2655,7 +3132,7 @@ Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>>
return OK;
}
-Error StreamTextureLayered::load(const String &p_path) {
+Error CompressedTextureLayered::load(const String &p_path) {
Vector<Ref<Image>> images;
int mipmap_limit;
@@ -2680,7 +3157,7 @@ Error StreamTextureLayered::load(const String &p_path) {
path_to_file = p_path;
- if (get_path() == String()) {
+ if (get_path().is_empty()) {
//temporarily set path if no path set for resource, helps find errors
RenderingServer::get_singleton()->texture_set_path(texture, p_path);
}
@@ -2690,38 +3167,38 @@ Error StreamTextureLayered::load(const String &p_path) {
return OK;
}
-String StreamTextureLayered::get_load_path() const {
+String CompressedTextureLayered::get_load_path() const {
return path_to_file;
}
-int StreamTextureLayered::get_width() const {
+int CompressedTextureLayered::get_width() const {
return w;
}
-int StreamTextureLayered::get_height() const {
+int CompressedTextureLayered::get_height() const {
return h;
}
-int StreamTextureLayered::get_layers() const {
+int CompressedTextureLayered::get_layers() const {
return layers;
}
-bool StreamTextureLayered::has_mipmaps() const {
+bool CompressedTextureLayered::has_mipmaps() const {
return mipmaps;
}
-TextureLayered::LayeredType StreamTextureLayered::get_layered_type() const {
+TextureLayered::LayeredType CompressedTextureLayered::get_layered_type() const {
return layered_type;
}
-RID StreamTextureLayered::get_rid() const {
+RID CompressedTextureLayered::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
}
return texture;
}
-Ref<Image> StreamTextureLayered::get_layer_data(int p_layer) const {
+Ref<Image> CompressedTextureLayered::get_layer_data(int p_layer) const {
if (texture.is_valid()) {
return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
} else {
@@ -2729,7 +3206,7 @@ Ref<Image> StreamTextureLayered::get_layer_data(int p_layer) const {
}
}
-void StreamTextureLayered::reload_from_file() {
+void CompressedTextureLayered::reload_from_file() {
String path = get_path();
if (!path.is_resource_file()) {
return;
@@ -2744,21 +3221,21 @@ void StreamTextureLayered::reload_from_file() {
load(path);
}
-void StreamTextureLayered::_validate_property(PropertyInfo &property) const {
+void CompressedTextureLayered::_validate_property(PropertyInfo &property) const {
}
-void StreamTextureLayered::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load", "path"), &StreamTextureLayered::load);
- ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTextureLayered::get_load_path);
+void CompressedTextureLayered::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTextureLayered::load);
+ ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTextureLayered::get_load_path);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
}
-StreamTextureLayered::StreamTextureLayered(LayeredType p_type) {
+CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) {
layered_type = p_type;
}
-StreamTextureLayered::~StreamTextureLayered() {
+CompressedTextureLayered::~CompressedTextureLayered() {
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
@@ -2766,56 +3243,56 @@ StreamTextureLayered::~StreamTextureLayered() {
/////////////////////////////////////////////////
-RES ResourceFormatLoaderStreamTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- Ref<StreamTextureLayered> st;
- if (p_path.get_extension().to_lower() == "stexarray") {
- Ref<StreamTexture2DArray> s;
- s.instantiate();
- st = s;
- } else if (p_path.get_extension().to_lower() == "scube") {
- Ref<StreamCubemap> s;
- s.instantiate();
- st = s;
- } else if (p_path.get_extension().to_lower() == "scubearray") {
- Ref<StreamCubemapArray> s;
- s.instantiate();
- st = s;
+Ref<Resource> ResourceFormatLoaderCompressedTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ Ref<CompressedTextureLayered> ct;
+ if (p_path.get_extension().to_lower() == "ctexarray") {
+ Ref<CompressedTexture2DArray> c;
+ c.instantiate();
+ ct = c;
+ } else if (p_path.get_extension().to_lower() == "ccube") {
+ Ref<CompressedCubemap> c;
+ c.instantiate();
+ ct = c;
+ } else if (p_path.get_extension().to_lower() == "ccubearray") {
+ Ref<CompressedCubemapArray> c;
+ c.instantiate();
+ ct = c;
} else {
if (r_error) {
*r_error = ERR_FILE_UNRECOGNIZED;
}
- return RES();
+ return Ref<Resource>();
}
- Error err = st->load(p_path);
+ Error err = ct->load(p_path);
if (r_error) {
*r_error = err;
}
if (err != OK) {
- return RES();
+ return Ref<Resource>();
}
- return st;
+ return ct;
}
-void ResourceFormatLoaderStreamTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("stexarray");
- p_extensions->push_back("scube");
- p_extensions->push_back("scubearray");
+void ResourceFormatLoaderCompressedTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("ctexarray");
+ p_extensions->push_back("ccube");
+ p_extensions->push_back("ccubearray");
}
-bool ResourceFormatLoaderStreamTextureLayered::handles_type(const String &p_type) const {
- return p_type == "StreamTexture2DArray" || p_type == "StreamCubemap" || p_type == "StreamCubemapArray";
+bool ResourceFormatLoaderCompressedTextureLayered::handles_type(const String &p_type) const {
+ return p_type == "CompressedTexture2DArray" || p_type == "CompressedCubemap" || p_type == "CompressedCubemapArray";
}
-String ResourceFormatLoaderStreamTextureLayered::get_resource_type(const String &p_path) const {
- if (p_path.get_extension().to_lower() == "stexarray") {
- return "StreamTexture2DArray";
+String ResourceFormatLoaderCompressedTextureLayered::get_resource_type(const String &p_path) const {
+ if (p_path.get_extension().to_lower() == "ctexarray") {
+ return "CompressedTexture2DArray";
}
- if (p_path.get_extension().to_lower() == "scube") {
- return "StreamCubemap";
+ if (p_path.get_extension().to_lower() == "ccube") {
+ return "CompressedCubemap";
}
- if (p_path.get_extension().to_lower() == "scubearray") {
- return "StreamCubemapArray";
+ if (p_path.get_extension().to_lower() == "ccubearray") {
+ return "CompressedCubemapArray";
}
return "";
}
@@ -2918,3 +3395,148 @@ CameraTexture::~CameraTexture() {
RenderingServer::get_singleton()->free(_texture);
}
}
+
+///////////////////////////
+
+void PlaceholderTexture2D::set_size(Size2 p_size) {
+ size = p_size;
+}
+
+int PlaceholderTexture2D::get_width() const {
+ return size.width;
+}
+
+int PlaceholderTexture2D::get_height() const {
+ return size.height;
+}
+
+bool PlaceholderTexture2D::has_alpha() const {
+ return false;
+}
+
+Ref<Image> PlaceholderTexture2D::get_image() const {
+ return Ref<Image>();
+}
+
+RID PlaceholderTexture2D::get_rid() const {
+ return rid;
+}
+
+void PlaceholderTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture2D::set_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size");
+}
+
+PlaceholderTexture2D::PlaceholderTexture2D() {
+ rid = RS::get_singleton()->texture_2d_placeholder_create();
+}
+
+PlaceholderTexture2D::~PlaceholderTexture2D() {
+ RS::get_singleton()->free(rid);
+}
+
+///////////////////////////////////////////////
+
+void PlaceholderTexture3D::set_size(const Vector3i &p_size) {
+ size = p_size;
+}
+
+Vector3i PlaceholderTexture3D::get_size() const {
+ return size;
+}
+
+Image::Format PlaceholderTexture3D::get_format() const {
+ return Image::FORMAT_RGB8;
+}
+
+int PlaceholderTexture3D::get_width() const {
+ return size.x;
+}
+
+int PlaceholderTexture3D::get_height() const {
+ return size.y;
+}
+
+int PlaceholderTexture3D::get_depth() const {
+ return size.z;
+}
+
+bool PlaceholderTexture3D::has_mipmaps() const {
+ return false;
+}
+
+Vector<Ref<Image>> PlaceholderTexture3D::get_data() const {
+ return Vector<Ref<Image>>();
+}
+
+void PlaceholderTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTexture3D::get_size);
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3I, "size"), "set_size", "get_size");
+}
+
+PlaceholderTexture3D::PlaceholderTexture3D() {
+ rid = RS::get_singleton()->texture_3d_placeholder_create();
+}
+PlaceholderTexture3D::~PlaceholderTexture3D() {
+ RS::get_singleton()->free(rid);
+}
+
+/////////////////////////////////////////////////
+
+void PlaceholderTextureLayered::set_size(const Size2i &p_size) {
+ size = p_size;
+}
+
+Size2i PlaceholderTextureLayered::get_size() const {
+ return size;
+}
+
+void PlaceholderTextureLayered::set_layers(int p_layers) {
+ layers = p_layers;
+}
+
+Image::Format PlaceholderTextureLayered::get_format() const {
+ return Image::FORMAT_RGB8;
+}
+
+TextureLayered::LayeredType PlaceholderTextureLayered::get_layered_type() const {
+ return layered_type;
+}
+
+int PlaceholderTextureLayered::get_width() const {
+ return size.x;
+}
+
+int PlaceholderTextureLayered::get_height() const {
+ return size.y;
+}
+
+int PlaceholderTextureLayered::get_layers() const {
+ return layers;
+}
+
+bool PlaceholderTextureLayered::has_mipmaps() const {
+ return false;
+}
+
+Ref<Image> PlaceholderTextureLayered::get_layer_data(int p_layer) const {
+ return Ref<Image>();
+}
+
+void PlaceholderTextureLayered::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTextureLayered::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTextureLayered::get_size);
+ ClassDB::bind_method(D_METHOD("set_layers", "layers"), &PlaceholderTextureLayered::set_layers);
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_RANGE, "1,4096"), "set_layers", "get_layers");
+}
+
+PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) {
+ layered_type = p_type;
+ rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
+}
+PlaceholderTextureLayered::~PlaceholderTextureLayered() {
+ RS::get_singleton()->free(rid);
+}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 5b69711de6..317756e313 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,14 +57,23 @@ class Texture2D : public Texture {
protected:
static void _bind_methods();
+ GDVIRTUAL0RC(int, _get_width)
+ GDVIRTUAL0RC(int, _get_height)
+ GDVIRTUAL2RC(bool, _is_pixel_opaque, int, int)
+ GDVIRTUAL0RC(bool, _has_alpha)
+
+ GDVIRTUAL4C(_draw, RID, Point2, Color, bool)
+ GDVIRTUAL5C(_draw_rect, RID, Rect2, bool, Color, bool)
+ GDVIRTUAL6C(_draw_rect_region, RID, Rect2, Rect2, Color, bool, bool)
+
public:
- virtual int get_width() const = 0;
- virtual int get_height() const = 0;
+ virtual int get_width() const;
+ virtual int get_height() const;
virtual Size2 get_size() const;
virtual bool is_pixel_opaque(int p_x, int p_y) const;
- virtual bool has_alpha() const = 0;
+ virtual bool has_alpha() const;
virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const;
virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const;
@@ -128,8 +137,80 @@ public:
~ImageTexture();
};
-class StreamTexture2D : public Texture2D {
- GDCLASS(StreamTexture2D, Texture2D);
+class PortableCompressedTexture2D : public Texture2D {
+ GDCLASS(PortableCompressedTexture2D, Texture2D);
+
+public:
+ enum CompressionMode {
+ COMPRESSION_MODE_LOSSLESS,
+ COMPRESSION_MODE_LOSSY,
+ COMPRESSION_MODE_BASIS_UNIVERSAL,
+ COMPRESSION_MODE_S3TC,
+ COMPRESSION_MODE_ETC2,
+ COMPRESSION_MODE_BPTC,
+ };
+
+private:
+ CompressionMode compression_mode = COMPRESSION_MODE_LOSSLESS;
+ static bool keep_all_compressed_buffers;
+ bool keep_compressed_buffer = false;
+ Vector<uint8_t> compressed_buffer;
+ Size2 size;
+ Size2 size_override;
+ bool mipmaps = false;
+ Image::Format format = Image::FORMAT_L8;
+
+ mutable RID texture;
+ mutable Ref<BitMap> alpha_cache;
+
+ bool image_stored = false;
+
+protected:
+ Vector<uint8_t> _get_data() const;
+ void _set_data(const Vector<uint8_t> &p_data);
+
+ static void _bind_methods();
+
+public:
+ CompressionMode get_compression_mode() const;
+ void create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map = false, float p_lossy_quality = 0.8);
+
+ Image::Format get_format() const;
+
+ void update(const Ref<Image> &p_image);
+ Ref<Image> get_image() const override;
+
+ int get_width() const override;
+ int get_height() const override;
+
+ virtual RID get_rid() const override;
+
+ bool has_alpha() const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
+
+ bool is_pixel_opaque(int p_x, int p_y) const override;
+
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+
+ void set_size_override(const Size2 &p_size);
+ Size2 get_size_override() const;
+
+ void set_keep_compressed_buffer(bool p_keep);
+ bool is_keeping_compressed_buffer() const;
+
+ static void set_keep_all_compressed_buffers(bool p_keep);
+ static bool is_keeping_all_compressed_buffers();
+
+ PortableCompressedTexture2D();
+ ~PortableCompressedTexture2D();
+};
+
+VARIANT_ENUM_CAST(PortableCompressedTexture2D::CompressionMode)
+
+class CompressedTexture2D : public Texture2D {
+ GDCLASS(CompressedTexture2D, Texture2D);
public:
enum DataFormat {
@@ -172,10 +253,10 @@ protected:
void _validate_property(PropertyInfo &property) const override;
public:
- static Ref<Image> load_image_from_file(FileAccess *p_file, int p_size_limit);
+ static Ref<Image> load_image_from_file(Ref<FileAccess> p_file, int p_size_limit);
- typedef void (*TextureFormatRequestCallback)(const Ref<StreamTexture2D> &);
- typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<StreamTexture2D> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel);
+ typedef void (*TextureFormatRequestCallback)(const Ref<CompressedTexture2D> &);
+ typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<CompressedTexture2D> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel);
static TextureFormatRequestCallback request_3d_callback;
static TextureFormatRoughnessRequestCallback request_roughness_callback;
@@ -200,13 +281,13 @@ public:
virtual Ref<Image> get_image() const override;
- StreamTexture2D();
- ~StreamTexture2D();
+ CompressedTexture2D();
+ ~CompressedTexture2D();
};
-class ResourceFormatLoaderStreamTexture2D : public ResourceFormatLoader {
+class ResourceFormatLoaderCompressedTexture2D : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
@@ -300,6 +381,13 @@ class TextureLayered : public Texture {
protected:
static void _bind_methods();
+ GDVIRTUAL0RC(Image::Format, _get_format)
+ GDVIRTUAL0RC(uint32_t, _get_layered_type)
+ GDVIRTUAL0RC(int, _get_width)
+ GDVIRTUAL0RC(int, _get_height)
+ GDVIRTUAL0RC(int, _get_layers)
+ GDVIRTUAL0RC(bool, _has_mipmaps)
+ GDVIRTUAL1RC(Ref<Image>, _get_layer_data, int)
public:
enum LayeredType {
LAYERED_TYPE_2D_ARRAY,
@@ -307,13 +395,15 @@ public:
LAYERED_TYPE_CUBEMAP_ARRAY
};
- virtual Image::Format get_format() const = 0;
- virtual LayeredType get_layered_type() const = 0;
- virtual int get_width() const = 0;
- virtual int get_height() const = 0;
- virtual int get_layers() const = 0;
- virtual bool has_mipmaps() const = 0;
- virtual Ref<Image> get_layer_data(int p_layer) const = 0;
+ virtual Image::Format get_format() const;
+ virtual LayeredType get_layered_type() const;
+ virtual int get_width() const;
+ virtual int get_height() const;
+ virtual int get_layers() const;
+ virtual bool has_mipmaps() const;
+ virtual Ref<Image> get_layer_data(int p_layer) const;
+
+ TextureLayered() {}
};
VARIANT_ENUM_CAST(TextureLayered::LayeredType)
@@ -380,8 +470,8 @@ public:
ImageTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
};
-class StreamTextureLayered : public TextureLayered {
- GDCLASS(StreamTextureLayered, TextureLayered);
+class CompressedTextureLayered : public TextureLayered {
+ GDCLASS(CompressedTextureLayered, TextureLayered);
public:
enum DataFormat {
@@ -433,36 +523,36 @@ public:
virtual Ref<Image> get_layer_data(int p_layer) const override;
- StreamTextureLayered(LayeredType p_layered_type);
- ~StreamTextureLayered();
+ CompressedTextureLayered(LayeredType p_layered_type);
+ ~CompressedTextureLayered();
};
-class StreamTexture2DArray : public StreamTextureLayered {
- GDCLASS(StreamTexture2DArray, StreamTextureLayered)
+class CompressedTexture2DArray : public CompressedTextureLayered {
+ GDCLASS(CompressedTexture2DArray, CompressedTextureLayered)
public:
- StreamTexture2DArray() :
- StreamTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
+ CompressedTexture2DArray() :
+ CompressedTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
};
-class StreamCubemap : public StreamTextureLayered {
- GDCLASS(StreamCubemap, StreamTextureLayered);
+class CompressedCubemap : public CompressedTextureLayered {
+ GDCLASS(CompressedCubemap, CompressedTextureLayered);
public:
- StreamCubemap() :
- StreamTextureLayered(LAYERED_TYPE_CUBEMAP) {}
+ CompressedCubemap() :
+ CompressedTextureLayered(LAYERED_TYPE_CUBEMAP) {}
};
-class StreamCubemapArray : public StreamTextureLayered {
- GDCLASS(StreamCubemapArray, StreamTextureLayered);
+class CompressedCubemapArray : public CompressedTextureLayered {
+ GDCLASS(CompressedCubemapArray, CompressedTextureLayered);
public:
- StreamCubemapArray() :
- StreamTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
+ CompressedCubemapArray() :
+ CompressedTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
};
-class ResourceFormatLoaderStreamTextureLayered : public ResourceFormatLoader {
+class ResourceFormatLoaderCompressedTextureLayered : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
@@ -474,15 +564,21 @@ class Texture3D : public Texture {
protected:
static void _bind_methods();
- TypedArray<Image> _get_data() const;
+ TypedArray<Image> _get_datai() const;
+ GDVIRTUAL0RC(Image::Format, _get_format)
+ GDVIRTUAL0RC(int, _get_width)
+ GDVIRTUAL0RC(int, _get_height)
+ GDVIRTUAL0RC(int, _get_depth)
+ GDVIRTUAL0RC(bool, _has_mipmaps)
+ GDVIRTUAL0RC(TypedArray<Image>, _get_data)
public:
- virtual Image::Format get_format() const = 0;
- virtual int get_width() const = 0;
- virtual int get_height() const = 0;
- virtual int get_depth() const = 0;
- virtual bool has_mipmaps() const = 0;
- virtual Vector<Ref<Image>> get_data() const = 0;
+ virtual Image::Format get_format() const;
+ virtual int get_width() const;
+ virtual int get_height() const;
+ virtual int get_depth() const;
+ virtual bool has_mipmaps() const;
+ virtual Vector<Ref<Image>> get_data() const;
};
class ImageTexture3D : public Texture3D {
@@ -520,8 +616,8 @@ public:
~ImageTexture3D();
};
-class StreamTexture3D : public Texture3D {
- GDCLASS(StreamTexture3D, Texture3D);
+class CompressedTexture3D : public Texture3D {
+ GDCLASS(CompressedTexture3D, Texture3D);
public:
enum DataFormat {
@@ -571,13 +667,13 @@ public:
virtual Vector<Ref<Image>> get_data() const override;
- StreamTexture3D();
- ~StreamTexture3D();
+ CompressedTexture3D();
+ ~CompressedTexture3D();
};
-class ResourceFormatLoaderStreamTexture3D : public ResourceFormatLoader {
+class ResourceFormatLoaderCompressedTexture3D : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
@@ -595,7 +691,7 @@ public:
private:
mutable RID _texture;
Ref<Curve> _curve;
- int _width = 2048;
+ int _width = 256;
int _current_width = 0;
TextureMode texture_mode = TEXTURE_MODE_RGB;
TextureMode _current_texture_mode = TEXTURE_MODE_RGB;
@@ -637,7 +733,7 @@ private:
Ref<Curve> _curve_x;
Ref<Curve> _curve_y;
Ref<Curve> _curve_z;
- int _width = 2048;
+ int _width = 256;
int _current_width = 0;
void _update();
@@ -685,7 +781,7 @@ private:
Ref<Gradient> gradient;
bool update_pending = false;
RID texture;
- int width = 2048;
+ int width = 256;
bool use_hdr = false;
void _queue_update();
@@ -915,4 +1011,98 @@ public:
~CameraTexture();
};
+class PlaceholderTexture2D : public Texture2D {
+ GDCLASS(PlaceholderTexture2D, Texture2D)
+
+ RID rid;
+ Size2 size = Size2(1, 1);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_size(Size2 p_size);
+
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual RID get_rid() const override;
+ virtual bool has_alpha() const override;
+
+ virtual Ref<Image> get_image() const override;
+
+ PlaceholderTexture2D();
+ ~PlaceholderTexture2D();
+};
+
+class PlaceholderTexture3D : public Texture3D {
+ GDCLASS(PlaceholderTexture3D, Texture3D)
+
+ RID rid;
+ Vector3i size = Vector3i(1, 1, 1);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_size(const Vector3i &p_size);
+ Vector3i get_size() const;
+ virtual Image::Format get_format() const override;
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual int get_depth() const override;
+ virtual bool has_mipmaps() const override;
+ virtual Vector<Ref<Image>> get_data() const override;
+
+ PlaceholderTexture3D();
+ ~PlaceholderTexture3D();
+};
+
+class PlaceholderTextureLayered : public TextureLayered {
+ GDCLASS(PlaceholderTextureLayered, TextureLayered)
+
+ RID rid;
+ Size2i size = Size2i(1, 1);
+ int layers = 1;
+ LayeredType layered_type = LAYERED_TYPE_2D_ARRAY;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_size(const Size2i &p_size);
+ Size2i get_size() const;
+ void set_layers(int p_layers);
+ virtual Image::Format get_format() const override;
+ virtual LayeredType get_layered_type() const override;
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual int get_layers() const override;
+ virtual bool has_mipmaps() const override;
+ virtual Ref<Image> get_layer_data(int p_layer) const override;
+
+ PlaceholderTextureLayered(LayeredType p_type);
+ ~PlaceholderTextureLayered();
+};
+
+class PlaceholderTexture2DArray : public PlaceholderTextureLayered {
+ GDCLASS(PlaceholderTexture2DArray, PlaceholderTextureLayered)
+public:
+ PlaceholderTexture2DArray() :
+ PlaceholderTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
+};
+
+class PlaceholderCubemap : public PlaceholderTextureLayered {
+ GDCLASS(PlaceholderCubemap, PlaceholderTextureLayered)
+public:
+ PlaceholderCubemap() :
+ PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP) {}
+};
+
+class PlaceholderCubemapArray : public PlaceholderTextureLayered {
+ GDCLASS(PlaceholderCubemapArray, PlaceholderTextureLayered)
+public:
+ PlaceholderCubemapArray() :
+ PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
+};
+
#endif
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 99977a20f2..1e57933000 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "theme.h"
+
#include "core/string/print_string.h"
// Universal Theme resources used when no other theme has the item.
@@ -36,17 +37,17 @@ Ref<Theme> Theme::default_theme;
Ref<Theme> Theme::project_default_theme;
// Universal default values, final fallback for every theme.
-float Theme::default_base_scale = 1.0;
-Ref<Texture2D> Theme::default_icon;
-Ref<StyleBox> Theme::default_style;
-Ref<Font> Theme::default_font;
-int Theme::default_font_size = 16;
+float Theme::fallback_base_scale = 1.0;
+Ref<Texture2D> Theme::fallback_icon;
+Ref<StyleBox> Theme::fallback_style;
+Ref<Font> Theme::fallback_font;
+int Theme::fallback_font_size = 16;
// Dynamic properties.
bool Theme::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
- if (sname.find("/") != -1) {
+ if (sname.contains("/")) {
String type = sname.get_slicec('/', 1);
String theme_type = sname.get_slicec('/', 0);
String name = sname.get_slicec('/', 2);
@@ -78,7 +79,7 @@ bool Theme::_set(const StringName &p_name, const Variant &p_value) {
bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
String sname = p_name;
- if (sname.find("/") != -1) {
+ if (sname.contains("/")) {
String type = sname.get_slicec('/', 1);
String theme_type = sname.get_slicec('/', 0);
String name = sname.get_slicec('/', 2);
@@ -122,82 +123,64 @@ bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> list;
- const StringName *key = nullptr;
-
// Type variations.
- while ((key = variation_map.next(key))) {
- list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type"));
+ for (const KeyValue<StringName, StringName> &E : variation_map) {
+ list.push_back(PropertyInfo(Variant::STRING_NAME, String() + E.key + "/base_type"));
}
- key = nullptr;
-
// Icons.
- while ((key = icon_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = icon_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/icons/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) {
+ for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/icons/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
}
}
- key = nullptr;
-
// Styles.
- while ((key = style_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = style_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/styles/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) {
+ for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/styles/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
}
}
- key = nullptr;
-
// Fonts.
- while ((key = font_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = font_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/fonts/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) {
+ for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/fonts/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
}
}
- key = nullptr;
-
// Font sizes.
- while ((key = font_size_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = font_size_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2, PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : font_size_map) {
+ for (const KeyValue<StringName, int> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/font_sizes/" + F.key, PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
}
}
- key = nullptr;
-
// Colors.
- while ((key = color_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = color_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::COLOR, String() + *key + "/colors/" + *key2));
+ for (const KeyValue<StringName, HashMap<StringName, Color>> &E : color_map) {
+ for (const KeyValue<StringName, Color> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/colors/" + F.key));
}
}
- key = nullptr;
-
// Constants.
- while ((key = constant_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = constant_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::INT, String() + *key + "/constants/" + *key2));
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : constant_map) {
+ for (const KeyValue<StringName, int> &F : E.value) {
+ list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/constants/" + F.key));
}
}
// Sort and store properties.
list.sort();
+ String prev_type;
for (const PropertyInfo &E : list) {
+ // Add groups for types so that their names are left unchanged in the inspector.
+ String current_type = E.name.get_slice("/", 0);
+ if (prev_type != current_type) {
+ p_list->push_back(PropertyInfo(Variant::NIL, current_type, PROPERTY_HINT_NONE, current_type + "/", PROPERTY_USAGE_GROUP));
+ prev_type = current_type;
+ }
+
p_list->push_back(E);
}
}
@@ -220,91 +203,135 @@ void Theme::set_project_default(const Ref<Theme> &p_project_default) {
}
// Universal fallback values for theme item types.
-void Theme::set_default_base_scale(float p_base_scale) {
- default_base_scale = p_base_scale;
+void Theme::set_fallback_base_scale(float p_base_scale) {
+ fallback_base_scale = p_base_scale;
}
-void Theme::set_default_icon(const Ref<Texture2D> &p_icon) {
- default_icon = p_icon;
+void Theme::set_fallback_icon(const Ref<Texture2D> &p_icon) {
+ fallback_icon = p_icon;
}
-void Theme::set_default_style(const Ref<StyleBox> &p_style) {
- default_style = p_style;
+void Theme::set_fallback_style(const Ref<StyleBox> &p_style) {
+ fallback_style = p_style;
}
-void Theme::set_default_font(const Ref<Font> &p_font) {
- default_font = p_font;
+void Theme::set_fallback_font(const Ref<Font> &p_font) {
+ fallback_font = p_font;
}
-void Theme::set_default_font_size(int p_font_size) {
- default_font_size = p_font_size;
+void Theme::set_fallback_font_size(int p_font_size) {
+ fallback_font_size = p_font_size;
+}
+
+float Theme::get_fallback_base_scale() {
+ return fallback_base_scale;
+}
+
+Ref<Texture2D> Theme::get_fallback_icon() {
+ return fallback_icon;
+}
+
+Ref<StyleBox> Theme::get_fallback_style() {
+ return fallback_style;
+}
+
+Ref<Font> Theme::get_fallback_font() {
+ return fallback_font;
+}
+
+int Theme::get_fallback_font_size() {
+ return fallback_font_size;
+}
+
+bool Theme::is_valid_type_name(const String &p_name) {
+ for (int i = 0; i < p_name.length(); i++) {
+ if (!is_ascii_identifier_char(p_name[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Theme::is_valid_item_name(const String &p_name) {
+ if (p_name.is_empty()) {
+ return false;
+ }
+ for (int i = 0; i < p_name.length(); i++) {
+ if (!is_ascii_identifier_char(p_name[i])) {
+ return false;
+ }
+ }
+ return true;
}
// Fallback values for theme item types, configurable per theme.
-void Theme::set_default_theme_base_scale(float p_base_scale) {
- if (default_theme_base_scale == p_base_scale) {
+void Theme::set_default_base_scale(float p_base_scale) {
+ if (default_base_scale == p_base_scale) {
return;
}
- default_theme_base_scale = p_base_scale;
+ default_base_scale = p_base_scale;
_emit_theme_changed();
}
-float Theme::get_default_theme_base_scale() const {
- return default_theme_base_scale;
+float Theme::get_default_base_scale() const {
+ return default_base_scale;
}
-bool Theme::has_default_theme_base_scale() const {
- return default_theme_base_scale > 0.0;
+bool Theme::has_default_base_scale() const {
+ return default_base_scale > 0.0;
}
-void Theme::set_default_theme_font(const Ref<Font> &p_default_font) {
- if (default_theme_font == p_default_font) {
+void Theme::set_default_font(const Ref<Font> &p_default_font) {
+ if (default_font == p_default_font) {
return;
}
- if (default_theme_font.is_valid()) {
- default_theme_font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ if (default_font.is_valid()) {
+ default_font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- default_theme_font = p_default_font;
+ default_font = p_default_font;
- if (default_theme_font.is_valid()) {
- default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
+ if (default_font.is_valid()) {
+ default_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed();
}
-Ref<Font> Theme::get_default_theme_font() const {
- return default_theme_font;
+Ref<Font> Theme::get_default_font() const {
+ return default_font;
}
-bool Theme::has_default_theme_font() const {
- return default_theme_font.is_valid();
+bool Theme::has_default_font() const {
+ return default_font.is_valid();
}
-void Theme::set_default_theme_font_size(int p_font_size) {
- if (default_theme_font_size == p_font_size) {
+void Theme::set_default_font_size(int p_font_size) {
+ if (default_font_size == p_font_size) {
return;
}
- default_theme_font_size = p_font_size;
+ default_font_size = p_font_size;
_emit_theme_changed();
}
-int Theme::get_default_theme_font_size() const {
- return default_theme_font_size;
+int Theme::get_default_font_size() const {
+ return default_font_size;
}
-bool Theme::has_default_theme_font_size() const {
- return default_theme_font_size > 0;
+bool Theme::has_default_font_size() const {
+ return default_font_size > 0;
}
// Icons.
void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
bool existing = false;
if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
existing = true;
@@ -324,7 +351,7 @@ Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_the
if (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
return icon_map[p_theme_type][p_name];
} else {
- return default_icon;
+ return fallback_icon;
}
}
@@ -337,6 +364,8 @@ bool Theme::has_icon_nocheck(const StringName &p_name, const StringName &p_theme
}
void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot rename the icon '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
ERR_FAIL_COND_MSG(icon_map[p_theme_type].has(p_name), "Cannot rename the icon '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_old_name), "Cannot rename the icon '" + String(p_old_name) + "' because it does not exist.");
@@ -367,31 +396,52 @@ void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) con
return;
}
- const StringName *key = nullptr;
-
- while ((key = icon_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
void Theme::add_icon_type(const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
if (icon_map.has(p_theme_type)) {
return;
}
icon_map[p_theme_type] = HashMap<StringName, Ref<Texture2D>>();
}
+void Theme::remove_icon_type(const StringName &p_theme_type) {
+ if (!icon_map.has(p_theme_type)) {
+ return;
+ }
+
+ _freeze_change_propagation();
+
+ for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) {
+ Ref<Texture2D> icon = E.value;
+ if (icon.is_valid()) {
+ icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+
+ icon_map.erase(p_theme_type);
+
+ _unfreeze_and_propagate_changes();
+}
+
void Theme::get_icon_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = icon_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) {
+ p_list->push_back(E.key);
}
}
// Styleboxes.
void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
bool existing = false;
if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
existing = true;
@@ -411,7 +461,7 @@ Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_
if (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
return style_map[p_theme_type][p_name];
} else {
- return default_style;
+ return fallback_style;
}
}
@@ -424,6 +474,8 @@ bool Theme::has_stylebox_nocheck(const StringName &p_name, const StringName &p_t
}
void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot rename the stylebox '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
ERR_FAIL_COND_MSG(style_map[p_theme_type].has(p_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_old_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because it does not exist.");
@@ -454,31 +506,52 @@ void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list)
return;
}
- const StringName *key = nullptr;
-
- while ((key = style_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
void Theme::add_stylebox_type(const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
if (style_map.has(p_theme_type)) {
return;
}
style_map[p_theme_type] = HashMap<StringName, Ref<StyleBox>>();
}
+void Theme::remove_stylebox_type(const StringName &p_theme_type) {
+ if (!style_map.has(p_theme_type)) {
+ return;
+ }
+
+ _freeze_change_propagation();
+
+ for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) {
+ Ref<StyleBox> style = E.value;
+ if (style.is_valid()) {
+ style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+
+ style_map.erase(p_theme_type);
+
+ _unfreeze_and_propagate_changes();
+}
+
void Theme::get_stylebox_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = style_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) {
+ p_list->push_back(E.key);
}
}
// Fonts.
void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
bool existing = false;
if (font_map[p_theme_type][p_name].is_valid()) {
existing = true;
@@ -497,15 +570,15 @@ void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, c
Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const {
if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) {
return font_map[p_theme_type][p_name];
- } else if (has_default_theme_font()) {
- return default_theme_font;
- } else {
+ } else if (has_default_font()) {
return default_font;
+ } else {
+ return fallback_font;
}
}
bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const {
- return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || has_default_theme_font());
+ return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || has_default_font());
}
bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
@@ -513,6 +586,8 @@ bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme
}
void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot rename the font '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
ERR_FAIL_COND_MSG(font_map[p_theme_type].has(p_name), "Cannot rename the font '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_old_name), "Cannot rename the font '" + String(p_old_name) + "' because it does not exist.");
@@ -543,31 +618,52 @@ void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) con
return;
}
- const StringName *key = nullptr;
-
- while ((key = font_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
void Theme::add_font_type(const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
if (font_map.has(p_theme_type)) {
return;
}
font_map[p_theme_type] = HashMap<StringName, Ref<Font>>();
}
+void Theme::remove_font_type(const StringName &p_theme_type) {
+ if (!font_map.has(p_theme_type)) {
+ return;
+ }
+
+ _freeze_change_propagation();
+
+ for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) {
+ Ref<Font> font = E.value;
+ if (font.is_valid()) {
+ font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+
+ font_map.erase(p_theme_type);
+
+ _unfreeze_and_propagate_changes();
+}
+
void Theme::get_font_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = font_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) {
+ p_list->push_back(E.key);
}
}
// Font sizes.
void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
bool existing = has_font_size_nocheck(p_name, p_theme_type);
font_size_map[p_theme_type][p_name] = p_font_size;
@@ -577,15 +673,15 @@ void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_ty
int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const {
if (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) {
return font_size_map[p_theme_type][p_name];
- } else if (has_default_theme_font_size()) {
- return default_theme_font_size;
- } else {
+ } else if (has_default_font_size()) {
return default_font_size;
+ } else {
+ return fallback_font_size;
}
}
bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || has_default_theme_font_size());
+ return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || has_default_font_size());
}
bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
@@ -593,6 +689,8 @@ bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_
}
void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot rename the font size '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
ERR_FAIL_COND_MSG(font_size_map[p_theme_type].has(p_name), "Cannot rename the font size '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_old_name), "Cannot rename the font size '" + String(p_old_name) + "' because it does not exist.");
@@ -619,31 +717,41 @@ void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list
return;
}
- const StringName *key = nullptr;
-
- while ((key = font_size_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, int> &E : font_size_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
void Theme::add_font_size_type(const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
if (font_size_map.has(p_theme_type)) {
return;
}
font_size_map[p_theme_type] = HashMap<StringName, int>();
}
+void Theme::remove_font_size_type(const StringName &p_theme_type) {
+ if (!font_size_map.has(p_theme_type)) {
+ return;
+ }
+
+ font_size_map.erase(p_theme_type);
+}
+
void Theme::get_font_size_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = font_size_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : font_size_map) {
+ p_list->push_back(E.key);
}
}
// Colors.
void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
bool existing = has_color_nocheck(p_name, p_theme_type);
color_map[p_theme_type][p_name] = p_color;
@@ -667,6 +775,8 @@ bool Theme::has_color_nocheck(const StringName &p_name, const StringName &p_them
}
void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot rename the color '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
ERR_FAIL_COND_MSG(color_map[p_theme_type].has(p_name), "Cannot rename the color '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_old_name), "Cannot rename the color '" + String(p_old_name) + "' because it does not exist.");
@@ -693,31 +803,41 @@ void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) co
return;
}
- const StringName *key = nullptr;
-
- while ((key = color_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, Color> &E : color_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
void Theme::add_color_type(const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
if (color_map.has(p_theme_type)) {
return;
}
color_map[p_theme_type] = HashMap<StringName, Color>();
}
+void Theme::remove_color_type(const StringName &p_theme_type) {
+ if (!color_map.has(p_theme_type)) {
+ return;
+ }
+
+ color_map.erase(p_theme_type);
+}
+
void Theme::get_color_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = color_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, Color>> &E : color_map) {
+ p_list->push_back(E.key);
}
}
// Theme constants.
void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
bool existing = has_constant_nocheck(p_name, p_theme_type);
constant_map[p_theme_type][p_name] = p_constant;
@@ -741,6 +861,8 @@ bool Theme::has_constant_nocheck(const StringName &p_name, const StringName &p_t
}
void Theme::rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot rename the constant '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
ERR_FAIL_COND_MSG(constant_map[p_theme_type].has(p_name), "Cannot rename the constant '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_old_name), "Cannot rename the constant '" + String(p_old_name) + "' because it does not exist.");
@@ -767,26 +889,33 @@ void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list)
return;
}
- const StringName *key = nullptr;
-
- while ((key = constant_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, int> &E : constant_map[p_theme_type]) {
+ p_list->push_back(E.key);
}
}
void Theme::add_constant_type(const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+
if (constant_map.has(p_theme_type)) {
return;
}
constant_map[p_theme_type] = HashMap<StringName, int>();
}
+void Theme::remove_constant_type(const StringName &p_theme_type) {
+ if (!constant_map.has(p_theme_type)) {
+ return;
+ }
+
+ constant_map.erase(p_theme_type);
+}
+
void Theme::get_constant_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- const StringName *key = nullptr;
- while ((key = constant_map.next(key))) {
- p_list->push_back(*key);
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : constant_map) {
+ p_list->push_back(E.key);
}
}
@@ -997,6 +1126,31 @@ void Theme::add_theme_item_type(DataType p_data_type, const StringName &p_theme_
}
}
+void Theme::remove_theme_item_type(DataType p_data_type, const StringName &p_theme_type) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ remove_color_type(p_theme_type);
+ break;
+ case DATA_TYPE_CONSTANT:
+ remove_constant_type(p_theme_type);
+ break;
+ case DATA_TYPE_FONT:
+ remove_font_type(p_theme_type);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ remove_font_size_type(p_theme_type);
+ break;
+ case DATA_TYPE_ICON:
+ remove_icon_type(p_theme_type);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ remove_stylebox_type(p_theme_type);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const {
switch (p_data_type) {
case DATA_TYPE_COLOR:
@@ -1024,6 +1178,8 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l
// Theme type variations.
void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) {
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
+ ERR_FAIL_COND_MSG(!is_valid_type_name(p_base_type), vformat("Invalid type name: '%s'", p_base_type));
ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type.");
ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type.");
ERR_FAIL_COND_MSG(p_base_type == StringName(), "An empty theme type cannot be the base type of a variation. Use clear_type_variation() instead if you want to unmark '" + String(p_theme_type) + "' as a variation.");
@@ -1081,55 +1237,47 @@ void Theme::get_type_variation_list(const StringName &p_base_type, List<StringNa
}
// Theme types.
-void Theme::get_type_list(List<StringName> *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- Set<StringName> types;
- const StringName *key = nullptr;
-
- // Icons.
- while ((key = icon_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // StyleBoxes.
- while ((key = style_map.next(key))) {
- types.insert(*key);
+void Theme::add_type(const StringName &p_theme_type) {
+ // Add a record to every data type map.
+ for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
+ Theme::DataType dt = (Theme::DataType)i;
+ add_theme_item_type(dt, p_theme_type);
}
- key = nullptr;
+ _emit_theme_changed(true);
+}
- // Fonts.
- while ((key = font_map.next(key))) {
- types.insert(*key);
+void Theme::remove_type(const StringName &p_theme_type) {
+ // Gracefully remove the record from every data type map.
+ for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
+ Theme::DataType dt = (Theme::DataType)i;
+ remove_theme_item_type(dt, p_theme_type);
}
- key = nullptr;
-
- // Font sizes.
- while ((key = font_size_map.next(key))) {
- types.insert(*key);
+ // If type is a variation, remove that connection.
+ if (get_type_variation_base(p_theme_type) != StringName()) {
+ clear_type_variation(p_theme_type);
}
- key = nullptr;
-
- // Colors.
- while ((key = color_map.next(key))) {
- types.insert(*key);
+ // If type is a variation base, remove all those connections.
+ List<StringName> names;
+ get_type_variation_list(p_theme_type, &names);
+ for (const StringName &E : names) {
+ clear_type_variation(E);
}
- key = nullptr;
+ _emit_theme_changed(true);
+}
- // Constants.
- while ((key = constant_map.next(key))) {
- types.insert(*key);
- }
+void Theme::get_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
- for (Set<StringName>::Element *E = types.front(); E; E = E->next()) {
- p_list->push_back(E->get());
- }
+ get_icon_type_list(p_list);
+ get_stylebox_type_list(p_list);
+ get_font_type_list(p_list);
+ get_font_size_type_list(p_list);
+ get_color_type_list(p_list);
+ get_constant_type_list(p_list);
}
void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) {
@@ -1440,75 +1588,62 @@ void Theme::merge_with(const Ref<Theme> &p_other) {
// Colors.
{
- const StringName *K = nullptr;
- while ((K = p_other->color_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->color_map[*K].next(L))) {
- set_color(*L, *K, p_other->color_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, Color>> &E : p_other->color_map) {
+ for (const KeyValue<StringName, Color> &F : E.value) {
+ set_color(F.key, E.key, F.value);
}
}
}
// Constants.
{
- const StringName *K = nullptr;
- while ((K = p_other->constant_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->constant_map[*K].next(L))) {
- set_constant(*L, *K, p_other->constant_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : p_other->constant_map) {
+ for (const KeyValue<StringName, int> &F : E.value) {
+ set_constant(F.key, E.key, F.value);
}
}
}
// Fonts.
{
- const StringName *K = nullptr;
- while ((K = p_other->font_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->font_map[*K].next(L))) {
- set_font(*L, *K, p_other->font_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : p_other->font_map) {
+ for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
+ set_font(F.key, E.key, F.value);
}
}
}
// Font sizes.
{
- const StringName *K = nullptr;
- while ((K = p_other->font_size_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->font_size_map[*K].next(L))) {
- set_font_size(*L, *K, p_other->font_size_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, int>> &E : p_other->font_size_map) {
+ for (const KeyValue<StringName, int> &F : E.value) {
+ set_font_size(F.key, E.key, F.value);
}
}
}
// Icons.
{
- const StringName *K = nullptr;
- while ((K = p_other->icon_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->icon_map[*K].next(L))) {
- set_icon(*L, *K, p_other->icon_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : p_other->icon_map) {
+ for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
+ set_icon(F.key, E.key, F.value);
}
}
}
// Styleboxes.
{
- const StringName *K = nullptr;
- while ((K = p_other->style_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = p_other->style_map[*K].next(L))) {
- set_stylebox(*L, *K, p_other->style_map[*K][*L]);
+ for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : p_other->style_map) {
+ for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
+ set_stylebox(F.key, E.key, F.value);
}
}
}
// Type variations.
{
- const StringName *K = nullptr;
- while ((K = p_other->variation_map.next(K))) {
- set_type_variation(*K, p_other->variation_map[*K]);
+ for (const KeyValue<StringName, StringName> &E : p_other->variation_map) {
+ set_type_variation(E.key, E.value);
}
}
@@ -1518,12 +1653,10 @@ void Theme::merge_with(const Ref<Theme> &p_other) {
void Theme::clear() {
// These items need disconnecting.
{
- const StringName *K = nullptr;
- while ((K = icon_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = icon_map[*K].next(L))) {
- Ref<Texture2D> icon = icon_map[*K][*L];
- if (icon.is_valid()) {
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) {
+ for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
+ if (F.value.is_valid()) {
+ Ref<Texture2D> icon = F.value;
icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
}
@@ -1531,12 +1664,10 @@ void Theme::clear() {
}
{
- const StringName *K = nullptr;
- while ((K = style_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = style_map[*K].next(L))) {
- Ref<StyleBox> style = style_map[*K][*L];
- if (style.is_valid()) {
+ for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) {
+ for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
+ if (F.value.is_valid()) {
+ Ref<StyleBox> style = F.value;
style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
}
@@ -1544,12 +1675,10 @@ void Theme::clear() {
}
{
- const StringName *K = nullptr;
- while ((K = font_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = font_map[*K].next(L))) {
- Ref<Font> font = font_map[*K][*L];
- if (font.is_valid()) {
+ for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) {
+ for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
+ if (F.value.is_valid()) {
+ Ref<Font> font = F.value;
font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
}
@@ -1622,17 +1751,17 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list);
ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list);
- ClassDB::bind_method(D_METHOD("set_default_base_scale", "font_size"), &Theme::set_default_theme_base_scale);
- ClassDB::bind_method(D_METHOD("get_default_base_scale"), &Theme::get_default_theme_base_scale);
- ClassDB::bind_method(D_METHOD("has_default_base_scale"), &Theme::has_default_theme_base_scale);
+ ClassDB::bind_method(D_METHOD("set_default_base_scale", "base_scale"), &Theme::set_default_base_scale);
+ ClassDB::bind_method(D_METHOD("get_default_base_scale"), &Theme::get_default_base_scale);
+ ClassDB::bind_method(D_METHOD("has_default_base_scale"), &Theme::has_default_base_scale);
- ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font);
- ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font);
- ClassDB::bind_method(D_METHOD("has_default_font"), &Theme::has_default_theme_font);
+ ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_font);
+ ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_font);
+ ClassDB::bind_method(D_METHOD("has_default_font"), &Theme::has_default_font);
- ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size);
- ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size);
- ClassDB::bind_method(D_METHOD("has_default_font_size"), &Theme::has_default_theme_font_size);
+ ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_font_size);
+ ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_font_size);
+ ClassDB::bind_method(D_METHOD("has_default_font_size"), &Theme::has_default_font_size);
ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item);
ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item);
@@ -1648,6 +1777,8 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_type_variation_base", "theme_type"), &Theme::get_type_variation_base);
ClassDB::bind_method(D_METHOD("get_type_variation_list", "base_type"), &Theme::_get_type_variation_list);
+ ClassDB::bind_method(D_METHOD("add_type", "theme_type"), &Theme::add_type);
+ ClassDB::bind_method(D_METHOD("remove_type", "theme_type"), &Theme::remove_type);
ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list);
ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with);
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index d170d53ae3..f8f1e95634 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,6 @@
#define THEME_H
#include "core/io/resource.h"
-#include "core/io/resource_loader.h"
#include "scene/resources/font.h"
#include "scene/resources/style_box.h"
#include "scene/resources/texture.h"
@@ -101,16 +100,16 @@ protected:
static Ref<Theme> project_default_theme;
// Universal default values, final fallback for every theme.
- static float default_base_scale;
- static Ref<Texture2D> default_icon;
- static Ref<StyleBox> default_style;
- static Ref<Font> default_font;
- static int default_font_size;
+ static float fallback_base_scale;
+ static Ref<Texture2D> fallback_icon;
+ static Ref<StyleBox> fallback_style;
+ static Ref<Font> fallback_font;
+ static int fallback_font_size;
// Default values configurable for each individual theme.
- float default_theme_base_scale = 0.0;
- Ref<Font> default_theme_font;
- int default_theme_font_size = -1;
+ float default_base_scale = 0.0;
+ Ref<Font> default_font;
+ int default_font_size = -1;
static void _bind_methods();
@@ -126,23 +125,32 @@ public:
static Ref<Theme> get_project_default();
static void set_project_default(const Ref<Theme> &p_project_default);
- static void set_default_base_scale(float p_base_scale);
- static void set_default_icon(const Ref<Texture2D> &p_icon);
- static void set_default_style(const Ref<StyleBox> &p_style);
- static void set_default_font(const Ref<Font> &p_font);
- static void set_default_font_size(int p_font_size);
+ static void set_fallback_base_scale(float p_base_scale);
+ static void set_fallback_icon(const Ref<Texture2D> &p_icon);
+ static void set_fallback_style(const Ref<StyleBox> &p_style);
+ static void set_fallback_font(const Ref<Font> &p_font);
+ static void set_fallback_font_size(int p_font_size);
- void set_default_theme_base_scale(float p_base_scale);
- float get_default_theme_base_scale() const;
- bool has_default_theme_base_scale() const;
+ static float get_fallback_base_scale();
+ static Ref<Texture2D> get_fallback_icon();
+ static Ref<StyleBox> get_fallback_style();
+ static Ref<Font> get_fallback_font();
+ static int get_fallback_font_size();
- void set_default_theme_font(const Ref<Font> &p_default_font);
- Ref<Font> get_default_theme_font() const;
- bool has_default_theme_font() const;
+ static bool is_valid_type_name(const String &p_name);
+ static bool is_valid_item_name(const String &p_name);
- void set_default_theme_font_size(int p_font_size);
- int get_default_theme_font_size() const;
- bool has_default_theme_font_size() const;
+ void set_default_base_scale(float p_base_scale);
+ float get_default_base_scale() const;
+ bool has_default_base_scale() const;
+
+ void set_default_font(const Ref<Font> &p_default_font);
+ Ref<Font> get_default_font() const;
+ bool has_default_font() const;
+
+ void set_default_font_size(int p_font_size);
+ int get_default_font_size() const;
+ bool has_default_font_size() const;
void set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_theme_type) const;
@@ -152,6 +160,7 @@ public:
void clear_icon(const StringName &p_name, const StringName &p_theme_type);
void get_icon_list(StringName p_theme_type, List<StringName> *p_list) const;
void add_icon_type(const StringName &p_theme_type);
+ void remove_icon_type(const StringName &p_theme_type);
void get_icon_type_list(List<StringName> *p_list) const;
void set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style);
@@ -162,6 +171,7 @@ public:
void clear_stylebox(const StringName &p_name, const StringName &p_theme_type);
void get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const;
void add_stylebox_type(const StringName &p_theme_type);
+ void remove_stylebox_type(const StringName &p_theme_type);
void get_stylebox_type_list(List<StringName> *p_list) const;
void set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font);
@@ -172,6 +182,7 @@ public:
void clear_font(const StringName &p_name, const StringName &p_theme_type);
void get_font_list(StringName p_theme_type, List<StringName> *p_list) const;
void add_font_type(const StringName &p_theme_type);
+ void remove_font_type(const StringName &p_theme_type);
void get_font_type_list(List<StringName> *p_list) const;
void set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size);
@@ -182,6 +193,7 @@ public:
void clear_font_size(const StringName &p_name, const StringName &p_theme_type);
void get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const;
void add_font_size_type(const StringName &p_theme_type);
+ void remove_font_size_type(const StringName &p_theme_type);
void get_font_size_type_list(List<StringName> *p_list) const;
void set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color);
@@ -192,6 +204,7 @@ public:
void clear_color(const StringName &p_name, const StringName &p_theme_type);
void get_color_list(StringName p_theme_type, List<StringName> *p_list) const;
void add_color_type(const StringName &p_theme_type);
+ void remove_color_type(const StringName &p_theme_type);
void get_color_type_list(List<StringName> *p_list) const;
void set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant);
@@ -202,6 +215,7 @@ public:
void clear_constant(const StringName &p_name, const StringName &p_theme_type);
void get_constant_list(StringName p_theme_type, List<StringName> *p_list) const;
void add_constant_type(const StringName &p_theme_type);
+ void remove_constant_type(const StringName &p_theme_type);
void get_constant_type_list(List<StringName> *p_list) const;
void set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value);
@@ -212,6 +226,7 @@ public:
void clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type);
void get_theme_item_list(DataType p_data_type, StringName p_theme_type, List<StringName> *p_list) const;
void add_theme_item_type(DataType p_data_type, const StringName &p_theme_type);
+ void remove_theme_item_type(DataType p_data_type, const StringName &p_theme_type);
void get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const;
void set_type_variation(const StringName &p_theme_type, const StringName &p_base_type);
@@ -220,6 +235,8 @@ public:
StringName get_type_variation_base(const StringName &p_theme_type) const;
void get_type_variation_list(const StringName &p_base_type, List<StringName> *p_list) const;
+ void add_type(const StringName &p_theme_type);
+ void remove_type(const StringName &p_theme_type);
void get_type_list(List<StringName> *p_list) const;
void get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variant, List<StringName> *p_list);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 8c92445338..cee3f85c32 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -207,9 +207,6 @@ void TileMapPattern::_get_property_list(List<PropertyInfo> *p_list) const {
}
void TileMapPattern::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_set_tile_data", "data"), &TileMapPattern::_set_tile_data);
- ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMapPattern::_get_tile_data);
-
ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell);
ClassDB::bind_method(D_METHOD("remove_cell", "coords", "update_size"), &TileMapPattern::remove_cell);
@@ -403,7 +400,7 @@ void TileSet::_update_terrains_cache() {
int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
// Executed for each tile_data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ TileData *tile_data = atlas_source->get_tile_data(tile_id, alternative_id);
int terrain_set = tile_data->get_terrain_set();
if (terrain_set >= 0) {
TileMapCell cell;
@@ -560,7 +557,7 @@ void TileSet::move_occlusion_layer(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, occlusion_layers.size());
ERR_FAIL_INDEX(p_to_pos, occlusion_layers.size() + 1);
occlusion_layers.insert(p_to_pos, occlusion_layers[p_from_index]);
- occlusion_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ occlusion_layers.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->move_occlusion_layer(p_from_index, p_to_pos);
}
@@ -570,7 +567,7 @@ void TileSet::move_occlusion_layer(int p_from_index, int p_to_pos) {
void TileSet::remove_occlusion_layer(int p_index) {
ERR_FAIL_INDEX(p_index, occlusion_layers.size());
- occlusion_layers.remove(p_index);
+ occlusion_layers.remove_at(p_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->remove_occlusion_layer(p_index);
}
@@ -623,7 +620,7 @@ void TileSet::move_physics_layer(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, physics_layers.size());
ERR_FAIL_INDEX(p_to_pos, physics_layers.size() + 1);
physics_layers.insert(p_to_pos, physics_layers[p_from_index]);
- physics_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ physics_layers.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->move_physics_layer(p_from_index, p_to_pos);
}
@@ -633,7 +630,7 @@ void TileSet::move_physics_layer(int p_from_index, int p_to_pos) {
void TileSet::remove_physics_layer(int p_index) {
ERR_FAIL_INDEX(p_index, physics_layers.size());
- physics_layers.remove(p_index);
+ physics_layers.remove_at(p_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->remove_physics_layer(p_index);
}
@@ -698,7 +695,7 @@ void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, terrain_sets.size());
ERR_FAIL_INDEX(p_to_pos, terrain_sets.size() + 1);
terrain_sets.insert(p_to_pos, terrain_sets[p_from_index]);
- terrain_sets.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ terrain_sets.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->move_terrain_set(p_from_index, p_to_pos);
}
@@ -709,7 +706,7 @@ void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
void TileSet::remove_terrain_set(int p_index) {
ERR_FAIL_INDEX(p_index, terrain_sets.size());
- terrain_sets.remove(p_index);
+ terrain_sets.remove_at(p_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->remove_terrain_set(p_index);
}
@@ -772,7 +769,7 @@ void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, terrains.size());
ERR_FAIL_INDEX(p_to_pos, terrains.size() + 1);
terrains.insert(p_to_pos, terrains[p_from_index]);
- terrains.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ terrains.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
}
@@ -786,7 +783,7 @@ void TileSet::remove_terrain(int p_terrain_set, int p_index) {
Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
ERR_FAIL_INDEX(p_index, terrains.size());
- terrains.remove(p_index);
+ terrains.remove_at(p_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->remove_terrain(p_terrain_set, p_index);
}
@@ -941,7 +938,7 @@ void TileSet::move_navigation_layer(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, navigation_layers.size());
ERR_FAIL_INDEX(p_to_pos, navigation_layers.size() + 1);
navigation_layers.insert(p_to_pos, navigation_layers[p_from_index]);
- navigation_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ navigation_layers.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->move_navigation_layer(p_from_index, p_to_pos);
}
@@ -951,7 +948,7 @@ void TileSet::move_navigation_layer(int p_from_index, int p_to_pos) {
void TileSet::remove_navigation_layer(int p_index) {
ERR_FAIL_INDEX(p_index, navigation_layers.size());
- navigation_layers.remove(p_index);
+ navigation_layers.remove_at(p_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->remove_navigation_layer(p_index);
}
@@ -994,7 +991,7 @@ void TileSet::move_custom_data_layer(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, custom_data_layers.size());
ERR_FAIL_INDEX(p_to_pos, custom_data_layers.size() + 1);
custom_data_layers.insert(p_to_pos, custom_data_layers[p_from_index]);
- custom_data_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ custom_data_layers.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
for (KeyValue<int, Ref<TileSetSource>> source : sources) {
source.value->move_custom_data_layer(p_from_index, p_to_pos);
}
@@ -1004,7 +1001,7 @@ void TileSet::move_custom_data_layer(int p_from_index, int p_to_pos) {
void TileSet::remove_custom_data_layer(int p_index) {
ERR_FAIL_INDEX(p_index, custom_data_layers.size());
- custom_data_layers.remove(p_index);
+ custom_data_layers.remove_at(p_index);
for (KeyValue<String, int> E : custom_data_layers_by_name) {
if (E.value == p_index) {
custom_data_layers_by_name.erase(E.key);
@@ -1340,7 +1337,7 @@ Ref<TileMapPattern> TileSet::get_pattern(int p_index) {
void TileSet::remove_pattern(int p_index) {
ERR_FAIL_INDEX(p_index, (int)patterns.size());
- patterns.remove(p_index);
+ patterns.remove_at(p_index);
emit_changed();
}
@@ -1348,19 +1345,19 @@ int TileSet::get_patterns_count() {
return patterns.size();
}
-Set<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) {
- ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileSet::TerrainsPattern>());
+RBSet<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), RBSet<TileSet::TerrainsPattern>());
_update_terrains_cache();
- Set<TileSet::TerrainsPattern> output;
- for (KeyValue<TileSet::TerrainsPattern, Set<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) {
+ RBSet<TileSet::TerrainsPattern> output;
+ for (KeyValue<TileSet::TerrainsPattern, RBSet<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) {
output.insert(kv.key);
}
return output;
}
-Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) {
- ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileMapCell>());
+RBSet<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), RBSet<TileMapCell>());
_update_terrains_cache();
return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
}
@@ -1371,13 +1368,13 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti
// Count the sum of probabilities.
double sum = 0.0;
- Set<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
- for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+ RBSet<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
+ for (RBSet<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
if (E->get().source_id >= 0) {
Ref<TileSetSource> source = sources[E->get().source_id];
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile);
sum += tile_data->get_probability();
} else {
sum += 1.0;
@@ -1392,13 +1389,13 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti
double picked = Math::random(0.0, sum);
// Pick the tile.
- for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+ for (RBSet<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
if (E->get().source_id >= 0) {
Ref<TileSetSource> source = sources[E->get().source_id];
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile);
count += tile_data->get_probability();
} else {
count += 1.0;
@@ -1663,7 +1660,7 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ TileData *tile_data = atlas_source->get_tile_data(tile_id, alternative_id);
int terrain_set = tile_data->get_terrain_set();
if (terrain_set >= 0) {
ERR_FAIL_INDEX_V(terrain_set, get_terrain_sets_count(), Vector<Vector<Ref<Texture2D>>>());
@@ -1765,11 +1762,14 @@ Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i
break;
}
bit_rect.position *= Vector2(p_size) / 6.0;
- Vector<Vector2> polygon;
- polygon.push_back(bit_rect.position);
- polygon.push_back(Vector2(bit_rect.get_end().x, bit_rect.position.y));
- polygon.push_back(bit_rect.get_end());
- polygon.push_back(Vector2(bit_rect.position.x, bit_rect.get_end().y));
+
+ Vector<Vector2> polygon = {
+ bit_rect.position,
+ Vector2(bit_rect.get_end().x, bit_rect.position.y),
+ bit_rect.get_end(),
+ Vector2(bit_rect.position.x, bit_rect.get_end().y)
+ };
+
return polygon;
}
@@ -1984,25 +1984,26 @@ Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size,
}
Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
- Vector<Vector2> point_list;
- point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
- point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
- point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0));
- point_list.push_back(Vector2(0, 3));
- point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0));
- point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
- point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
- point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
- point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
- point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(0, -3));
- point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
- point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+ Vector<Vector2> point_list = {
+ Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0),
+ Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)),
+ Vector2(1, 3.0 - p_overlap * 2.0),
+ Vector2(0, 3),
+ Vector2(-1, 3.0 - p_overlap * 2.0),
+ Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)),
+ Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0),
+ Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0),
+ Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)),
+ Vector2(-1, -(3.0 - p_overlap * 2.0)),
+ Vector2(0, -3),
+ Vector2(1, -(3.0 - p_overlap * 2.0)),
+ Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)),
+ Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)
+ };
Vector2 unit = Vector2(p_size) / 6.0;
for (int i = 0; i < point_list.size(); i++) {
@@ -2144,19 +2145,20 @@ Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vect
}
Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
- Vector<Vector2> point_list;
- point_list.push_back(Vector2(3, 0));
- point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
- point_list.push_back(Vector2(0, 3));
- point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
- point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(-3, 0));
- point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
- point_list.push_back(Vector2(0, -3));
- point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
- point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
+ Vector<Vector2> point_list = {
+ Vector2(3, 0),
+ Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0),
+ Vector2(0, 3),
+ Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0),
+ Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(-3, 0),
+ Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0),
+ Vector2(0, -3),
+ Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0),
+ Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))
+ };
Vector2 unit = Vector2(p_size) / 6.0;
for (int i = 0; i < point_list.size(); i++) {
@@ -2250,13 +2252,14 @@ Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_s
}
Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
- Vector<Vector2> point_list;
- point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(0, 3));
- point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
- point_list.push_back(Vector2(0, -3));
- point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
+ Vector<Vector2> point_list = {
+ Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(0, 3),
+ Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)),
+ Vector2(0, -3),
+ Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))
+ };
Vector2 unit = Vector2(p_size) / 6.0;
for (int i = 0; i < point_list.size(); i++) {
@@ -2388,12 +2391,12 @@ void TileSet::_compatibility_conversion() {
value_array.push_back(alternative_tile);
if (!compatibility_tilemap_mapping.has(E.key)) {
- compatibility_tilemap_mapping[E.key] = Map<Array, Array>();
+ compatibility_tilemap_mapping[E.key] = RBMap<Array, Array>();
}
compatibility_tilemap_mapping[E.key][key_array] = value_array;
compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE;
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(coords, alternative_tile);
tile_data->set_flip_h(flip_h);
tile_data->set_flip_v(flip_v);
@@ -2480,12 +2483,12 @@ void TileSet::_compatibility_conversion() {
value_array.push_back(alternative_tile);
if (!compatibility_tilemap_mapping.has(E.key)) {
- compatibility_tilemap_mapping[E.key] = Map<Array, Array>();
+ compatibility_tilemap_mapping[E.key] = RBMap<Array, Array>();
}
compatibility_tilemap_mapping[E.key][key_array] = value_array;
compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE;
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(coords, alternative_tile);
tile_data->set_flip_h(flip_h);
tile_data->set_flip_v(flip_v);
@@ -2568,7 +2571,7 @@ void TileSet::_compatibility_conversion() {
for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
memdelete(E.value);
}
- compatibility_data = Map<int, CompatibilityTileData *>();
+ compatibility_data = HashMap<int, CompatibilityTileData *>();
}
Array TileSet::compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose) {
@@ -2619,12 +2622,12 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
// Get or create the compatibility object
CompatibilityTileData *ctd;
- Map<int, CompatibilityTileData *>::Element *E = compatibility_data.find(id);
+ HashMap<int, CompatibilityTileData *>::Iterator E = compatibility_data.find(id);
if (!E) {
ctd = memnew(CompatibilityTileData);
compatibility_data.insert(id, ctd);
} else {
- ctd = E->get();
+ ctd = E->value;
}
if (components.size() < 2) {
@@ -3096,7 +3099,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
// Terrains.
p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) {
- p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides"));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match Corners and Sides,Match Corners,Match Sides"));
p_list->push_back(PropertyInfo(Variant::NIL, vformat("terrain_set_%d/terrains", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, vformat("terrain_set_%d/terrain_", terrain_set_index)));
for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) {
p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index)));
@@ -3261,16 +3264,10 @@ void TileSet::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping");
ADD_ARRAY("occlusion_layers", "occlusion_layer_");
- ADD_GROUP("Physics", "");
+ ADD_GROUP("", "");
ADD_ARRAY("physics_layers", "physics_layer_");
-
- ADD_GROUP("Terrains", "");
ADD_ARRAY("terrain_sets", "terrain_set_");
-
- ADD_GROUP("Navigation", "");
ADD_ARRAY("navigation_layers", "navigation_layer_");
-
- ADD_GROUP("Custom data", "");
ADD_ARRAY("custom_data_layers", "custom_data_layer_");
// -- Enum binding --
@@ -4117,7 +4114,7 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_
Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2;
margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y));
- Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset();
+ Vector2i effective_texture_offset = get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_offset();
if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) {
effective_texture_offset = effective_texture_offset.clamp(-margin, margin);
}
@@ -4256,7 +4253,7 @@ int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, i
return tiles[p_atlas_coords].alternatives_ids[p_index];
}
-Object *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const {
+TileData *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
@@ -4407,7 +4404,7 @@ void TileSetAtlasSource::_clear_tiles_outside_texture() {
void TileSetAtlasSource::_queue_update_padded_texture() {
padded_texture_needs_update = true;
- call_deferred("_update_padded_texture");
+ call_deferred(SNAME("_update_padded_texture"));
}
void TileSetAtlasSource::_update_padded_texture() {
@@ -4429,9 +4426,13 @@ void TileSetAtlasSource::_update_padded_texture() {
Ref<Image> src = texture->get_image();
+ if (!src.is_valid()) {
+ return;
+ }
+
Ref<Image> image;
image.instantiate();
- image->create(size.x, size.y, false, Image::FORMAT_RGBA8);
+ image->create(size.x, size.y, false, src->get_format());
for (KeyValue<Vector2i, TileAlternativesData> kv : tiles) {
for (int frame = 0; frame < (int)kv.value.animation_frames_durations.size(); frame++) {
@@ -4707,12 +4708,12 @@ void TileData::move_occlusion_layer(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, occluders.size());
ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1);
occluders.insert(p_to_pos, occluders[p_from_index]);
- occluders.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ occluders.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
}
void TileData::remove_occlusion_layer(int p_index) {
ERR_FAIL_INDEX(p_index, occluders.size());
- occluders.remove(p_index);
+ occluders.remove_at(p_index);
}
void TileData::add_physics_layer(int p_to_pos) {
@@ -4727,12 +4728,12 @@ void TileData::move_physics_layer(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, physics.size());
ERR_FAIL_INDEX(p_to_pos, physics.size() + 1);
physics.insert(p_to_pos, physics[p_from_index]);
- physics.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ physics.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
}
void TileData::remove_physics_layer(int p_index) {
ERR_FAIL_INDEX(p_index, physics.size());
- physics.remove(p_index);
+ physics.remove_at(p_index);
}
void TileData::add_terrain_set(int p_to_pos) {
@@ -4816,12 +4817,12 @@ void TileData::move_navigation_layer(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, navigation.size());
ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1);
navigation.insert(p_to_pos, navigation[p_from_index]);
- navigation.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ navigation.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
}
void TileData::remove_navigation_layer(int p_index) {
ERR_FAIL_INDEX(p_index, navigation.size());
- navigation.remove(p_index);
+ navigation.remove_at(p_index);
}
void TileData::add_custom_data_layer(int p_to_pos) {
@@ -4836,12 +4837,12 @@ void TileData::move_custom_data_layer(int p_from_index, int p_to_pos) {
ERR_FAIL_INDEX(p_from_index, custom_data.size());
ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1);
custom_data.insert(p_to_pos, navigation[p_from_index]);
- custom_data.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ custom_data.remove_at(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
}
void TileData::remove_custom_data_layer(int p_index) {
ERR_FAIL_INDEX(p_index, custom_data.size());
- custom_data.remove(p_index);
+ custom_data.remove_at(p_index);
}
void TileData::reset_state() {
@@ -5019,7 +5020,7 @@ void TileData::add_collision_polygon(int p_layer_id) {
void TileData::remove_collision_polygon(int p_layer_id, int p_polygon_index) {
ERR_FAIL_INDEX(p_layer_id, physics.size());
ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size());
- physics.write[p_layer_id].polygons.remove(p_polygon_index);
+ physics.write[p_layer_id].polygons.remove_at(p_polygon_index);
emit_signal(SNAME("changed"));
}
@@ -5086,7 +5087,7 @@ int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_i
}
Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const {
- ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref<ConvexPolygonShape2D>());
ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>());
ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[p_polygon_index].shapes.size(), Ref<ConvexPolygonShape2D>());
return physics[p_layer_id].polygons[p_polygon_index].shapes[shape_index];
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index d2238c26d2..633e1f4bed 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -69,6 +69,11 @@ union TileMapCell {
};
uint64_t _u64t;
+
+ static uint32_t hash(const TileMapCell &p_hash) {
+ return hash_one_uint64(p_hash._u64t);
+ }
+
TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = Vector2i(-1, -1), int p_alternative_tile = -1) { // default are INVALID_SOURCE, INVALID_ATLAS_COORDS, INVALID_TILE_ALTERNATIVE
source_id = p_source_id;
set_atlas_coords(p_atlas_coords);
@@ -103,13 +108,16 @@ union TileMapCell {
bool operator!=(const TileMapCell &p_other) const {
return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile);
}
+ bool operator==(const TileMapCell &p_other) const {
+ return source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile;
+ }
};
class TileMapPattern : public Resource {
GDCLASS(TileMapPattern, Resource);
Vector2i size;
- Map<Vector2i, TileMapCell> pattern;
+ HashMap<Vector2i, TileMapCell> pattern;
void _set_tile_data(const Vector<int> &p_data);
Vector<int> _get_tile_data() const;
@@ -166,11 +174,11 @@ private:
Size2i autotile_tile_size = Size2i(16, 16);
int autotile_spacing = 0;
- Map<Vector2i, int> autotile_bitmask_flags;
- Map<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map;
- Map<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map;
- Map<Vector2i, int> autotile_priority_map;
- Map<Vector2i, int> autotile_z_index_map;
+ HashMap<Vector2i, int> autotile_bitmask_flags;
+ HashMap<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map;
+ HashMap<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map;
+ HashMap<Vector2i, int> autotile_priority_map;
+ HashMap<Vector2i, int> autotile_z_index_map;
Vector<CompatibilityShapeData> shapes;
Ref<OccluderPolygon2D> occluder;
@@ -186,9 +194,9 @@ private:
COMPATIBILITY_TILE_MODE_ATLAS_TILE,
};
- Map<int, CompatibilityTileData *> compatibility_data;
- Map<int, int> compatibility_tilemap_mapping_tile_modes;
- Map<int, Map<Array, Array>> compatibility_tilemap_mapping;
+ HashMap<int, CompatibilityTileData *> compatibility_data;
+ HashMap<int, int> compatibility_tilemap_mapping_tile_modes;
+ HashMap<int, RBMap<Array, Array>> compatibility_tilemap_mapping;
void _compatibility_conversion();
@@ -324,10 +332,10 @@ private:
};
Vector<TerrainSet> terrain_sets;
- Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes;
+ HashMap<TerrainMode, HashMap<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes;
bool terrain_bits_meshes_dirty = true;
- LocalVector<Map<TileSet::TerrainsPattern, Set<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
+ LocalVector<RBMap<TileSet::TerrainsPattern, RBSet<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
bool terrains_cache_dirty = true;
void _update_terrains_cache();
@@ -343,10 +351,10 @@ private:
Variant::Type type = Variant::NIL;
};
Vector<CustomDataLayer> custom_data_layers;
- Map<String, int> custom_data_layers_by_name;
+ HashMap<String, int> custom_data_layers_by_name;
// Per Atlas source data.
- Map<int, Ref<TileSetSource>> sources;
+ HashMap<int, Ref<TileSetSource>> sources;
Vector<int> source_ids;
int next_source_id = 0;
// ---------------------
@@ -357,9 +365,9 @@ private:
void _source_changed();
// Tile proxies
- Map<int, int> source_level_proxies;
- Map<Array, Array> coords_level_proxies;
- Map<Array, Array> alternative_level_proxies;
+ RBMap<int, int> source_level_proxies;
+ RBMap<Array, Array> coords_level_proxies;
+ RBMap<Array, Array> alternative_level_proxies;
// Helpers
Vector<Point2> _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
@@ -499,8 +507,8 @@ public:
int get_patterns_count();
// Terrains.
- Set<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set);
- Set<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
+ RBSet<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set);
+ RBSet<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
// Helpers
@@ -579,7 +587,7 @@ private:
LocalVector<real_t> animation_frames_durations;
// Alternatives
- Map<int, TileData *> alternatives;
+ HashMap<int, TileData *> alternatives;
Vector<int> alternatives_ids;
int next_alternative_id = 1;
};
@@ -589,9 +597,9 @@ private:
Vector2i separation;
Size2i texture_region_size = Size2i(16, 16);
- Map<Vector2i, TileAlternativesData> tiles;
+ HashMap<Vector2i, TileAlternativesData> tiles;
Vector<Vector2i> tiles_ids;
- Map<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile
+ HashMap<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile
TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile);
const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const;
@@ -693,7 +701,7 @@ public:
virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
// Get data associated to a tile.
- Object *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
+ TileData *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
// Helpers.
Vector2i get_atlas_grid_size() const;
@@ -716,7 +724,7 @@ private:
bool display_placeholder = false;
};
Vector<int> scenes_ids;
- Map<int, SceneData> scenes;
+ HashMap<int, SceneData> scenes;
int next_scene_id = 1;
void _compute_next_alternative_id();
diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h
index f960f85521..3e154d539b 100644
--- a/scene/resources/video_stream.h
+++ b/scene/resources/video_stream.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 87ff225de9..8e7824665f 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -53,8 +53,139 @@ int VisualShaderNode::get_output_port_for_preview() const {
return port_preview;
}
-void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p_value) {
- default_input_values[p_port] = p_value;
+void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value) {
+ Variant value = p_value;
+
+ if (p_prev_value.get_type() != Variant::NIL) {
+ switch (p_value.get_type()) {
+ case Variant::FLOAT: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ value = (float)p_prev_value;
+ } break;
+ case Variant::FLOAT: {
+ value = p_prev_value;
+ } break;
+ case Variant::VECTOR2: {
+ Vector2 pv = p_prev_value;
+ value = pv.x;
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 pv = p_prev_value;
+ value = pv.x;
+ } break;
+ case Variant::QUATERNION: {
+ Quaternion pv = p_prev_value;
+ value = pv.x;
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case Variant::INT: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ value = p_prev_value;
+ } break;
+ case Variant::FLOAT: {
+ value = (int)p_prev_value;
+ } break;
+ case Variant::VECTOR2: {
+ Vector2 pv = p_prev_value;
+ value = (int)pv.x;
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 pv = p_prev_value;
+ value = (int)pv.x;
+ } break;
+ case Variant::QUATERNION: {
+ Quaternion pv = p_prev_value;
+ value = (int)pv.x;
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case Variant::VECTOR2: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ float pv = (float)(int)p_prev_value;
+ value = Vector2(pv, pv);
+ } break;
+ case Variant::FLOAT: {
+ float pv = p_prev_value;
+ value = Vector2(pv, pv);
+ } break;
+ case Variant::VECTOR2: {
+ value = p_prev_value;
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 pv = p_prev_value;
+ value = Vector2(pv.x, pv.y);
+ } break;
+ case Variant::QUATERNION: {
+ Quaternion pv = p_prev_value;
+ value = Vector2(pv.x, pv.y);
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case Variant::VECTOR3: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ float pv = (float)(int)p_prev_value;
+ value = Vector3(pv, pv, pv);
+ } break;
+ case Variant::FLOAT: {
+ float pv = p_prev_value;
+ value = Vector3(pv, pv, pv);
+ } break;
+ case Variant::VECTOR2: {
+ Vector2 pv = p_prev_value;
+ value = Vector3(pv.x, pv.y, pv.y);
+ } break;
+ case Variant::VECTOR3: {
+ value = p_prev_value;
+ } break;
+ case Variant::QUATERNION: {
+ Quaternion pv = p_prev_value;
+ value = Vector3(pv.x, pv.y, pv.z);
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case Variant::QUATERNION: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ float pv = (float)(int)p_prev_value;
+ value = Quaternion(pv, pv, pv, pv);
+ } break;
+ case Variant::FLOAT: {
+ float pv = p_prev_value;
+ value = Quaternion(pv, pv, pv, pv);
+ } break;
+ case Variant::VECTOR2: {
+ Vector2 pv = p_prev_value;
+ value = Quaternion(pv.x, pv.y, pv.y, pv.y);
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 pv = p_prev_value;
+ value = Quaternion(pv.x, pv.y, pv.z, pv.z);
+ } break;
+ case Variant::QUATERNION: {
+ value = p_prev_value;
+ } break;
+ default:
+ break;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ default_input_values[p_port] = value;
emit_changed();
}
@@ -156,8 +287,18 @@ int VisualShaderNode::get_expanded_output_port_count() const {
int count2 = count;
for (int i = 0; i < count; i++) {
if (is_output_port_expandable(i) && _is_output_port_expanded(i)) {
- if (get_output_port_type(i) == PORT_TYPE_VECTOR) {
- count2 += 3;
+ switch (get_output_port_type(i)) {
+ case PORT_TYPE_VECTOR_2D: {
+ count2 += 2;
+ } break;
+ case PORT_TYPE_VECTOR_3D: {
+ count2 += 3;
+ } break;
+ case PORT_TYPE_VECTOR_4D: {
+ count2 += 4;
+ } break;
+ default:
+ break;
}
}
}
@@ -192,7 +333,7 @@ String VisualShaderNode::generate_global(Shader::Mode p_mode, VisualShader::Type
return String();
}
-String VisualShaderNode::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNode::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
return String();
}
@@ -204,8 +345,8 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const {
return Vector<StringName>();
}
-Map<StringName, String> VisualShaderNode::get_editable_properties_names() const {
- return Map<StringName, String>();
+HashMap<StringName, String> VisualShaderNode::get_editable_properties_names() const {
+ return HashMap<StringName, String>();
}
Array VisualShaderNode::get_default_input_values() const {
@@ -231,8 +372,8 @@ String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_t
return String();
}
-String VisualShaderNode::get_input_port_default_hint(int p_port) const {
- return "";
+bool VisualShaderNode::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ return false;
}
void VisualShaderNode::_bind_methods() {
@@ -245,7 +386,7 @@ void VisualShaderNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_output_ports_expanded", "values"), &VisualShaderNode::_set_output_ports_expanded);
ClassDB::bind_method(D_METHOD("_get_output_ports_expanded"), &VisualShaderNode::_get_output_ports_expanded);
- ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value);
+ ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value", "prev_value"), &VisualShaderNode::set_input_port_default_value, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value);
ClassDB::bind_method(D_METHOD("remove_input_port_default_value", "port"), &VisualShaderNode::remove_input_port_default_value);
@@ -261,7 +402,9 @@ void VisualShaderNode::_bind_methods() {
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR);
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR_INT);
- BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_4D);
BIND_ENUM_CONSTANT(PORT_TYPE_BOOLEAN);
BIND_ENUM_CONSTANT(PORT_TYPE_TRANSFORM);
BIND_ENUM_CONSTANT(PORT_TYPE_SAMPLER);
@@ -349,45 +492,82 @@ String VisualShaderNodeCustom::get_output_port_name(int p_port) const {
String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
ERR_FAIL_COND_V(!GDVIRTUAL_IS_OVERRIDDEN(_get_code), "");
- Vector<String> input_vars;
+ TypedArray<String> input_vars;
for (int i = 0; i < get_input_port_count(); i++) {
input_vars.push_back(p_input_vars[i]);
}
- Array output_vars;
+ TypedArray<String> output_vars;
for (int i = 0; i < get_output_port_count(); i++) {
output_vars.push_back(p_output_vars[i]);
}
- String code = " {\n";
+
String _code;
GDVIRTUAL_CALL(_get_code, input_vars, output_vars, p_mode, p_type, _code);
- bool nend = _code.ends_with("\n");
- _code = _code.insert(0, " ");
- _code = _code.replace("\n", "\n ");
- code += _code;
- if (!nend) {
- code += "\n }";
- } else {
- code.remove(code.size() - 1);
- code += "}";
+ if (_is_valid_code(_code)) {
+ String code = " {\n";
+ bool nend = _code.ends_with("\n");
+ _code = _code.insert(0, " ");
+ _code = _code.replace("\n", "\n ");
+ code += _code;
+ if (!nend) {
+ code += "\n }";
+ } else {
+ code.remove_at(code.size() - 1);
+ code += "}";
+ }
+ code += "\n";
+ return code;
}
- code += "\n";
- return code;
+ return String();
}
-String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
- String ret;
- if (GDVIRTUAL_CALL(_get_global_code, p_mode, ret)) {
- String code = "// " + get_caption() + "\n";
- code += ret;
- code += "\n";
- return code;
+String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
+ String _code;
+ if (GDVIRTUAL_CALL(_get_global_code, p_mode, _code)) {
+ if (_is_valid_code(_code)) {
+ String code = "// " + get_caption() + "\n";
+ code += _code;
+ code += "\n";
+ return code;
+ }
}
- return "";
+ return String();
+}
+
+String VisualShaderNodeCustom::generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String _code;
+ if (GDVIRTUAL_CALL(_get_func_code, p_mode, p_type, _code)) {
+ if (_is_valid_code(_code)) {
+ bool nend = _code.ends_with("\n");
+ String code = "// " + get_caption() + "\n";
+ code += " {\n";
+ _code = _code.insert(0, " ");
+ _code = _code.replace("\n", "\n ");
+ code += _code;
+ if (!nend) {
+ code += "\n }";
+ } else {
+ code.remove_at(code.size() - 1);
+ code += "}";
+ }
+ code += "\n";
+ return code;
+ }
+ }
+ return String();
}
-void VisualShaderNodeCustom::set_input_port_default_value(int p_port, const Variant &p_value) {
+bool VisualShaderNodeCustom::is_available(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ bool ret;
+ if (GDVIRTUAL_CALL(_is_available, p_mode, p_type, ret)) {
+ return ret;
+ }
+ return true;
+}
+
+void VisualShaderNodeCustom::set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value) {
if (!is_initialized) {
- VisualShaderNode::set_input_port_default_value(p_port, p_value);
+ VisualShaderNode::set_input_port_default_value(p_port, p_value, p_prev_value);
}
}
@@ -413,6 +593,13 @@ void VisualShaderNodeCustom::_set_input_port_default_value(int p_port, const Var
VisualShaderNode::set_input_port_default_value(p_port, p_value);
}
+bool VisualShaderNodeCustom::_is_valid_code(const String &p_code) const {
+ if (p_code.is_empty() || p_code == "null") {
+ return false;
+ }
+ return true;
+}
+
bool VisualShaderNodeCustom::_is_initialized() {
return is_initialized;
}
@@ -433,8 +620,10 @@ void VisualShaderNodeCustom::_bind_methods() {
GDVIRTUAL_BIND(_get_output_port_type, "port");
GDVIRTUAL_BIND(_get_output_port_name, "port");
GDVIRTUAL_BIND(_get_code, "input_vars", "output_vars", "mode", "type");
+ GDVIRTUAL_BIND(_get_func_code, "mode", "type");
GDVIRTUAL_BIND(_get_global_code, "mode");
GDVIRTUAL_BIND(_is_highend);
+ GDVIRTUAL_BIND(_is_available, "mode", "type");
ClassDB::bind_method(D_METHOD("_set_initialized", "enabled"), &VisualShaderNodeCustom::_set_initialized);
ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized);
@@ -457,6 +646,72 @@ VisualShader::Type VisualShader::get_shader_type() const {
return current_type;
}
+void VisualShader::add_varying(const String &p_name, VaryingMode p_mode, VaryingType p_type) {
+ ERR_FAIL_COND(!p_name.is_valid_identifier());
+ ERR_FAIL_INDEX((int)p_mode, (int)VARYING_MODE_MAX);
+ ERR_FAIL_INDEX((int)p_type, (int)VARYING_TYPE_MAX);
+ ERR_FAIL_COND(varyings.has(p_name));
+ Varying var = Varying(p_name, p_mode, p_type);
+ varyings[p_name] = var;
+ varyings_list.push_back(var);
+ _queue_update();
+}
+
+void VisualShader::remove_varying(const String &p_name) {
+ ERR_FAIL_COND(!varyings.has(p_name));
+ varyings.erase(p_name);
+ for (List<Varying>::Element *E = varyings_list.front(); E; E = E->next()) {
+ if (E->get().name == p_name) {
+ varyings_list.erase(E);
+ break;
+ }
+ }
+ _queue_update();
+}
+
+bool VisualShader::has_varying(const String &p_name) const {
+ return varyings.has(p_name);
+}
+
+int VisualShader::get_varyings_count() const {
+ return varyings_list.size();
+}
+
+const VisualShader::Varying *VisualShader::get_varying_by_index(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, varyings_list.size(), nullptr);
+ return &varyings_list[p_idx];
+}
+
+void VisualShader::set_varying_mode(const String &p_name, VaryingMode p_mode) {
+ ERR_FAIL_INDEX((int)p_mode, (int)VARYING_MODE_MAX);
+ ERR_FAIL_COND(!varyings.has(p_name));
+ if (varyings[p_name].mode == p_mode) {
+ return;
+ }
+ varyings[p_name].mode = p_mode;
+ _queue_update();
+}
+
+VisualShader::VaryingMode VisualShader::get_varying_mode(const String &p_name) {
+ ERR_FAIL_COND_V(!varyings.has(p_name), VARYING_MODE_MAX);
+ return varyings[p_name].mode;
+}
+
+void VisualShader::set_varying_type(const String &p_name, VaryingType p_type) {
+ ERR_FAIL_INDEX((int)p_type, (int)VARYING_TYPE_MAX);
+ ERR_FAIL_COND(!varyings.has(p_name));
+ if (varyings[p_name].type == p_type) {
+ return;
+ }
+ varyings[p_name].type = p_type;
+ _queue_update();
+}
+
+VisualShader::VaryingType VisualShader::get_varying_type(const String &p_name) {
+ ERR_FAIL_COND_V(!varyings.has(p_name), VARYING_TYPE_MAX);
+ return varyings[p_name].type;
+}
+
void VisualShader::set_engine_version(const Dictionary &p_engine_version) {
ERR_FAIL_COND(!p_engine_version.has("major"));
ERR_FAIL_COND(!p_engine_version.has("minor"));
@@ -484,24 +739,48 @@ void VisualShader::update_engine_version(const Dictionary &p_new_version) {
if (expression.is_valid()) {
for (int j = 0; j < expression->get_input_port_count(); j++) {
int type = expression->get_input_port_type(j);
- if (type > 0) { // + PORT_TYPE_SCALAR_INT
- type += 1;
+ if (type > 0) { // + PORT_TYPE_SCALAR_INT + PORT_TYPE_VECTOR_2D
+ type += 2;
}
expression->set_input_port_type(j, type);
}
for (int j = 0; j < expression->get_output_port_count(); j++) {
int type = expression->get_output_port_type(j);
- if (type > 0) { // + PORT_TYPE_SCALAR_INT
- type += 1;
+ if (type > 0) { // + PORT_TYPE_SCALAR_INT + PORT_TYPE_VECTOR_2D
+ type += 2;
}
expression->set_output_port_type(j, type);
}
}
+ Ref<VisualShaderNodeStep> step = Object::cast_to<VisualShaderNodeStep>(E.value.node.ptr());
+ if (step.is_valid()) {
+ int op_type = int(step->get_op_type());
+ if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR
+ op_type += 2;
+ }
+ step->set_op_type(VisualShaderNodeStep::OpType(op_type));
+ }
+ Ref<VisualShaderNodeSmoothStep> sstep = Object::cast_to<VisualShaderNodeSmoothStep>(E.value.node.ptr());
+ if (sstep.is_valid()) {
+ int op_type = int(sstep->get_op_type());
+ if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR
+ op_type += 2;
+ }
+ sstep->set_op_type(VisualShaderNodeSmoothStep::OpType(op_type));
+ }
+ Ref<VisualShaderNodeMix> mix = Object::cast_to<VisualShaderNodeMix>(E.value.node.ptr());
+ if (mix.is_valid()) {
+ int op_type = int(mix->get_op_type());
+ if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR
+ op_type += 2;
+ }
+ mix->set_op_type(VisualShaderNodeMix::OpType(op_type));
+ }
Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E.value.node.ptr());
if (compare.is_valid()) {
int ctype = int(compare->get_comparison_type());
- if (int(ctype) > 0) { // + PORT_TYPE_SCALAR_INT
- ctype += 1;
+ if (int(ctype) > 0) { // + CTYPE_SCALAR_INT + CTYPE_VECTOR_2D
+ ctype += 2;
}
compare->set_comparison_type(VisualShaderNodeCompare::ComparisonType(ctype));
}
@@ -533,7 +812,6 @@ void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, co
if (input.is_valid()) {
input->shader_mode = shader_mode;
input->shader_type = p_type;
- input->connect("input_type_changed", callable_mp(this, &VisualShader::_input_type_changed), varray(p_type, p_id));
}
n.node->connect("changed", callable_mp(this, &VisualShader::_queue_update));
@@ -603,11 +881,6 @@ void VisualShader::remove_node(Type p_type, int p_id) {
Graph *g = &graph[p_type];
ERR_FAIL_COND(!g->nodes.has(p_id));
- Ref<VisualShaderNodeInput> input = g->nodes[p_id].node;
- if (input.is_valid()) {
- input->disconnect("input_type_changed", callable_mp(this, &VisualShader::_input_type_changed));
- }
-
g->nodes[p_id].node->disconnect("changed", callable_mp(this, &VisualShader::_queue_update));
g->nodes.erase(p_id);
@@ -719,7 +992,7 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
}
bool VisualShader::is_port_types_compatible(int p_a, int p_b) const {
- return MAX(0, p_a - 3) == (MAX(0, p_b - 3));
+ return MAX(0, p_a - 4) == (MAX(0, p_b - 4));
}
void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
@@ -905,16 +1178,16 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
StringBuilder global_code;
StringBuilder global_code_per_node;
- Map<Type, StringBuilder> global_code_per_func;
+ HashMap<Type, StringBuilder> global_code_per_func;
StringBuilder code;
- Set<StringName> classes;
+ RBSet<StringName> classes;
global_code += String() + "shader_type canvas_item;\n";
String global_expressions;
for (int i = 0, index = 0; i < TYPE_MAX; i++) {
- for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
- Ref<VisualShaderNodeGlobalExpression> global_expression = Object::cast_to<VisualShaderNodeGlobalExpression>(E->get().node.ptr());
+ for (const KeyValue<int, Node> &E : graph[i].nodes) {
+ Ref<VisualShaderNodeGlobalExpression> global_expression = E.value.node;
if (global_expression.is_valid()) {
String expr = "";
expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n";
@@ -949,19 +1222,34 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
code += "\nvoid fragment() {\n";
- Set<int> processed;
- Error err = _write_node(p_type, global_code, global_code_per_node, global_code_per_func, code, default_tex_params, input_connections, output_connections, p_node, processed, true, classes);
+ RBSet<int> processed;
+ Error err = _write_node(p_type, &global_code, &global_code_per_node, &global_code_per_func, code, default_tex_params, input_connections, output_connections, p_node, processed, true, classes);
ERR_FAIL_COND_V(err != OK, String());
- if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) {
- code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " );\n";
- } else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- code += " COLOR.rgb = vec3(float(n_out" + itos(p_node) + "p" + itos(p_port) + "));\n";
- } else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0);\n";
- } else {
- code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
+ switch (node->get_output_port_type(p_port)) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + ");\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ code += " COLOR.rgb = vec3(float(n_out" + itos(p_node) + "p" + itos(p_port) + "));\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0);\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + ", 0.0);\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ".xyz;\n";
+ } break;
+ default: {
+ code += " COLOR.rgb = vec3(0.0);\n";
+ } break;
}
+
code += "}\n";
//set code secretly
@@ -972,26 +1260,22 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
return final_code;
}
-#define IS_INITIAL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z'))
-
-#define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_')
-
String VisualShader::validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const {
String name = p_port_name;
- if (name == String()) {
+ if (name.is_empty()) {
return String();
}
- while (name.length() && !IS_INITIAL_CHAR(name[0])) {
+ while (name.length() && !is_ascii_char(name[0])) {
name = name.substr(1, name.length() - 1);
}
- if (name != String()) {
+ if (!name.is_empty()) {
String valid_name;
for (int i = 0; i < name.length(); i++) {
- if (IS_SYMBOL_CHAR(name[i])) {
+ if (is_ascii_identifier_char(name[i])) {
valid_name += String::chr(name[i]);
} else if (name[i] == ' ') {
valid_name += "_";
@@ -1028,14 +1312,14 @@ String VisualShader::validate_port_name(const String &p_port_name, VisualShaderN
String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const {
String name = p_name; //validate name first
- while (name.length() && !IS_INITIAL_CHAR(name[0])) {
+ while (name.length() && !is_ascii_char(name[0])) {
name = name.substr(1, name.length() - 1);
}
- if (name != String()) {
+ if (!name.is_empty()) {
String valid_name;
for (int i = 0; i < name.length(); i++) {
- if (IS_SYMBOL_CHAR(name[i])) {
+ if (is_ascii_identifier_char(name[i])) {
valid_name += String::chr(name[i]);
} else if (name[i] == ' ') {
valid_name += "_";
@@ -1045,7 +1329,7 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua
name = valid_name;
}
- if (name == String()) {
+ if (name.is_empty()) {
name = p_uniform->get_caption();
}
@@ -1072,10 +1356,10 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua
if (exists) {
//remove numbers, put new and try again
attempt++;
- while (name.length() && name[name.length() - 1] >= '0' && name[name.length() - 1] <= '9') {
+ while (name.length() && is_digit(name[name.length() - 1])) {
name = name.substr(0, name.length() - 1);
}
- ERR_FAIL_COND_V(name == String(), String());
+ ERR_FAIL_COND_V(name.is_empty(), String());
name += itos(attempt);
} else {
break;
@@ -1085,16 +1369,6 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua
return name;
}
-VisualShader::RenderModeEnums VisualShader::render_mode_enums[] = {
- { Shader::MODE_SPATIAL, "blend" },
- { Shader::MODE_SPATIAL, "depth_draw" },
- { Shader::MODE_SPATIAL, "cull" },
- { Shader::MODE_SPATIAL, "diffuse" },
- { Shader::MODE_SPATIAL, "specular" },
- { Shader::MODE_CANVAS_ITEM, "blend" },
- { Shader::MODE_CANVAS_ITEM, nullptr }
-};
-
static const char *type_string[VisualShader::TYPE_MAX] = {
"vertex",
"fragment",
@@ -1133,6 +1407,16 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
}
_queue_update();
return true;
+ } else if (name.begins_with("varyings/")) {
+ String var_name = name.get_slicec('/', 1);
+ Varying value = Varying();
+ value.name = var_name;
+ if (value.from_string(p_value) && !varyings.has(var_name)) {
+ varyings[var_name] = value;
+ varyings_list.push_back(value);
+ }
+ _queue_update();
+ return true;
} else if (name.begins_with("nodes/")) {
String typestr = name.get_slicec('/', 1);
Type type = TYPE_VERTEX;
@@ -1197,6 +1481,14 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = 0;
}
return true;
+ } else if (name.begins_with("varyings/")) {
+ String var_name = name.get_slicec('/', 1);
+ if (varyings.has(var_name)) {
+ r_ret = varyings[var_name].to_string();
+ } else {
+ r_ret = String();
+ }
+ return true;
} else if (name.begins_with("nodes/")) {
String typestr = name.get_slicec('/', 1);
Type type = TYPE_VERTEX;
@@ -1258,30 +1550,28 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog"));
//render modes
- Map<String, String> blend_mode_enums;
- Set<String> toggles;
+ HashMap<String, String> blend_mode_enums;
+ RBSet<String> toggles;
+
+ const Vector<ShaderLanguage::ModeInfo> &rmodes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode));
+
+ for (int i = 0; i < rmodes.size(); i++) {
+ const ShaderLanguage::ModeInfo &info = rmodes[i];
+
+ if (!info.options.is_empty()) {
+ const String begin = String(info.name);
+
+ for (int j = 0; j < info.options.size(); j++) {
+ const String option = String(info.options[j]).capitalize();
- for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)).size(); i++) {
- String mode = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode))[i];
- int idx = 0;
- bool in_enum = false;
- while (render_mode_enums[idx].string) {
- if (mode.begins_with(render_mode_enums[idx].string)) {
- String begin = render_mode_enums[idx].string;
- String option = mode.replace_first(begin + "_", "");
if (!blend_mode_enums.has(begin)) {
blend_mode_enums[begin] = option;
} else {
blend_mode_enums[begin] += "," + option;
}
- in_enum = true;
- break;
}
- idx++;
- }
-
- if (!in_enum) {
- toggles.insert(mode);
+ } else {
+ toggles.insert(String(info.name));
}
}
@@ -1289,10 +1579,14 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E.key, PROPERTY_HINT_ENUM, E.value));
}
- for (Set<String>::Element *E = toggles.front(); E; E = E->next()) {
+ for (RBSet<String>::Element *E = toggles.front(); E; E = E->next()) {
p_list->push_back(PropertyInfo(Variant::BOOL, "flags/" + E->get()));
}
+ for (const KeyValue<String, Varying> &E : varyings) {
+ p_list->push_back(PropertyInfo(Variant::STRING, "varyings/" + E.key));
+ }
+
for (int i = 0; i < TYPE_MAX; i++) {
for (const KeyValue<int, Node> &E : graph[i].nodes) {
String prop_name = "nodes/";
@@ -1317,7 +1611,7 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
-Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &global_code_per_node, Map<Type, StringBuilder> &global_code_per_func, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const {
+Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBuilder *global_code_per_node, HashMap<Type, StringBuilder> *global_code_per_func, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, RBSet<int> &processed, bool for_preview, RBSet<StringName> &r_classes) const {
const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node;
if (vsnode->is_disabled()) {
@@ -1359,7 +1653,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
if (!skip_global) {
Ref<VisualShaderNodeUniform> uniform = vsnode;
if (!uniform.is_valid() || !uniform->is_global_code_generated()) {
- global_code += vsnode->generate_global(get_mode(), type, node);
+ if (global_code) {
+ *global_code += vsnode->generate_global(get_mode(), type, node);
+ }
}
String class_name = vsnode->get_class_name();
@@ -1367,9 +1663,13 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
class_name = vsnode->get_script_instance()->get_script()->get_path();
}
if (!r_classes.has(class_name)) {
- global_code_per_node += vsnode->generate_global_per_node(get_mode(), type, node);
+ if (global_code_per_node) {
+ *global_code_per_node += vsnode->generate_global_per_node(get_mode(), node);
+ }
for (int i = 0; i < TYPE_MAX; i++) {
- global_code_per_func[Type(i)] += vsnode->generate_global_per_func(get_mode(), Type(i), node);
+ if (global_code_per_func) {
+ (*global_code_per_func)[Type(i)] += vsnode->generate_global_per_func(get_mode(), Type(i), node);
+ }
}
r_classes.insert(class_name);
}
@@ -1418,30 +1718,136 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
}
} else if (in_type == out_type) {
inputs[i] = src_var;
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
- inputs[i] = "dot(" + src_var + ", vec3(0.333333, 0.333333, 0.333333))";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR_INT && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
- inputs[i] = "dot(float(" + src_var + "), vec3(0.333333, 0.333333, 0.333333))";
- } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
- inputs[i] = "vec3(" + src_var + ")";
- } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- inputs[i] = "vec3(float(" + src_var + "))";
- } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
- inputs[i] = "all(bvec3(" + src_var + "))";
- } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
- inputs[i] = src_var + " > 0.0 ? true : false";
- } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- inputs[i] = src_var + " > 0 ? true : false";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = "(" + src_var + " ? 1.0 : 0.0)";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR_INT && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = "(" + src_var + " ? 1 : 0)";
- } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = "vec3(" + src_var + " ? 1.0 : 0.0)";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- inputs[i] = "float(" + src_var + ")";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR_INT && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
- inputs[i] = "int(" + src_var + ")";
+ } else {
+ switch (in_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = "float(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "(" + src_var + " ? 1.0 : 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "dot(" + src_var + ", vec2(0.5, 0.5))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ inputs[i] = "dot(" + src_var + ", vec3(0.333333, 0.333333, 0.333333))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ inputs[i] = "dot(" + src_var + ", vec4(0.25, 0.25, 0.25, 0.25))";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = "int(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "(" + src_var + " ? 1 : 0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "dot(float(" + src_var + "), vec2(0.5, 0.5))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ inputs[i] = "dot(float(" + src_var + "), vec3(0.333333, 0.333333, 0.333333))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ inputs[i] = "dot(float(" + src_var + "), vec4(0.25, 0.25, 0.25, 0.25))";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = src_var + " > 0.0 ? true : false";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = src_var + " > 0 ? true : false";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "all(bvec2(" + src_var + "))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ inputs[i] = "all(bvec3(" + src_var + "))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ inputs[i] = "all(bvec4(" + src_var + "))";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = "vec2(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = "vec2(float(" + src_var + "))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "vec2(" + src_var + " ? 1.0 : 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D:
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ inputs[i] = "vec2(" + src_var + ".xy)";
+ } break;
+ default:
+ break;
+ }
+ } break;
+
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = "vec3(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = "vec3(float(" + src_var + "))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "vec3(" + src_var + " ? 1.0 : 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "vec3(" + src_var + ", 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ inputs[i] = "vec3(" + src_var + ".xyz)";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = "vec4(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = "vec4(float(" + src_var + "))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "vec4(" + src_var + " ? 1.0 : 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "vec4(" + src_var + ", 0.0, 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ inputs[i] = "vec4(" + src_var + ", 0.0)";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ default:
+ break;
+ }
}
} else {
if (!vsnode->is_generate_input_var(i)) {
@@ -1461,10 +1867,18 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
bool val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
node_code += " bool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n";
+ } else if (defval.get_type() == Variant::VECTOR2) {
+ Vector2 val = defval;
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ node_code += " vec2 " + inputs[i] + " = " + vformat("vec2(%.5f, %.5f);\n", val.x, val.y);
} else if (defval.get_type() == Variant::VECTOR3) {
Vector3 val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
node_code += " vec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z);
+ } else if (defval.get_type() == Variant::QUATERNION) {
+ Quaternion val = defval;
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ node_code += " vec4 " + inputs[i] + " = " + vformat("vec4(%.5f, %.5f, %.5f, %.5f);\n", val.x, val.y, val.z, val.w);
} else if (defval.get_type() == Variant::TRANSFORM3D) {
Transform3D val = defval;
val.basis.transpose();
@@ -1489,7 +1903,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
int output_count = vsnode->get_output_port_count();
int initial_output_count = output_count;
- Map<int, bool> expanded_output_ports;
+ HashMap<int, bool> expanded_output_ports;
for (int i = 0; i < initial_output_count; i++) {
bool expanded = false;
@@ -1497,8 +1911,18 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
if (vsnode->is_output_port_expandable(i) && vsnode->_is_output_port_expanded(i)) {
expanded = true;
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- output_count += 3;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ output_count += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ output_count += 3;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ output_count += 4;
+ } break;
+ default:
+ break;
}
}
expanded_output_ports.insert(i, expanded);
@@ -1518,21 +1942,37 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_SCALAR_INT:
outputs[i] = "int " + var_name;
break;
- case VisualShaderNode::PORT_TYPE_VECTOR:
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D:
+ outputs[i] = "vec2 " + var_name;
+ break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D:
outputs[i] = "vec3 " + var_name;
break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D:
+ outputs[i] = "vec4 " + var_name;
+ break;
case VisualShaderNode::PORT_TYPE_BOOLEAN:
outputs[i] = "bool " + var_name;
break;
case VisualShaderNode::PORT_TYPE_TRANSFORM:
outputs[i] = "mat4 " + var_name;
break;
- default: {
- }
+ default:
+ break;
}
if (expanded_output_ports[i]) {
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- j += 3;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ j += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ j += 3;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ j += 4;
+ } break;
+ default:
+ break;
}
}
}
@@ -1547,72 +1987,124 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_SCALAR_INT:
code += " int " + outputs[i] + ";\n";
break;
- case VisualShaderNode::PORT_TYPE_VECTOR:
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D:
+ code += " vec2 " + outputs[i] + ";\n";
+ break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D:
code += " vec3 " + outputs[i] + ";\n";
break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D:
+ code += " vec4 " + outputs[i] + ";\n";
+ break;
case VisualShaderNode::PORT_TYPE_BOOLEAN:
code += " bool " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_TRANSFORM:
code += " mat4 " + outputs[i] + ";\n";
break;
- default: {
- }
+ default:
+ break;
}
if (expanded_output_ports[i]) {
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- j += 3;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ j += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ j += 3;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ j += 4;
+ } break;
+ default:
+ break;
}
}
}
}
node_code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview);
- if (node_code != String()) {
+ if (!node_code.is_empty()) {
code += node_name;
code += node_code;
- code += "\n";
}
for (int i = 0; i < output_count; i++) {
- bool new_line_inserted = false;
if (expanded_output_ports[i]) {
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
- if (!new_line_inserted) {
- code += "\n";
- new_line_inserted = true;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
+ String r = "n_out" + itos(node) + "p" + itos(i + 1);
+ code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
+ outputs[i + 1] = r;
}
- String r = "n_out" + itos(node) + "p" + itos(i + 1);
- code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
- outputs[i + 1] = r;
- }
- if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
- if (!new_line_inserted) {
- code += "\n";
- new_line_inserted = true;
+ if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
+ String g = "n_out" + itos(node) + "p" + itos(i + 2);
+ code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
+ outputs[i + 2] = g;
}
- String g = "n_out" + itos(node) + "p" + itos(i + 2);
- code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
- outputs[i + 2] = g;
- }
- if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component
- if (!new_line_inserted) {
- code += "\n";
- new_line_inserted = true;
+ i += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
+ if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
+ String r = "n_out" + itos(node) + "p" + itos(i + 1);
+ code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
+ outputs[i + 1] = r;
+ }
+
+ if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
+ String g = "n_out" + itos(node) + "p" + itos(i + 2);
+ code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
+ outputs[i + 2] = g;
+ }
+
+ if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component
+ String b = "n_out" + itos(node) + "p" + itos(i + 3);
+ code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n";
+ outputs[i + 3] = b;
+ }
+
+ i += 3;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_4D: {
+ if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
+ String r = "n_out" + itos(node) + "p" + itos(i + 1);
+ code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
+ outputs[i + 1] = r;
+ }
+
+ if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
+ String g = "n_out" + itos(node) + "p" + itos(i + 2);
+ code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
+ outputs[i + 2] = g;
+ }
+
+ if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component
+ String b = "n_out" + itos(node) + "p" + itos(i + 3);
+ code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n";
+ outputs[i + 3] = b;
+ }
+
+ if (vsnode->is_output_port_connected(i + 4) || (for_preview && vsnode->get_output_port_for_preview() == (i + 4))) { // alpha-component
+ String a = "n_out" + itos(node) + "p" + itos(i + 4);
+ code += " float " + a + " = n_out" + itos(node) + "p" + itos(i) + ".a;\n";
+ outputs[i + 4] = a;
}
- String b = "n_out" + itos(node) + "p" + itos(i + 3);
- code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n";
- outputs[i + 3] = b;
- }
- i += 3;
+ i += 4;
+ } break;
+ default:
+ break;
}
}
}
+ if (!node_code.is_empty()) {
+ code += "\n";
+ }
+
code += "\n"; //
processed.insert(node);
@@ -1641,11 +2133,11 @@ void VisualShader::_update_shader() const {
StringBuilder global_code;
StringBuilder global_code_per_node;
- Map<Type, StringBuilder> global_code_per_func;
+ HashMap<Type, StringBuilder> global_code_per_func;
StringBuilder code;
Vector<VisualShader::DefaultTextureParam> default_tex_params;
- Set<StringName> classes;
- Map<int, int> insertion_pos;
+ RBSet<StringName> classes;
+ HashMap<int, int> insertion_pos;
static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky", "fog" };
global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n";
@@ -1653,61 +2145,54 @@ void VisualShader::_update_shader() const {
String render_mode;
{
- //fill render mode enums
- int idx = 0;
- while (render_mode_enums[idx].string) {
- if (shader_mode == render_mode_enums[idx].mode) {
- if (modes.has(render_mode_enums[idx].string)) {
- int which = modes[render_mode_enums[idx].string];
- int count = 0;
- for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)).size(); i++) {
- String mode = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode))[i];
- if (mode.begins_with(render_mode_enums[idx].string)) {
- if (count == which) {
- if (render_mode != String()) {
- render_mode += ", ";
- }
- render_mode += mode;
- break;
- }
- count++;
- }
+ const Vector<ShaderLanguage::ModeInfo> &rmodes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode));
+ Vector<String> flag_names;
+
+ // Add enum modes first.
+ for (int i = 0; i < rmodes.size(); i++) {
+ const ShaderLanguage::ModeInfo &info = rmodes[i];
+ const String temp = String(info.name);
+
+ if (!info.options.is_empty()) {
+ if (modes.has(temp) && modes[temp] < info.options.size()) {
+ if (!render_mode.is_empty()) {
+ render_mode += ", ";
}
+ render_mode += temp + "_" + info.options[modes[temp]];
}
+ } else if (flags.has(temp)) {
+ flag_names.push_back(temp);
}
- idx++;
}
- //fill render mode flags
- for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)).size(); i++) {
- String mode = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode))[i];
- if (flags.has(mode)) {
- if (render_mode != String()) {
- render_mode += ", ";
- }
- render_mode += mode;
+ // Add flags afterward.
+ for (int i = 0; i < flag_names.size(); i++) {
+ if (!render_mode.is_empty()) {
+ render_mode += ", ";
}
+ render_mode += flag_names[i];
}
}
- if (render_mode != String()) {
+ if (!render_mode.is_empty()) {
global_code += "render_mode " + render_mode + ";\n\n";
}
static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" };
String global_expressions;
- Set<String> used_uniform_names;
+ RBSet<String> used_uniform_names;
List<VisualShaderNodeUniform *> uniforms;
- Map<int, List<int>> emitters;
+ HashMap<int, List<int>> emitters;
+ HashMap<int, List<int>> varying_setters;
for (int i = 0, index = 0; i < TYPE_MAX; i++) {
if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
continue;
}
- for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
- Ref<VisualShaderNodeGlobalExpression> global_expression = Object::cast_to<VisualShaderNodeGlobalExpression>(E->get().node.ptr());
+ for (const KeyValue<int, Node> &E : graph[i].nodes) {
+ Ref<VisualShaderNodeGlobalExpression> global_expression = E.value.node;
if (global_expression.is_valid()) {
String expr = "";
expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n";
@@ -1716,26 +2201,27 @@ void VisualShader::_update_shader() const {
expr += "\n";
global_expressions += expr;
}
- Ref<VisualShaderNodeUniformRef> uniform_ref = Object::cast_to<VisualShaderNodeUniformRef>(E->get().node.ptr());
+ Ref<VisualShaderNodeUniformRef> uniform_ref = E.value.node;
if (uniform_ref.is_valid()) {
used_uniform_names.insert(uniform_ref->get_uniform_name());
}
- Ref<VisualShaderNodeUniform> uniform = Object::cast_to<VisualShaderNodeUniform>(E->get().node.ptr());
+ Ref<VisualShaderNodeUniform> uniform = E.value.node;
if (uniform.is_valid()) {
uniforms.push_back(uniform.ptr());
}
- Ref<VisualShaderNodeParticleEmit> emit_particle = Object::cast_to<VisualShaderNodeParticleEmit>(E->get().node.ptr());
+ Ref<VisualShaderNodeVaryingSetter> varying_setter = E.value.node;
+ if (varying_setter.is_valid() && varying_setter->is_input_port_connected(0)) {
+ if (!varying_setters.has(i)) {
+ varying_setters.insert(i, List<int>());
+ }
+ varying_setters[i].push_back(E.key);
+ }
+ Ref<VisualShaderNodeParticleEmit> emit_particle = E.value.node;
if (emit_particle.is_valid()) {
if (!emitters.has(i)) {
emitters.insert(i, List<int>());
}
-
- for (const KeyValue<int, Node> &M : graph[i].nodes) {
- if (M.value.node == emit_particle.ptr()) {
- emitters[i].push_back(M.key);
- break;
- }
- }
+ emitters[i].push_back(E.key);
}
}
}
@@ -1750,8 +2236,41 @@ void VisualShader::_update_shader() const {
}
}
- Map<int, String> code_map;
- Set<int> empty_funcs;
+ if (!varyings.is_empty()) {
+ global_code += "\n// Varyings\n";
+
+ for (const KeyValue<String, Varying> &E : varyings) {
+ global_code += "varying ";
+ switch (E.value.type) {
+ case VaryingType::VARYING_TYPE_FLOAT:
+ global_code += "float ";
+ break;
+ case VaryingType::VARYING_TYPE_VECTOR_2D:
+ global_code += "vec2 ";
+ break;
+ case VaryingType::VARYING_TYPE_VECTOR_3D:
+ global_code += "vec3 ";
+ break;
+ case VaryingType::VARYING_TYPE_VECTOR_4D:
+ global_code += "vec4 ";
+ break;
+ case VaryingType::VARYING_TYPE_COLOR:
+ global_code += "vec4 ";
+ break;
+ case VaryingType::VARYING_TYPE_TRANSFORM:
+ global_code += "mat4 ";
+ break;
+ default:
+ break;
+ }
+ global_code += E.key + ";\n";
+ }
+
+ global_code += "\n";
+ }
+
+ HashMap<int, String> code_map;
+ RBSet<int> empty_funcs;
for (int i = 0; i < TYPE_MAX; i++) {
if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
@@ -1763,12 +2282,57 @@ void VisualShader::_update_shader() const {
VMap<ConnectionKey, const List<Connection>::Element *> output_connections;
StringBuilder func_code;
+ RBSet<int> processed;
bool is_empty_func = false;
if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY && shader_mode != Shader::MODE_FOG) {
is_empty_func = true;
}
+ String varying_code;
+ if (shader_mode == Shader::MODE_SPATIAL || shader_mode == Shader::MODE_CANVAS_ITEM) {
+ for (const KeyValue<String, Varying> &E : varyings) {
+ if ((E.value.mode == VARYING_MODE_VERTEX_TO_FRAG_LIGHT && i == TYPE_VERTEX) || (E.value.mode == VARYING_MODE_FRAG_TO_LIGHT && i == TYPE_FRAGMENT)) {
+ bool found = false;
+ for (int key : varying_setters[i]) {
+ Ref<VisualShaderNodeVaryingSetter> setter = Object::cast_to<VisualShaderNodeVaryingSetter>(const_cast<VisualShaderNode *>(graph[i].nodes[key].node.ptr()));
+ if (setter.is_valid() && E.value.name == setter->get_varying_name()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ String code2;
+ switch (E.value.type) {
+ case VaryingType::VARYING_TYPE_FLOAT:
+ code2 += "0.0";
+ break;
+ case VaryingType::VARYING_TYPE_VECTOR_2D:
+ code2 += "vec2(0.0)";
+ break;
+ case VaryingType::VARYING_TYPE_VECTOR_3D:
+ code2 += "vec3(0.0)";
+ break;
+ case VaryingType::VARYING_TYPE_VECTOR_4D:
+ code2 += "vec4(0.0)";
+ break;
+ case VaryingType::VARYING_TYPE_COLOR:
+ code2 += "vec4(0.0)";
+ break;
+ case VaryingType::VARYING_TYPE_TRANSFORM:
+ code2 += "mat4(1.0)";
+ break;
+ default:
+ break;
+ }
+ varying_code += vformat(" %s = %s;\n", E.key, code2);
+ }
+ is_empty_func = false;
+ }
+ }
+ }
+
for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) {
ConnectionKey from_key;
from_key.node = E->get().from_node;
@@ -1797,13 +2361,19 @@ void VisualShader::_update_shader() const {
}
insertion_pos.insert(i, code.get_string_length() + func_code.get_string_length());
- Set<int> processed;
- Error err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false, classes);
+ Error err = _write_node(Type(i), &global_code, &global_code_per_node, &global_code_per_func, func_code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false, classes);
ERR_FAIL_COND(err != OK);
+ if (varying_setters.has(i)) {
+ for (int &E : varying_setters[i]) {
+ err = _write_node(Type(i), nullptr, nullptr, nullptr, func_code, default_tex_params, input_connections, output_connections, E, processed, false, classes);
+ ERR_FAIL_COND(err != OK);
+ }
+ }
+
if (emitters.has(i)) {
for (int &E : emitters[i]) {
- err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, E, processed, false, classes);
+ err = _write_node(Type(i), &global_code, &global_code_per_node, &global_code_per_func, func_code, default_tex_params, input_connections, output_connections, E, processed, false, classes);
ERR_FAIL_COND(err != OK);
}
}
@@ -1811,6 +2381,7 @@ void VisualShader::_update_shader() const {
if (shader_mode == Shader::MODE_PARTICLES) {
code_map.insert(i, func_code);
} else {
+ func_code += varying_code;
func_code += "}\n";
code += func_code;
}
@@ -1906,7 +2477,11 @@ void VisualShader::_update_shader() const {
global_compute_code += " return __rand_from_seed(seed) * (to - from) + from;\n";
global_compute_code += "}\n\n";
- global_compute_code += "vec3 __randv_range(inout uint seed, vec3 from, vec3 to) {\n";
+ global_compute_code += "vec2 __randv2_range(inout uint seed, vec2 from, vec2 to) {\n";
+ global_compute_code += " return vec2(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y));\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "vec3 __randv3_range(inout uint seed, vec3 from, vec3 to) {\n";
global_compute_code += " return vec3(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y), __randf_range(seed, from.z, to.z));\n";
global_compute_code += "}\n\n";
@@ -1982,21 +2557,6 @@ void VisualShader::_queue_update() {
call_deferred(SNAME("_update_shader"));
}
-void VisualShader::_input_type_changed(Type p_type, int p_id) {
- ERR_FAIL_INDEX(p_type, TYPE_MAX);
- //erase connections using this input, as type changed
- Graph *g = &graph[p_type];
-
- for (List<Connection>::Element *E = g->connections.front(); E;) {
- List<Connection>::Element *N = E->next();
- if (E->get().from_node == p_id) {
- g->connections.erase(E);
- g->nodes[E->get().to_node].prev_connected_nodes.erase(p_id);
- }
- E = N;
- }
-}
-
void VisualShader::rebuild() {
dirty.set();
_update_shader();
@@ -2032,10 +2592,14 @@ void VisualShader::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset);
ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset);
+ ClassDB::bind_method(D_METHOD("add_varying", "name", "mode", "type"), &VisualShader::add_varying);
+ ClassDB::bind_method(D_METHOD("remove_varying", "name"), &VisualShader::remove_varying);
+ ClassDB::bind_method(D_METHOD("has_varying", "name"), &VisualShader::has_varying);
+
ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_engine_version", "get_engine_version");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_engine_version", "get_engine_version");
ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs.
@@ -2051,6 +2615,18 @@ void VisualShader::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_FOG);
BIND_ENUM_CONSTANT(TYPE_MAX);
+ BIND_ENUM_CONSTANT(VARYING_MODE_VERTEX_TO_FRAG_LIGHT);
+ BIND_ENUM_CONSTANT(VARYING_MODE_FRAG_TO_LIGHT);
+ BIND_ENUM_CONSTANT(VARYING_MODE_MAX);
+
+ BIND_ENUM_CONSTANT(VARYING_TYPE_FLOAT);
+ BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_4D);
+ BIND_ENUM_CONSTANT(VARYING_TYPE_COLOR);
+ BIND_ENUM_CONSTANT(VARYING_TYPE_TRANSFORM);
+ BIND_ENUM_CONSTANT(VARYING_TYPE_MAX);
+
BIND_CONSTANT(NODE_ID_INVALID);
BIND_CONSTANT(NODE_ID_OUTPUT);
}
@@ -2082,146 +2658,141 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
// Node3D
// Node3D, Vertex
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "vertex_id", "VERTEX_ID" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "instance_custom", "INSTANCE_CUSTOM.rgb" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "instance_custom", "INSTANCE_CUSTOM" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview", "MODELVIEW_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview_matrix", "MODELVIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_view_matrix", "INV_VIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "view_matrix", "VIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection_matrix", "PROJECTION_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection_matrix", "INV_PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_index", "VIEW_INDEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_mono_left", "VIEW_MONO_LEFT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_right", "VIEW_RIGHT" },
// Node3D, Fragment
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "view", "VIEW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "view_matrix", "VIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_view_matrix", "INV_VIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection_matrix", "PROJECTION_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection_matrix", "INV_PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "front_facing", "FRONT_FACING" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_roughness_texture", "NORMAL_ROUGHNESS_TEXTURE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "depth_texture", "DEPTH_TEXTURE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_index", "VIEW_INDEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_mono_left", "VIEW_MONO_LEFT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_right", "VIEW_RIGHT" },
// Node3D, Light
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "view", "VIEW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_color", "LIGHT_COLOR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "attenuation", "ATTENUATION" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "backlight", "BACKLIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "diffuse", "DIFFUSE_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular", "SPECULAR_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "view_matrix", "VIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_view_matrix", "INV_VIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection_matrix", "PROJECTION_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection_matrix", "INV_PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
// Canvas Item
// Canvas Item, Vertex
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas", "CANVAS_MATRIX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas_matrix", "CANVAS_MATRIX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen_matrix", "SCREEN_MATRIX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "instance_custom", "INSTANCE_CUSTOM.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "instance_custom", "INSTANCE_CUSTOM" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "vertex_id", "VERTEX_ID" },
// Canvas Item, Fragment
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_pixel_size", "vec3(SCREEN_PIXEL_SIZE, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_pixel_size", "SCREEN_PIXEL_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_texture", "NORMAL_TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "specular_shininess", "SPECULAR_SHININESS" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
// Canvas Item, Light
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_color_alpha", "LIGHT_COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_position", "LIGHT_POSITION" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow", "SHADOW_MODULATE.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light", "LIGHT" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light_color", "LIGHT_COLOR" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_position", "LIGHT_POSITION" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "shadow", "SHADOW_MODULATE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "specular_shininess", "SPECULAR_SHININESS" },
// Particles, Start
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
@@ -2230,14 +2801,12 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Start (Custom)
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
@@ -2246,14 +2815,12 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Process
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
@@ -2262,14 +2829,12 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Process (Custom)
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
@@ -2278,16 +2843,14 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Collide
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "collision_depth", "COLLISION_DEPTH" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "collision_normal", "COLLISION_NORMAL" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "collision_normal", "COLLISION_NORMAL" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
@@ -2299,40 +2862,37 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "eyedir", "EYEDIR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "half_res_color", "HALF_RES_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light0_color", "LIGHT0_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light0_direction", "LIGHT0_DIRECTION" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light1_color", "LIGHT1_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light1_direction", "LIGHT1_DIRECTION" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light2_color", "LIGHT2_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light2_direction", "LIGHT2_DIRECTION" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light3_color", "LIGHT3_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light3_direction", "LIGHT3_DIRECTION" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "position", "POSITION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "quarter_res_color", "QUARTER_RES_COLOR" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "sky_coords", "SKY_COORDS" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Fog, Fog
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "world_position", "WORLD_POSITION" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "object_position", "OBJECT_POSITION" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "uvw", "UVW" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "extents", "EXTENTS" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "world_position", "WORLD_POSITION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "object_position", "OBJECT_POSITION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "uvw", "UVW" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "extents", "EXTENTS" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "sdf", "SDF" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
@@ -2342,64 +2902,64 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
// Spatial, Vertex
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "vec3(0.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "vec3(1.0, 0.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Spatial, Fragment
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "vec3(0.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "vec3(1.0, 0.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Spatial, Light
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Canvas Item, Vertex
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Canvas Item, Fragment
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Canvas Item, Light
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles
@@ -2412,7 +2972,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
// Sky
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Fog
@@ -2439,7 +2999,7 @@ int VisualShaderNodeInput::get_output_port_count() const {
}
VisualShaderNodeInput::PortType VisualShaderNodeInput::get_output_port_type(int p_port) const {
- return get_input_type_by_name(input_name);
+ return p_port == 0 ? get_input_type_by_name(input_name) : PORT_TYPE_SCALAR;
}
String VisualShaderNodeInput::get_output_port_name(int p_port) const {
@@ -2450,6 +3010,22 @@ String VisualShaderNodeInput::get_caption() const {
return "Input";
}
+bool VisualShaderNodeInput::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ switch (get_input_type_by_name(input_name)) {
+ case PORT_TYPE_VECTOR_2D:
+ return true;
+ case PORT_TYPE_VECTOR_3D:
+ return true;
+ case PORT_TYPE_VECTOR_4D:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
if (get_output_port_type(0) == PORT_TYPE_SAMPLER) {
return "";
@@ -2468,7 +3044,7 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
idx++;
}
- if (code == String()) {
+ if (code.is_empty()) {
switch (get_output_port_type(0)) {
case PORT_TYPE_SCALAR: {
code = " " + p_output_vars[0] + " = 0.0;\n";
@@ -2476,9 +3052,15 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
case PORT_TYPE_SCALAR_INT: {
code = " " + p_output_vars[0] + " = 0;\n";
} break;
- case PORT_TYPE_VECTOR: {
+ case PORT_TYPE_VECTOR_2D: {
+ code = " " + p_output_vars[0] + " = vec2(0.0);\n";
+ } break;
+ case PORT_TYPE_VECTOR_3D: {
code = " " + p_output_vars[0] + " = vec3(0.0);\n";
} break;
+ case PORT_TYPE_VECTOR_4D: {
+ code = " " + p_output_vars[0] + " = vec4(0.0);\n";
+ } break;
case PORT_TYPE_BOOLEAN: {
code = " " + p_output_vars[0] + " = false;\n";
} break;
@@ -2502,7 +3084,7 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
idx++;
}
- if (code == String()) {
+ if (code.is_empty()) {
code = " " + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar
}
@@ -2605,7 +3187,7 @@ void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const {
while (ports[idx].mode != Shader::MODE_MAX) {
if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
- if (port_list != String()) {
+ if (!port_list.is_empty()) {
port_list += ",";
}
port_list += ports[idx].name;
@@ -2613,8 +3195,8 @@ void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const {
idx++;
}
- if (port_list == "") {
- port_list = TTR("None");
+ if (port_list.is_empty()) {
+ port_list = RTR("None");
}
property.hint_string = port_list;
}
@@ -2691,7 +3273,11 @@ int VisualShaderNodeUniformRef::get_output_port_count() const {
return 1;
case UniformType::UNIFORM_TYPE_BOOLEAN:
return 1;
- case UniformType::UNIFORM_TYPE_VECTOR:
+ case UniformType::UNIFORM_TYPE_VECTOR2:
+ return 1;
+ case UniformType::UNIFORM_TYPE_VECTOR3:
+ return 1;
+ case UniformType::UNIFORM_TYPE_VECTOR4:
return 1;
case UniformType::UNIFORM_TYPE_TRANSFORM:
return 1;
@@ -2713,13 +3299,17 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port
return PortType::PORT_TYPE_SCALAR_INT;
case UniformType::UNIFORM_TYPE_BOOLEAN:
return PortType::PORT_TYPE_BOOLEAN;
- case UniformType::UNIFORM_TYPE_VECTOR:
- return PortType::PORT_TYPE_VECTOR;
+ case UniformType::UNIFORM_TYPE_VECTOR2:
+ return PortType::PORT_TYPE_VECTOR_2D;
+ case UniformType::UNIFORM_TYPE_VECTOR3:
+ return PortType::PORT_TYPE_VECTOR_3D;
+ case UniformType::UNIFORM_TYPE_VECTOR4:
+ return PortType::PORT_TYPE_VECTOR_4D;
case UniformType::UNIFORM_TYPE_TRANSFORM:
return PortType::PORT_TYPE_TRANSFORM;
case UniformType::UNIFORM_TYPE_COLOR:
if (p_port == 0) {
- return PortType::PORT_TYPE_VECTOR;
+ return PortType::PORT_TYPE_VECTOR_3D;
} else if (p_port == 1) {
return PORT_TYPE_SCALAR;
}
@@ -2740,7 +3330,11 @@ String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const {
return "";
case UniformType::UNIFORM_TYPE_BOOLEAN:
return "";
- case UniformType::UNIFORM_TYPE_VECTOR:
+ case UniformType::UNIFORM_TYPE_VECTOR2:
+ return "";
+ case UniformType::UNIFORM_TYPE_VECTOR3:
+ return "";
+ case UniformType::UNIFORM_TYPE_VECTOR4:
return "";
case UniformType::UNIFORM_TYPE_TRANSFORM:
return "";
@@ -2810,12 +3404,16 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_b
return PORT_TYPE_SCALAR_INT;
case UniformType::UNIFORM_TYPE_SAMPLER:
return PORT_TYPE_SAMPLER;
- case UniformType::UNIFORM_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
+ case UniformType::UNIFORM_TYPE_VECTOR2:
+ return PORT_TYPE_VECTOR_2D;
+ case UniformType::UNIFORM_TYPE_VECTOR3:
+ return PORT_TYPE_VECTOR_3D;
+ case UniformType::UNIFORM_TYPE_VECTOR4:
+ return PORT_TYPE_VECTOR_4D;
case UniformType::UNIFORM_TYPE_TRANSFORM:
return PORT_TYPE_TRANSFORM;
case UniformType::UNIFORM_TYPE_COLOR:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -2829,26 +3427,19 @@ String VisualShaderNodeUniformRef::generate_code(Shader::Mode p_mode, VisualShad
if (uniform_name == "[None]") {
return " " + p_output_vars[0] + " = 0.0;\n";
}
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
- case UniformType::UNIFORM_TYPE_INT:
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
- case UniformType::UNIFORM_TYPE_BOOLEAN:
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
- case UniformType::UNIFORM_TYPE_VECTOR:
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
- case UniformType::UNIFORM_TYPE_TRANSFORM:
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ break;
case UniformType::UNIFORM_TYPE_COLOR: {
String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
return code;
} break;
case UniformType::UNIFORM_TYPE_SAMPLER:
- break;
+ return String();
default:
break;
}
- return "";
+
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
void VisualShaderNodeUniformRef::_set_uniform_type(int p_uniform_type) {
@@ -2888,89 +3479,89 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
////////////////////////////////////////////////////////////////////////
// Node3D, Vertex.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "UV2:xy" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV2", "UV2" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Roughness", "ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Point Size", "POINT_SIZE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Model View Matrix", "MODELVIEW_MATRIX" },
////////////////////////////////////////////////////////////////////////
// Node3D, Fragment.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_gloss", "CLEARCOAT_GLOSS" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "anisotropy_flow", "ANISOTROPY_FLOW:xy" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor_threshold", "ALPHA_SCISSOR_THRESHOLD" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Metallic", "METALLIC" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Roughness", "ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Specular", "SPECULAR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "AO", "AO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "AO Light Affect", "AO_LIGHT_AFFECT" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim", "RIM" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim Tint", "RIM_TINT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat", "CLEARCOAT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat Roughness", "CLEARCOAT_ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Anisotropy", "ANISOTROPY" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Anisotropy Flow", "ANISOTROPY_FLOW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Subsurf Scatter", "SSS_STRENGTH" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Backlight", "BACKLIGHT" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha Scissor Threshold", "ALPHA_SCISSOR_THRESHOLD" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha Hash Scale", "ALPHA_HASH_SCALE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha AA Edge", "ALPHA_ANTIALIASING_EDGE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Alpha UV", "ALPHA_TEXTURE_COORDINATE" },
+
////////////////////////////////////////////////////////////////////////
// Node3D, Light.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Diffuse", "DIFFUSE_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Specular", "SPECULAR_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" },
////////////////////////////////////////////////////////////////////////
// Canvas Item.
////////////////////////////////////////////////////////////////////////
// Canvas Item, Vertex.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX:xy" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Vertex", "VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV", "UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Point Size", "POINT_SIZE" },
////////////////////////////////////////////////////////////////////////
// Canvas Item, Fragment.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_vertex", "SHADOW_VERTEX:xy" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Light Vertex", "LIGHT_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Shadow Vertex", "SHADOW_VERTEX" },
////////////////////////////////////////////////////////////////////////
// Canvas Item, Light.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Light", "LIGHT" },
////////////////////////////////////////////////////////////////////////
// Sky, Sky.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "fog", "FOG.rgb" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "fog_alpha", "FOG.a" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Color", "COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Fog", "FOG" },
////////////////////////////////////////////////////////////////////////
// Fog, Fog.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "density", "DENSITY" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "Density", "DENSITY" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" },
////////////////////////////////////////////////////////////////////////
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
@@ -3014,7 +3605,7 @@ String VisualShaderNodeOutput::get_input_port_name(int p_port) const {
while (ports[idx].mode != Shader::MODE_MAX) {
if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
if (count == p_port) {
- return String(ports[idx].name).capitalize();
+ return String(ports[idx].name);
}
count++;
}
@@ -3047,7 +3638,7 @@ bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
}
if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) {
String name = get_input_port_name(p_index);
- return bool(name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold");
+ return bool(name == "AO" || name == "Normal" || name == "Rim" || name == "Clearcoat" || name == "Anisotropy" || name == "Subsurf Scatter" || name == "Alpha Scissor Threshold");
}
return false;
}
@@ -3063,9 +3654,9 @@ String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader::
String code;
while (ports[idx].mode != Shader::MODE_MAX) {
if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
- if (p_input_vars[count] != String()) {
+ if (!p_input_vars[count].is_empty()) {
String s = ports[idx].string;
- if (s.find(":") != -1) {
+ if (s.contains(":")) {
code += " " + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n";
} else {
code += " " + s + " = " + p_input_vars[count] + ";\n";
@@ -3151,7 +3742,7 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
List<String> keyword_list;
ShaderLanguage::get_keyword_list(&keyword_list);
if (keyword_list.find(uniform_name)) {
- return TTR("Shader keywords cannot be used as uniform names.\nChoose another name.");
+ return RTR("Shader keywords cannot be used as uniform names.\nChoose another name.");
}
if (!is_qualifier_supported(qualifier)) {
String qualifier_str;
@@ -3167,11 +3758,11 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
default:
break;
}
- return vformat(TTR("This uniform type does not support the '%s' qualifier."), qualifier_str);
+ 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);
if (gvt == RS::GLOBAL_VAR_TYPE_MAX) {
- return vformat(TTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name);
+ return vformat(RTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name);
}
bool incompatible_type = false;
switch (gvt) {
@@ -3200,6 +3791,11 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
incompatible_type = true;
}
} break;
+ case RS::GLOBAL_VAR_TYPE_VEC4: {
+ if (!Object::cast_to<VisualShaderNodeVec4Uniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
if (!Object::cast_to<VisualShaderNodeTransformUniform>(this)) {
incompatible_type = true;
@@ -3229,7 +3825,7 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
break;
}
if (incompatible_type) {
- return vformat(TTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name);
+ return vformat(RTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name);
}
}
@@ -3422,7 +4018,6 @@ bool VisualShaderNodeGroupBase::is_valid_port_name(const String &p_name) const {
}
void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) {
- ERR_FAIL_COND(has_input_port(p_id));
ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
ERR_FAIL_COND(!is_valid_port_name(p_name));
@@ -3498,7 +4093,6 @@ bool VisualShaderNodeGroupBase::has_input_port(int p_id) const {
}
void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) {
- ERR_FAIL_COND(has_output_port(p_id));
ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
ERR_FAIL_COND(!is_valid_port_name(p_name));
@@ -3903,9 +4497,15 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad
case PORT_TYPE_SCALAR_INT:
tk = "0";
break;
- case PORT_TYPE_VECTOR:
+ case PORT_TYPE_VECTOR_2D:
+ tk = "vec2(0.0, 0.0)";
+ break;
+ case PORT_TYPE_VECTOR_3D:
tk = "vec3(0.0, 0.0, 0.0)";
break;
+ case PORT_TYPE_VECTOR_4D:
+ tk = "vec4(0.0, 0.0, 0.0, 0.0)";
+ break;
case PORT_TYPE_BOOLEAN:
tk = "false";
break;
@@ -3951,3 +4551,306 @@ String VisualShaderNodeGlobalExpression::generate_global(Shader::Mode p_mode, Vi
VisualShaderNodeGlobalExpression::VisualShaderNodeGlobalExpression() {
set_editable(false);
}
+
+////////////// Varying
+
+List<VisualShaderNodeVarying::Varying> varyings;
+
+void VisualShaderNodeVarying::add_varying(const String &p_name, VisualShader::VaryingMode p_mode, VisualShader::VaryingType p_type) { // static
+ varyings.push_back({ p_name, p_mode, p_type });
+}
+
+void VisualShaderNodeVarying::clear_varyings() { // static
+ varyings.clear();
+}
+
+bool VisualShaderNodeVarying::has_varying(const String &p_name) { // static
+ for (const VisualShaderNodeVarying::Varying &E : varyings) {
+ if (E.name == p_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int VisualShaderNodeVarying::get_varyings_count() const {
+ return varyings.size();
+}
+
+String VisualShaderNodeVarying::get_varying_name_by_index(int p_idx) const {
+ if (p_idx >= 0 && p_idx < varyings.size()) {
+ return varyings[p_idx].name;
+ }
+ return "";
+}
+
+VisualShader::VaryingType VisualShaderNodeVarying::get_varying_type_by_name(const String &p_name) const {
+ for (int i = 0; i < varyings.size(); i++) {
+ if (varyings[i].name == p_name) {
+ return varyings[i].type;
+ }
+ }
+ return VisualShader::VARYING_TYPE_FLOAT;
+}
+
+VisualShader::VaryingType VisualShaderNodeVarying::get_varying_type_by_index(int p_idx) const {
+ if (p_idx >= 0 && p_idx < varyings.size()) {
+ return varyings[p_idx].type;
+ }
+ return VisualShader::VARYING_TYPE_FLOAT;
+}
+
+VisualShader::VaryingMode VisualShaderNodeVarying::get_varying_mode_by_name(const String &p_name) const {
+ for (int i = 0; i < varyings.size(); i++) {
+ if (varyings[i].name == p_name) {
+ return varyings[i].mode;
+ }
+ }
+ return VisualShader::VARYING_MODE_VERTEX_TO_FRAG_LIGHT;
+}
+
+VisualShader::VaryingMode VisualShaderNodeVarying::get_varying_mode_by_index(int p_idx) const {
+ if (p_idx >= 0 && p_idx < varyings.size()) {
+ return varyings[p_idx].mode;
+ }
+ return VisualShader::VARYING_MODE_VERTEX_TO_FRAG_LIGHT;
+}
+
+VisualShaderNodeVarying::PortType VisualShaderNodeVarying::get_port_type_by_index(int p_idx) const {
+ if (p_idx >= 0 && p_idx < varyings.size()) {
+ return get_port_type(varyings[p_idx].type, 0);
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+//////////////
+
+void VisualShaderNodeVarying::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_varying_name", "name"), &VisualShaderNodeVarying::set_varying_name);
+ ClassDB::bind_method(D_METHOD("get_varying_name"), &VisualShaderNodeVarying::get_varying_name);
+
+ ClassDB::bind_method(D_METHOD("set_varying_type", "type"), &VisualShaderNodeVarying::set_varying_type);
+ ClassDB::bind_method(D_METHOD("get_varying_type"), &VisualShaderNodeVarying::get_varying_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "varying_name"), "set_varying_name", "get_varying_name");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "varying_type", PROPERTY_HINT_ENUM, "Float,Vector,Transform"), "set_varying_type", "get_varying_type");
+}
+
+String VisualShaderNodeVarying::get_type_str() const {
+ switch (varying_type) {
+ case VisualShader::VARYING_TYPE_FLOAT:
+ return "float";
+ case VisualShader::VARYING_TYPE_VECTOR_2D:
+ return "vec2";
+ case VisualShader::VARYING_TYPE_VECTOR_3D:
+ return "vec3";
+ case VisualShader::VARYING_TYPE_VECTOR_4D:
+ return "vec4";
+ case VisualShader::VARYING_TYPE_COLOR:
+ return "vec4";
+ case VisualShader::VARYING_TYPE_TRANSFORM:
+ return "mat4";
+ default:
+ break;
+ }
+ return "";
+}
+
+VisualShaderNodeVarying::PortType VisualShaderNodeVarying::get_port_type(VisualShader::VaryingType p_type, int p_port) const {
+ switch (p_type) {
+ case VisualShader::VARYING_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case VisualShader::VARYING_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case VisualShader::VARYING_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ case VisualShader::VARYING_TYPE_COLOR:
+ if (p_port == 1) {
+ break; // scalar
+ }
+ return PORT_TYPE_VECTOR_3D;
+ case VisualShader::VARYING_TYPE_TRANSFORM:
+ return PORT_TYPE_TRANSFORM;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+void VisualShaderNodeVarying::set_varying_name(String p_varying_name) {
+ if (varying_name == p_varying_name) {
+ return;
+ }
+ varying_name = p_varying_name;
+ emit_changed();
+}
+
+String VisualShaderNodeVarying::get_varying_name() const {
+ return varying_name;
+}
+
+void VisualShaderNodeVarying::set_varying_type(VisualShader::VaryingType p_varying_type) {
+ ERR_FAIL_INDEX(p_varying_type, VisualShader::VARYING_TYPE_MAX);
+ if (varying_type == p_varying_type) {
+ return;
+ }
+ varying_type = p_varying_type;
+ emit_changed();
+}
+
+VisualShader::VaryingType VisualShaderNodeVarying::get_varying_type() const {
+ return varying_type;
+}
+
+VisualShaderNodeVarying::VisualShaderNodeVarying() {
+}
+
+////////////// Varying Setter
+
+String VisualShaderNodeVaryingSetter::get_caption() const {
+ return vformat("VaryingSetter");
+}
+
+int VisualShaderNodeVaryingSetter::get_input_port_count() const {
+ if (varying_type == VisualShader::VARYING_TYPE_COLOR) {
+ return 2;
+ }
+ return 1;
+}
+
+VisualShaderNodeVaryingSetter::PortType VisualShaderNodeVaryingSetter::get_input_port_type(int p_port) const {
+ return get_port_type(varying_type, p_port);
+}
+
+String VisualShaderNodeVaryingSetter::get_input_port_name(int p_port) const {
+ if (varying_type == VisualShader::VARYING_TYPE_COLOR) {
+ if (p_port == 0) {
+ return "color";
+ } else {
+ return "alpha";
+ }
+ }
+ return "";
+}
+
+int VisualShaderNodeVaryingSetter::get_output_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeVaryingSetter::PortType VisualShaderNodeVaryingSetter::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeVaryingSetter::get_output_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeVaryingSetter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return vformat("varying %s %s;\n", get_type_str(), varying_name);
+}
+
+String VisualShaderNodeVaryingSetter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String code;
+ if (varying_name == "[None]") {
+ return code;
+ }
+ if (varying_type == VisualShader::VARYING_TYPE_COLOR) {
+ code += vformat(" %s = vec4(%s, %s);\n", varying_name, p_input_vars[0], p_input_vars[1]);
+ } else {
+ code += vformat(" %s = %s;\n", varying_name, p_input_vars[0]);
+ }
+ return code;
+}
+
+VisualShaderNodeVaryingSetter::VisualShaderNodeVaryingSetter() {
+}
+
+////////////// Varying Getter
+
+String VisualShaderNodeVaryingGetter::get_caption() const {
+ return vformat("VaryingGetter");
+}
+
+int VisualShaderNodeVaryingGetter::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeVaryingGetter::PortType VisualShaderNodeVaryingGetter::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeVaryingGetter::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeVaryingGetter::get_output_port_count() const {
+ if (varying_type == VisualShader::VARYING_TYPE_COLOR) {
+ return 2;
+ }
+ return 1;
+}
+
+VisualShaderNodeVaryingGetter::PortType VisualShaderNodeVaryingGetter::get_output_port_type(int p_port) const {
+ return get_port_type(varying_type, p_port);
+}
+
+String VisualShaderNodeVaryingGetter::get_output_port_name(int p_port) const {
+ if (varying_type == VisualShader::VARYING_TYPE_COLOR) {
+ if (p_port == 0) {
+ return "color";
+ } else {
+ return "alpha";
+ }
+ }
+ return "";
+}
+
+bool VisualShaderNodeVaryingGetter::has_output_port_preview(int p_port) const {
+ return false;
+}
+
+String VisualShaderNodeVaryingGetter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String from = varying_name;
+ String from2;
+
+ if (varying_name == "[None]") {
+ switch (varying_type) {
+ case VisualShader::VARYING_TYPE_FLOAT:
+ from = "0.0";
+ break;
+ case VisualShader::VARYING_TYPE_VECTOR_2D:
+ from = "vec2(0.0)";
+ break;
+ case VisualShader::VARYING_TYPE_VECTOR_3D:
+ from = "vec3(0.0)";
+ break;
+ case VisualShader::VARYING_TYPE_VECTOR_4D:
+ from = "vec4(0.0)";
+ break;
+ case VisualShader::VARYING_TYPE_COLOR:
+ from = "vec3(0.0)";
+ from2 = "0.0";
+ break;
+ case VisualShader::VARYING_TYPE_TRANSFORM:
+ from = "mat4(1.0)";
+ break;
+ default:
+ break;
+ }
+ } else if (varying_type == VisualShader::VARYING_TYPE_COLOR) {
+ from = varying_name + ".rgb";
+ from2 = varying_name + ".a";
+ }
+
+ if (varying_type == VisualShader::VARYING_TYPE_COLOR) {
+ String code;
+ code += vformat(" %s = %s;\n", p_output_vars[0], from);
+ code += vformat(" %s = %s;\n", p_output_vars[1], from2);
+ return code;
+ }
+ return vformat(" %s = %s;\n", p_output_vars[0], from);
+}
+
+VisualShaderNodeVaryingGetter::VisualShaderNodeVaryingGetter() {
+ varying_name = "[None]";
+}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 5bb9d45f39..aa5263943b 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -73,6 +73,50 @@ public:
List<Ref<Texture2D>> params;
};
+ enum VaryingMode {
+ VARYING_MODE_VERTEX_TO_FRAG_LIGHT,
+ VARYING_MODE_FRAG_TO_LIGHT,
+ VARYING_MODE_MAX,
+ };
+
+ enum VaryingType {
+ VARYING_TYPE_FLOAT,
+ VARYING_TYPE_VECTOR_2D,
+ VARYING_TYPE_VECTOR_3D,
+ VARYING_TYPE_VECTOR_4D,
+ VARYING_TYPE_COLOR,
+ VARYING_TYPE_TRANSFORM,
+ VARYING_TYPE_MAX,
+ };
+
+ struct Varying {
+ String name;
+ VaryingMode mode;
+ VaryingType type;
+
+ Varying() {
+ }
+
+ Varying(String p_name, VaryingMode p_mode, VaryingType p_type) :
+ name(p_name), mode(p_mode), type(p_type) {}
+
+ bool from_string(const String &p_str) {
+ Vector<String> arr = p_str.split(",");
+ if (arr.size() != 2) {
+ return false;
+ }
+
+ mode = (VaryingMode)arr[0].to_int();
+ type = (VaryingType)arr[1].to_int();
+
+ return true;
+ }
+
+ String to_string() const {
+ return vformat("%s,%s", itos((int)mode), itos((int)type));
+ }
+ };
+
private:
Type current_type;
@@ -83,7 +127,7 @@ private:
};
struct Graph {
- Map<int, Node> nodes;
+ RBMap<int, Node> nodes;
List<Connection> connections;
} graph[TYPE_MAX];
@@ -94,15 +138,11 @@ private:
Vector2 graph_offset;
- struct RenderModeEnums {
- Shader::Mode mode = Shader::Mode::MODE_MAX;
- const char *string;
- };
-
HashMap<String, int> modes;
- Set<StringName> flags;
+ RBSet<StringName> flags;
- static RenderModeEnums render_mode_enums[];
+ HashMap<String, Varying> varyings;
+ List<Varying> varyings_list;
mutable SafeFlag dirty;
void _queue_update();
@@ -118,7 +158,7 @@ private:
}
};
- Error _write_node(Type p_type, StringBuilder &global_code, StringBuilder &global_code_per_node, Map<Type, StringBuilder> &global_code_per_func, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const;
+ Error _write_node(Type p_type, StringBuilder *global_code, StringBuilder *global_code_per_node, HashMap<Type, StringBuilder> *global_code_per_func, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, RBSet<int> &processed, bool for_preview, RBSet<StringName> &r_classes) const;
void _input_type_changed(Type p_type, int p_id);
bool has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const;
@@ -153,6 +193,18 @@ public:
void add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id);
void set_node_position(Type p_type, int p_id, const Vector2 &p_position);
+ void add_varying(const String &p_name, VaryingMode p_mode, VaryingType p_type);
+ void remove_varying(const String &p_name);
+ bool has_varying(const String &p_name) const;
+ int get_varyings_count() const;
+ const Varying *get_varying_by_index(int p_idx) const;
+
+ void set_varying_mode(const String &p_name, VaryingMode p_mode);
+ VaryingMode get_varying_mode(const String &p_name);
+
+ void set_varying_type(const String &p_name, VaryingType p_type);
+ VaryingType get_varying_type(const String &p_name);
+
Vector2 get_node_position(Type p_type, int p_id) const;
Ref<VisualShaderNode> get_node(Type p_type, int p_id) const;
@@ -192,6 +244,8 @@ public:
};
VARIANT_ENUM_CAST(VisualShader::Type)
+VARIANT_ENUM_CAST(VisualShader::VaryingMode)
+VARIANT_ENUM_CAST(VisualShader::VaryingType)
///
///
///
@@ -201,10 +255,10 @@ class VisualShaderNode : public Resource {
int port_preview = -1;
- Map<int, Variant> default_input_values;
- Map<int, bool> connected_input_ports;
- Map<int, int> connected_output_ports;
- Map<int, bool> expanded_output_ports;
+ HashMap<int, Variant> default_input_values;
+ HashMap<int, bool> connected_input_ports;
+ HashMap<int, int> connected_output_ports;
+ HashMap<int, bool> expanded_output_ports;
protected:
bool simple_decl = true;
@@ -216,7 +270,9 @@ public:
enum PortType {
PORT_TYPE_SCALAR,
PORT_TYPE_SCALAR_INT,
- PORT_TYPE_VECTOR,
+ PORT_TYPE_VECTOR_2D,
+ PORT_TYPE_VECTOR_3D,
+ PORT_TYPE_VECTOR_4D,
PORT_TYPE_BOOLEAN,
PORT_TYPE_TRANSFORM,
PORT_TYPE_SAMPLER,
@@ -231,7 +287,7 @@ public:
virtual PortType get_input_port_type(int p_port) const = 0;
virtual String get_input_port_name(int p_port) const = 0;
- virtual void set_input_port_default_value(int p_port, const Variant &p_value);
+ virtual void set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value = Variant());
Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied)
Array get_default_input_values() const;
virtual void set_default_input_values(const Array &p_values);
@@ -242,7 +298,7 @@ public:
virtual PortType get_output_port_type(int p_port) const = 0;
virtual String get_output_port_name(int p_port) const = 0;
- virtual String get_input_port_default_hint(int p_port) const;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const;
void set_output_port_for_preview(int p_index);
int get_output_port_for_preview() const;
@@ -272,11 +328,11 @@ public:
void set_disabled(bool p_disabled = true);
virtual Vector<StringName> get_editable_properties() const;
- virtual Map<StringName, String> get_editable_properties_names() const;
+ virtual HashMap<StringName, String> get_editable_properties_names() const;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const;
virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
// If no output is connected, the output var passed will be empty. If no input is connected and input is NIL, the input var passed will be empty.
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const = 0;
@@ -313,7 +369,7 @@ protected:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual void set_input_port_default_value(int p_port, const Variant &p_value) override;
+ virtual void set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value = Variant()) override;
virtual void set_default_input_values(const Array &p_values) override;
virtual void remove_input_port_default_value(int p_port) override;
virtual void clear_default_input_values() override;
@@ -328,15 +384,21 @@ protected:
GDVIRTUAL0RC(int, _get_output_port_count)
GDVIRTUAL1RC(int, _get_output_port_type, int)
GDVIRTUAL1RC(String, _get_output_port_name, int)
- GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, Shader::Mode, VisualShader::Type)
+ GDVIRTUAL4RC(String, _get_code, TypedArray<String>, TypedArray<String>, Shader::Mode, VisualShader::Type)
+ GDVIRTUAL2RC(String, _get_func_code, Shader::Mode, VisualShader::Type)
GDVIRTUAL1RC(String, _get_global_code, Shader::Mode)
GDVIRTUAL0RC(bool, _is_highend)
+ GDVIRTUAL2RC(bool, _is_available, Shader::Mode, VisualShader::Type)
+
+ bool _is_valid_code(const String &p_code) const;
protected:
void _set_input_port_default_value(int p_port, const Variant &p_value);
+ bool is_available(Shader::Mode p_mode, VisualShader::Type p_type) const;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
+ virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
static void _bind_methods();
@@ -386,6 +448,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) const override;
virtual String get_caption() const override;
@@ -494,7 +557,9 @@ public:
UNIFORM_TYPE_FLOAT,
UNIFORM_TYPE_INT,
UNIFORM_TYPE_BOOLEAN,
- UNIFORM_TYPE_VECTOR,
+ UNIFORM_TYPE_VECTOR2,
+ UNIFORM_TYPE_VECTOR3,
+ UNIFORM_TYPE_VECTOR4,
UNIFORM_TYPE_TRANSFORM,
UNIFORM_TYPE_COLOR,
UNIFORM_TYPE_SAMPLER,
@@ -615,9 +680,9 @@ protected:
String name;
};
- Map<int, Port> input_ports;
- Map<int, Port> output_ports;
- Map<int, Control *> controls;
+ HashMap<int, Port> input_ports;
+ HashMap<int, Port> output_ports;
+ HashMap<int, Control *> controls;
protected:
static void _bind_methods();
@@ -697,6 +762,103 @@ public:
VisualShaderNodeGlobalExpression();
};
+class VisualShaderNodeVarying : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVarying, VisualShaderNode);
+
+public:
+ struct Varying {
+ String name;
+ VisualShader::VaryingMode mode;
+ VisualShader::VaryingType type;
+ bool assigned = false;
+ };
+
+protected:
+ VisualShader::VaryingType varying_type = VisualShader::VARYING_TYPE_FLOAT;
+ String varying_name = "[None]";
+
+public: // internal
+ static void add_varying(const String &p_name, VisualShader::VaryingMode p_mode, VisualShader::VaryingType p_type);
+ static void clear_varyings();
+ static bool has_varying(const String &p_name);
+
+ int get_varyings_count() const;
+ String get_varying_name_by_index(int p_idx) const;
+ VisualShader::VaryingMode get_varying_mode_by_name(const String &p_name) const;
+ VisualShader::VaryingMode get_varying_mode_by_index(int p_idx) const;
+ VisualShader::VaryingType get_varying_type_by_name(const String &p_name) const;
+ VisualShader::VaryingType get_varying_type_by_index(int p_idx) const;
+ PortType get_port_type_by_index(int p_idx) const;
+
+protected:
+ static void _bind_methods();
+
+protected:
+ String get_type_str() const;
+ PortType get_port_type(VisualShader::VaryingType p_type, int p_port) const;
+
+public:
+ virtual String get_caption() const override = 0;
+
+ virtual int get_input_port_count() const override = 0;
+ virtual PortType get_input_port_type(int p_port) const override = 0;
+ virtual String get_input_port_name(int p_port) const override = 0;
+
+ virtual int get_output_port_count() const override = 0;
+ virtual PortType get_output_port_type(int p_port) const override = 0;
+ virtual String get_output_port_name(int p_port) const override = 0;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override = 0;
+
+ void set_varying_name(String p_varying_name);
+ String get_varying_name() const;
+
+ void set_varying_type(VisualShader::VaryingType p_varying_type);
+ VisualShader::VaryingType get_varying_type() const;
+
+ VisualShaderNodeVarying();
+};
+
+class VisualShaderNodeVaryingSetter : public VisualShaderNodeVarying {
+ GDCLASS(VisualShaderNodeVaryingSetter, VisualShaderNodeVarying);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ VisualShaderNodeVaryingSetter();
+};
+
+class VisualShaderNodeVaryingGetter : public VisualShaderNodeVarying {
+ GDCLASS(VisualShaderNodeVaryingGetter, VisualShaderNodeVarying);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ VisualShaderNodeVaryingGetter();
+};
+
extern String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name);
#endif // VISUAL_SHADER_H
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 951870fe34..dbd45793f9 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,70 @@
#include "visual_shader_nodes.h"
+////////////// Vector Base
+
+VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_input_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_output_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+void VisualShaderNodeVectorBase::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
+VisualShaderNodeVectorBase::OpType VisualShaderNodeVectorBase::get_op_type() const {
+ return op_type;
+}
+
+void VisualShaderNodeVectorBase::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeVectorBase::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeVectorBase::get_op_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Vector2,Vector3,Vector4"), "set_op_type", "get_op_type");
+
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
+}
+
+Vector<StringName> VisualShaderNodeVectorBase::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
+}
+
+VisualShaderNodeVectorBase::VisualShaderNodeVectorBase() {
+}
+
////////////// Constants Base
VisualShaderNodeConstant::VisualShaderNodeConstant() {
@@ -232,7 +296,7 @@ int VisualShaderNodeColorConstant::get_input_port_count() const {
}
VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_4D;
}
String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const {
@@ -240,15 +304,15 @@ String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const {
}
int VisualShaderNodeColorConstant::get_output_port_count() const {
- return 2;
+ return 1;
}
VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const {
- return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port
+ return "";
}
bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const {
@@ -259,11 +323,7 @@ bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const
}
String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- String code;
- code += " " + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.r, constant.g, constant.b) + ";\n";
- code += " " + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n";
-
- return code;
+ return " " + p_output_vars[0] + " = " + vformat("vec4(%.6f, %.6f, %.6f, %.6f)", constant.r, constant.g, constant.b, constant.a) + ";\n";
}
void VisualShaderNodeColorConstant::set_constant(const Color &p_constant) {
@@ -294,10 +354,72 @@ void VisualShaderNodeColorConstant::_bind_methods() {
VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() {
}
-////////////// Vector
+////////////// Vector2
+
+String VisualShaderNodeVec2Constant::get_caption() const {
+ return "Vector2Constant";
+}
+
+int VisualShaderNodeVec2Constant::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeVec2Constant::PortType VisualShaderNodeVec2Constant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeVec2Constant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec2Constant::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVec2Constant::PortType VisualShaderNodeVec2Constant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeVec2Constant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeVec2Constant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return " " + p_output_vars[0] + " = " + vformat("vec2(%.6f, %.6f)", constant.x, constant.y) + ";\n";
+}
+
+void VisualShaderNodeVec2Constant::set_constant(const Vector2 &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
+ emit_changed();
+}
+
+Vector2 VisualShaderNodeVec2Constant::get_constant() const {
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeVec2Constant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeVec2Constant::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeVec2Constant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec2Constant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeVec2Constant::VisualShaderNodeVec2Constant() {
+}
+
+////////////// Vector3
String VisualShaderNodeVec3Constant::get_caption() const {
- return "VectorConstant";
+ return "Vector3Constant";
}
int VisualShaderNodeVec3Constant::get_input_port_count() const {
@@ -305,7 +427,7 @@ int VisualShaderNodeVec3Constant::get_input_port_count() const {
}
VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVec3Constant::get_input_port_name(int p_port) const {
@@ -317,7 +439,7 @@ int VisualShaderNodeVec3Constant::get_output_port_count() const {
}
VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const {
@@ -356,6 +478,68 @@ void VisualShaderNodeVec3Constant::_bind_methods() {
VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() {
}
+////////////// Vector4
+
+String VisualShaderNodeVec4Constant::get_caption() const {
+ return "Vector4Constant";
+}
+
+int VisualShaderNodeVec4Constant::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeVec4Constant::PortType VisualShaderNodeVec4Constant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_4D;
+}
+
+String VisualShaderNodeVec4Constant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec4Constant::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVec4Constant::PortType VisualShaderNodeVec4Constant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_4D;
+}
+
+String VisualShaderNodeVec4Constant::get_output_port_name(int p_port) const {
+ return ""; // No output port means the editor will be used as port.
+}
+
+String VisualShaderNodeVec4Constant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return " " + p_output_vars[0] + " = " + vformat("vec4(%.6f, %.6f, %.6f, %.6f)", constant.x, constant.y, constant.z, constant.w) + ";\n";
+}
+
+void VisualShaderNodeVec4Constant::set_constant(const Quaternion &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
+ emit_changed();
+}
+
+Quaternion VisualShaderNodeVec4Constant::get_constant() const {
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeVec4Constant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeVec4Constant::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeVec4Constant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec4Constant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeVec4Constant::VisualShaderNodeVec4Constant() {
+}
+
////////////// Transform3D
String VisualShaderNodeTransformConstant::get_caption() const {
@@ -367,7 +551,7 @@ int VisualShaderNodeTransformConstant::get_input_port_count() const {
}
VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformConstant::get_input_port_name(int p_port) const {
@@ -439,7 +623,7 @@ int VisualShaderNodeTexture::get_input_port_count() const {
VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
case 1:
return PORT_TYPE_SCALAR;
case 2:
@@ -463,21 +647,25 @@ String VisualShaderNodeTexture::get_input_port_name(int p_port) const {
}
int VisualShaderNodeTexture::get_output_port_count() const {
- return 2;
+ return 1;
}
VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(int p_port) const {
- if (p_port == 0 && source == SOURCE_DEPTH) {
- return PORT_TYPE_SCALAR;
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ return PORT_TYPE_SCALAR;
}
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
}
String VisualShaderNodeTexture::get_output_port_name(int p_port) const {
- if (p_port == 0 && source == SOURCE_DEPTH) {
- return "depth";
+ switch (p_port) {
+ case 0:
+ return "color";
+ default:
+ return "";
}
- return p_port == 0 ? "rgb" : "alpha";
}
bool VisualShaderNodeTexture::is_output_port_expandable(int p_port) const {
@@ -487,11 +675,13 @@ bool VisualShaderNodeTexture::is_output_port_expandable(int p_port) const {
return false;
}
-String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "default";
+bool VisualShaderNodeTexture::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
@@ -526,176 +716,122 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String default_uv;
- if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
- default_uv = "UV.xy";
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ default_uv = "UV";
} else {
default_uv = "vec2(0.0)";
}
+ String code;
if (source == SOURCE_TEXTURE) {
String id = make_unique_id(p_type, p_id, "tex");
- String code;
- if (p_input_vars[0] == String()) { // Use UV by default.
-
- if (p_input_vars[1] == String()) {
- code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n";
+ if (p_input_vars[0].is_empty()) { // Use UV by default.
+ if (p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
-
- } else if (p_input_vars[1] == String()) {
+ } else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
-
- code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n";
- code += " " + p_output_vars[1] + " = " + id + "_read.a;\n";
return code;
}
if (source == SOURCE_PORT) {
String id = p_input_vars[2];
-
- String code;
- code += " {\n";
- if (id == String()) {
- code += " vec4 " + id + "_tex_read = vec4(0.0);\n";
+ if (id.is_empty()) {
+ code += " " + p_output_vars[0] + " = vec4(0.0);\n";
} else {
- if (p_input_vars[0] == String()) { // Use UV by default.
-
- if (p_input_vars[1] == String()) {
- code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n";
+ if (p_input_vars[0].is_empty()) { // Use UV by default.
+ if (p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
-
- } else if (p_input_vars[1] == String()) {
+ } else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
-
- code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
- code += " " + p_output_vars[1] + " = " + id + "_tex_read.a;\n";
}
- code += " }\n";
return code;
}
if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) {
- String code = " {\n";
- if (p_input_vars[0] == String() || p_for_preview) { // Use UV by default.
-
- if (p_input_vars[1] == String()) {
- code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0 );\n";
+ if (p_input_vars[0].is_empty() || p_for_preview) { // Use UV by default.
+ if (p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0);\n";
} else {
- code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
-
- } else if (p_input_vars[1] == String()) {
+ } else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, 0.0);\n";
+ code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", 0.0);\n";
} else {
- code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
-
- code += " " + p_output_vars[0] + " = _tex_read.rgb;\n";
- code += " " + p_output_vars[1] + " = _tex_read.a;\n";
- code += " }\n";
return code;
}
if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
- String code = " {\n";
- if (p_input_vars[0] == String()) { // Use UV by default.
-
- if (p_input_vars[1] == String()) {
- code += " vec4 _tex_read = texture(TEXTURE, " + default_uv + ");\n";
+ if (p_input_vars[0].is_empty()) { // Use UV by default.
+ if (p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = texture(TEXTURE, " + default_uv + ");\n";
} else {
- code += " vec4 _tex_read = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
-
- } else if (p_input_vars[1] == String()) {
+ } else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ".xy);\n";
+ code += " " + p_output_vars[0] + " = texture(TEXTURE, " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
-
- code += " " + p_output_vars[0] + " = _tex_read.rgb;\n";
- code += " " + p_output_vars[1] + " = _tex_read.a;\n";
- code += " }\n";
return code;
}
if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
- String code = " {\n";
- if (p_input_vars[0] == String()) { // Use UV by default.
-
- if (p_input_vars[1] == String()) {
- code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + default_uv + ");\n";
+ if (p_input_vars[0].is_empty()) { // Use UV by default.
+ if (p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = texture(NORMAL_TEXTURE, " + default_uv + ");\n";
} else {
- code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
-
- } else if (p_input_vars[1] == String()) {
+ } else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n";
+ code += " " + p_output_vars[0] + " = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n";
} else {
- code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
}
-
- code += " " + p_output_vars[0] + " = _tex_read.rgb;\n";
- code += " " + p_output_vars[1] + " = _tex_read.a;\n";
- code += " }\n";
return code;
}
- if (p_for_preview) // DEPTH_TEXTURE is not supported in preview(canvas_item) shader
- {
- if (source == SOURCE_DEPTH) {
- String code;
- code += " " + p_output_vars[0] + " = 0.0;\n";
- code += " " + p_output_vars[1] + " = 1.0;\n";
- return code;
- }
- }
-
- if (source == SOURCE_DEPTH && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) {
- String code = " {\n";
- if (p_input_vars[0] == String()) { // Use UV by default.
-
- if (p_input_vars[1] == String()) {
- code += " float _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n";
+ if (source == SOURCE_DEPTH) {
+ if (!p_for_preview && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) {
+ code += " {\n";
+ if (p_input_vars[0].is_empty()) { // Use UV by default.
+ if (p_input_vars[1].is_empty()) {
+ code += " float _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n";
+ } else {
+ code += " float _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n";
+ }
+ } else if (p_input_vars[1].is_empty()) {
+ //no lod
+ code += " float _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n";
} else {
- code += " float _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n";
+ code += " float _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n";
}
- } else if (p_input_vars[1] == String()) {
- //no lod
- code += " float _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n";
- } else {
- code += " float _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n";
+ code += " " + p_output_vars[0] + " = vec4(_depth, _depth, _depth, 1.0);\n";
+ code += " }\n";
+ return code;
}
-
- code += " " + p_output_vars[0] + " = _depth;\n";
- code += " " + p_output_vars[1] + " = 1.0;\n";
- code += " }\n";
- return code;
- } else if (source == SOURCE_DEPTH) {
- String code;
- code += " " + p_output_vars[0] + " = 0.0;\n";
- code += " " + p_output_vars[1] + " = 1.0;\n";
- return code;
}
- //none
- String code;
- code += " " + p_output_vars[0] + " = vec3(0.0);\n";
- code += " " + p_output_vars[1] + " = 1.0;\n";
+ code += " " + p_output_vars[0] + " = vec4(0.0);\n";
return code;
}
@@ -769,7 +905,7 @@ Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const {
String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
if (is_input_port_connected(2) && source != SOURCE_PORT) {
- return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
}
if (source == SOURCE_TEXTURE) {
@@ -794,12 +930,12 @@ String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::T
if (source == SOURCE_DEPTH && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) {
if (get_output_port_for_preview() == 0) { // DEPTH_TEXTURE is not supported in preview(canvas_item) shader
- return TTR("Invalid source for preview.");
+ return RTR("Invalid source for preview.");
}
return String(); // all good
}
- return TTR("Invalid source for shader.");
+ return RTR("Invalid source for shader.");
}
void VisualShaderNodeTexture::_bind_methods() {
@@ -883,7 +1019,7 @@ String VisualShaderNodeCurveTexture::generate_global(Shader::Mode p_mode, Visual
}
String VisualShaderNodeCurveTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- if (p_input_vars[0] == String()) {
+ if (p_input_vars[0].is_empty()) {
return " " + p_output_vars[0] + " = 0.0;\n";
}
String id = make_unique_id(p_type, p_id, "curve");
@@ -941,7 +1077,7 @@ int VisualShaderNodeCurveXYZTexture::get_output_port_count() const {
}
VisualShaderNodeCurveXYZTexture::PortType VisualShaderNodeCurveXYZTexture::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeCurveXYZTexture::get_output_port_name(int p_port) const {
@@ -968,7 +1104,7 @@ String VisualShaderNodeCurveXYZTexture::generate_global(Shader::Mode p_mode, Vis
}
String VisualShaderNodeCurveXYZTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- if (p_input_vars[0] == String()) {
+ if (p_input_vars[0].is_empty()) {
return " " + p_output_vars[0] + " = vec3(0.0);\n";
}
String id = make_unique_id(p_type, p_id, "curve3d");
@@ -1012,7 +1148,7 @@ int VisualShaderNodeSample3D::get_input_port_count() const {
VisualShaderNodeSample3D::PortType VisualShaderNodeSample3D::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 1:
return PORT_TYPE_SCALAR;
case 2:
@@ -1034,15 +1170,15 @@ String VisualShaderNodeSample3D::get_input_port_name(int p_port) const {
}
int VisualShaderNodeSample3D::get_output_port_count() const {
- return 2;
+ return 1;
}
VisualShaderNodeSample3D::PortType VisualShaderNodeSample3D::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeSample3D::get_output_port_name(int p_port) const {
- return p_port == 0 ? "rgb" : "alpha";
+ return "color";
}
bool VisualShaderNodeSample3D::is_output_port_expandable(int p_port) const {
@@ -1052,16 +1188,18 @@ bool VisualShaderNodeSample3D::is_output_port_expandable(int p_port) const {
return false;
}
-String VisualShaderNodeSample3D::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "default";
+bool VisualShaderNodeSample3D::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String default_uv;
- if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
default_uv = "vec3(UV, 0.0)";
} else {
default_uv = "vec3(0.0)";
@@ -1070,36 +1208,30 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader
String code;
if (source == SOURCE_TEXTURE || source == SOURCE_PORT) {
String id;
- code += " {\n";
if (source == SOURCE_TEXTURE) {
id = make_unique_id(p_type, p_id, "tex3d");
} else {
id = p_input_vars[2];
}
- if (id != String()) {
- if (p_input_vars[0] == String()) { // Use UV by default.
- if (p_input_vars[1] == String()) {
- code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n";
+ if (!id.is_empty()) {
+ if (p_input_vars[0].is_empty()) { // Use UV by default.
+ if (p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
- } else if (p_input_vars[1] == String()) {
+ } else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
} else {
- code += " vec4 " + id + "_tex_read = vec4(0.0);\n";
+ code += " " + p_output_vars[0] + " = vec4(0.0);\n";
}
-
- code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
- code += " " + p_output_vars[1] + " = " + id + "_tex_read.a;\n";
- code += " }\n";
return code;
}
- code += " " + p_output_vars[0] + " = vec3(0.0);\n";
- code += " " + p_output_vars[1] + " = 1.0;\n";
+ code += " " + p_output_vars[0] + " = vec4(0.0);\n";
return code;
}
@@ -1130,7 +1262,7 @@ void VisualShaderNodeSample3D::_bind_methods() {
String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
if (is_input_port_connected(2) && source != SOURCE_PORT) {
- return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
}
if (source == SOURCE_TEXTURE) {
@@ -1139,7 +1271,7 @@ String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::
if (source == SOURCE_PORT) {
return String(); // all good
}
- return TTR("Invalid source for shader.");
+ return RTR("Invalid source for shader.");
}
VisualShaderNodeSample3D::VisualShaderNodeSample3D() {
@@ -1273,7 +1405,7 @@ int VisualShaderNodeCubemap::get_input_port_count() const {
VisualShaderNodeCubemap::PortType VisualShaderNodeCubemap::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 1:
return PORT_TYPE_SCALAR;
case 2:
@@ -1297,15 +1429,15 @@ String VisualShaderNodeCubemap::get_input_port_name(int p_port) const {
}
int VisualShaderNodeCubemap::get_output_port_count() const {
- return 2;
+ return 1;
}
VisualShaderNodeCubemap::PortType VisualShaderNodeCubemap::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return PORT_TYPE_VECTOR_4D;
}
String VisualShaderNodeCubemap::get_output_port_name(int p_port) const {
- return p_port == 0 ? "rgb" : "alpha";
+ return "color";
}
bool VisualShaderNodeCubemap::is_output_port_expandable(int p_port) const {
@@ -1346,7 +1478,7 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade
String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String default_uv;
- if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
default_uv = "vec3(UV, 0.0)";
} else {
default_uv = "vec3(0.0)";
@@ -1359,45 +1491,38 @@ String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader:
} else if (source == SOURCE_PORT) {
id = p_input_vars[2];
} else {
- return String();
+ return code;
}
- code += " {\n";
-
- if (id == String()) {
- code += " vec4 " + id + "_read = vec4(0.0);\n";
- code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n";
- code += " " + p_output_vars[1] + " = " + id + "_read.a;\n";
- code += " }\n";
+ if (id.is_empty()) {
+ code += " " + p_output_vars[0] + " = vec4(0.0);\n";
return code;
}
- if (p_input_vars[0] == String()) { // Use UV by default.
+ if (p_input_vars[0].is_empty()) { // Use UV by default.
- if (p_input_vars[1] == String()) {
- code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n";
+ if (p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + " );\n";
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
- } else if (p_input_vars[1] == String()) {
+ } else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
- code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n";
- code += " " + p_output_vars[1] + " = " + id + "_read.a;\n";
- code += " }\n";
-
return code;
}
-String VisualShaderNodeCubemap::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "default";
+bool VisualShaderNodeCubemap::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
void VisualShaderNodeCubemap::set_source(Source p_source) {
@@ -1448,7 +1573,7 @@ Vector<StringName> VisualShaderNodeCubemap::get_editable_properties() const {
String VisualShaderNodeCubemap::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
if (is_input_port_connected(2) && source != SOURCE_PORT) {
- return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
}
return String();
}
@@ -1647,6 +1772,21 @@ String VisualShaderNodeIntOp::generate_code(Shader::Mode p_mode, VisualShader::T
case OP_MIN:
code += "min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ case OP_BITWISE_AND:
+ code += p_input_vars[0] + " & " + p_input_vars[1] + ";\n";
+ break;
+ case OP_BITWISE_OR:
+ code += p_input_vars[0] + " | " + p_input_vars[1] + ";\n";
+ break;
+ case OP_BITWISE_XOR:
+ code += p_input_vars[0] + " ^ " + p_input_vars[1] + ";\n";
+ break;
+ case OP_BITWISE_LEFT_SHIFT:
+ code += p_input_vars[0] + " << " + p_input_vars[1] + ";\n";
+ break;
+ case OP_BITWISE_RIGHT_SHIFT:
+ code += p_input_vars[0] + " >> " + p_input_vars[1] + ";\n";
+ break;
default:
break;
}
@@ -1677,7 +1817,7 @@ void VisualShaderNodeIntOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeIntOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeIntOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Max,Min"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Max,Min,Bitwise AND,Bitwise OR,Bitwise XOR,Bitwise Left Shift,Bitwise Right Shift"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -1686,6 +1826,11 @@ void VisualShaderNodeIntOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MOD);
BIND_ENUM_CONSTANT(OP_MAX);
BIND_ENUM_CONSTANT(OP_MIN);
+ BIND_ENUM_CONSTANT(OP_BITWISE_AND);
+ BIND_ENUM_CONSTANT(OP_BITWISE_OR);
+ BIND_ENUM_CONSTANT(OP_BITWISE_XOR);
+ BIND_ENUM_CONSTANT(OP_BITWISE_LEFT_SHIFT);
+ BIND_ENUM_CONSTANT(OP_BITWISE_RIGHT_SHIFT);
BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
@@ -1704,10 +1849,6 @@ int VisualShaderNodeVectorOp::get_input_port_count() const {
return 2;
}
-VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorOp::get_input_port_name(int p_port) const {
return p_port == 0 ? "a" : "b";
}
@@ -1716,12 +1857,8 @@ int VisualShaderNodeVectorOp::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const {
- return "op"; //no output port means the editor will be used as port
+ return "op";
}
String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
@@ -1752,13 +1889,25 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
code += "min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
case OP_CROSS:
- code += "cross(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ if (op_type == OP_TYPE_VECTOR_2D) { // Not supported.
+ code += "vec2(0.0);\n";
+ } else if (op_type == OP_TYPE_VECTOR_4D) { // Not supported.
+ code += "vec4(0.0);\n";
+ } else {
+ code += "cross(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ }
break;
case OP_ATAN2:
code += "atan(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
case OP_REFLECT:
- code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ if (op_type == OP_TYPE_VECTOR_2D) { // Not supported.
+ code += "vec2(0.0);\n";
+ } else if (op_type == OP_TYPE_VECTOR_4D) { // Not supported.
+ code += "vec4(0.0);\n";
+ } else {
+ code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ }
break;
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
@@ -1770,6 +1919,31 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
return code;
}
+void VisualShaderNodeVectorOp::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
void VisualShaderNodeVectorOp::set_operator(Operator p_op) {
ERR_FAIL_INDEX(int(p_op), int(OP_ENUM_SIZE));
if (op == p_op) {
@@ -1784,11 +1958,27 @@ VisualShaderNodeVectorOp::Operator VisualShaderNodeVectorOp::get_operator() cons
}
Vector<StringName> VisualShaderNodeVectorOp::get_editable_properties() const {
- Vector<StringName> props;
+ Vector<StringName> props = VisualShaderNodeVectorBase::get_editable_properties();
props.push_back("operator");
return props;
}
+String VisualShaderNodeVectorOp::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ bool invalid_type = false;
+
+ if (op_type == OP_TYPE_VECTOR_2D || op_type == OP_TYPE_VECTOR_4D) {
+ if (op == OP_CROSS || op == OP_REFLECT) {
+ invalid_type = true;
+ }
+ }
+
+ if (invalid_type) {
+ return RTR("Invalid operator for that type.");
+ }
+
+ return String();
+}
+
void VisualShaderNodeVectorOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator);
@@ -1811,8 +2001,22 @@ void VisualShaderNodeVectorOp::_bind_methods() {
}
VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
- set_input_port_default_value(0, Vector3());
- set_input_port_default_value(1, Vector3());
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2());
+ set_input_port_default_value(1, Vector2());
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion());
+ set_input_port_default_value(1, Quaternion());
+ } break;
+ default:
+ break;
+ }
}
////////////// Color Op
@@ -1826,7 +2030,7 @@ int VisualShaderNodeColorOp::get_input_port_count() const {
}
VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorOp::get_input_port_name(int p_port) const {
@@ -1838,7 +2042,7 @@ int VisualShaderNodeColorOp::get_output_port_count() const {
}
VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorOp::get_output_port_name(int p_port) const {
@@ -2099,7 +2303,7 @@ int VisualShaderNodeTransformVecMult::get_input_port_count() const {
}
VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_input_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR;
+ return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformVecMult::get_input_port_name(int p_port) const {
@@ -2111,7 +2315,7 @@ int VisualShaderNodeTransformVecMult::get_output_port_count() const {
}
VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const {
@@ -2333,7 +2537,8 @@ String VisualShaderNodeIntFunc::generate_code(Shader::Mode p_mode, VisualShader:
static const char *functions[FUNC_MAX] = {
"abs($)",
"-($)",
- "sign($)"
+ "sign($)",
+ "~($)"
};
return " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
@@ -2362,11 +2567,12 @@ void VisualShaderNodeIntFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeIntFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeIntFunc::get_function);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Abs,Negate,Sign"), "set_function", "get_function");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Abs,Negate,Sign,Bitwise NOT"), "set_function", "get_function");
BIND_ENUM_CONSTANT(FUNC_ABS);
BIND_ENUM_CONSTANT(FUNC_NEGATE);
BIND_ENUM_CONSTANT(FUNC_SIGN);
+ BIND_ENUM_CONSTANT(FUNC_BITWISE_NOT);
BIND_ENUM_CONSTANT(FUNC_MAX);
}
@@ -2384,10 +2590,6 @@ int VisualShaderNodeVectorFunc::get_input_port_count() const {
return 1;
}
-VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorFunc::get_input_port_name(int p_port) const {
return "";
}
@@ -2396,22 +2598,16 @@ int VisualShaderNodeVectorFunc::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const {
- return ""; //no output port means the editor will be used as port
+ return "result";
}
String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *vec_func_id[FUNC_MAX] = {
+ static const char *funcs[FUNC_MAX] = {
"normalize($)",
- "max(min($, vec3(1.0)), vec3(0.0))",
+ "", // FUNC_SATURATE
"-($)",
"1.0 / ($)",
- "",
- "",
"abs($)",
"acos($)",
"acosh($)",
@@ -2440,34 +2636,58 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
"tan($)",
"tanh($)",
"trunc($)",
- "vec3(1.0, 1.0, 1.0) - $"
+ "" // FUNC_ONEMINUS
};
- String code;
+ if (func == FUNC_SATURATE) {
+ String code;
- if (func == FUNC_RGB2HSV) {
- code += " {\n";
- code += " vec3 c = " + p_input_vars[0] + ";\n";
- code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
- code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n";
- code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n";
- code += " float d = q.x - min(q.w, q.y);\n";
- code += " float e = 1.0e-10;\n";
- code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
- code += " }\n";
- } else if (func == FUNC_HSV2RGB) {
- code += " {\n";
- code += " vec3 c = " + p_input_vars[0] + ";\n";
- code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
- code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n";
- code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n";
- code += " }\n";
+ if (op_type == OP_TYPE_VECTOR_2D) {
+ code = "max(min($, vec2(1.0)), vec2(0.0))";
+ } else if (op_type == OP_TYPE_VECTOR_3D) {
+ code = "max(min($, vec3(1.0)), vec3(0.0))";
+ } else {
+ code = "max(min($, vec4(1.0)), vec4(0.0))";
+ }
+ return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n";
+ }
- } else {
- code += " " + p_output_vars[0] + " = " + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ if (func == FUNC_ONEMINUS) {
+ String code;
+
+ if (op_type == OP_TYPE_VECTOR_2D) {
+ code = "vec2(1.0) - $";
+ } else if (op_type == OP_TYPE_VECTOR_3D) {
+ code = "vec3(1.0) - $";
+ } else {
+ code = "vec4(1.0) - $";
+ }
+ return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n";
}
- return code;
+ return " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+}
+
+void VisualShaderNodeVectorFunc::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
}
void VisualShaderNodeVectorFunc::set_function(Function p_func) {
@@ -2475,13 +2695,6 @@ void VisualShaderNodeVectorFunc::set_function(Function p_func) {
if (func == p_func) {
return;
}
- if (p_func == FUNC_RGB2HSV) {
- simple_decl = false;
- } else if (p_func == FUNC_HSV2RGB) {
- simple_decl = false;
- } else {
- simple_decl = true;
- }
func = p_func;
emit_changed();
}
@@ -2491,7 +2704,7 @@ VisualShaderNodeVectorFunc::Function VisualShaderNodeVectorFunc::get_function()
}
Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const {
- Vector<StringName> props;
+ Vector<StringName> props = VisualShaderNodeVectorBase::get_editable_properties();
props.push_back("function");
return props;
}
@@ -2500,14 +2713,12 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function");
BIND_ENUM_CONSTANT(FUNC_NORMALIZE);
BIND_ENUM_CONSTANT(FUNC_SATURATE);
BIND_ENUM_CONSTANT(FUNC_NEGATE);
BIND_ENUM_CONSTANT(FUNC_RECIPROCAL);
- BIND_ENUM_CONSTANT(FUNC_RGB2HSV);
- BIND_ENUM_CONSTANT(FUNC_HSV2RGB);
BIND_ENUM_CONSTANT(FUNC_ABS);
BIND_ENUM_CONSTANT(FUNC_ACOS);
BIND_ENUM_CONSTANT(FUNC_ACOSH);
@@ -2541,7 +2752,19 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
}
VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
- set_input_port_default_value(0, Vector3());
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2());
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3());
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion());
+ } break;
+ default:
+ break;
+ }
}
////////////// ColorFunc
@@ -2555,7 +2778,7 @@ int VisualShaderNodeColorFunc::get_input_port_count() const {
}
VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorFunc::get_input_port_name(int p_port) const {
@@ -2567,7 +2790,7 @@ int VisualShaderNodeColorFunc::get_output_port_count() const {
}
VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorFunc::get_output_port_name(int p_port) const {
@@ -2583,8 +2806,26 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade
code += " vec3 c = " + p_input_vars[0] + ";\n";
code += " float max1 = max(c.r, c.g);\n";
code += " float max2 = max(max1, c.b);\n";
- code += " float max3 = max(max1, max2);\n";
- code += " " + p_output_vars[0] + " = vec3(max3, max3, max3);\n";
+ code += " " + p_output_vars[0] + " = vec3(max2, max2, max2);\n";
+ code += " }\n";
+ break;
+ case FUNC_HSV2RGB:
+ code += " {\n";
+ code += " vec3 c = " + p_input_vars[0] + ";\n";
+ code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
+ code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n";
+ code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n";
+ code += " }\n";
+ break;
+ case FUNC_RGB2HSV:
+ code += " {\n";
+ code += " vec3 c = " + p_input_vars[0] + ";\n";
+ code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
+ code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n";
+ code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n";
+ code += " float d = q.x - min(q.w, q.y);\n";
+ code += " float e = 1.0e-10;\n";
+ code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
code += " }\n";
break;
case FUNC_SEPIA:
@@ -2626,9 +2867,11 @@ void VisualShaderNodeColorFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,Sepia"), "set_function", "get_function");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia"), "set_function", "get_function");
BIND_ENUM_CONSTANT(FUNC_GRAYSCALE);
+ BIND_ENUM_CONSTANT(FUNC_HSV2RGB);
+ BIND_ENUM_CONSTANT(FUNC_RGB2HSV);
BIND_ENUM_CONSTANT(FUNC_SEPIA);
BIND_ENUM_CONSTANT(FUNC_MAX);
}
@@ -2726,11 +2969,11 @@ int VisualShaderNodeUVFunc::get_input_port_count() const {
VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- [[fallthrough]]; // uv
+ return PORT_TYPE_VECTOR_2D; // uv
case 1:
- return PORT_TYPE_VECTOR; // scale
+ return PORT_TYPE_VECTOR_2D; // scale
case 2:
- return PORT_TYPE_VECTOR; // offset & pivot
+ return PORT_TYPE_VECTOR_2D; // offset & pivot
default:
break;
}
@@ -2759,11 +3002,13 @@ String VisualShaderNodeUVFunc::get_input_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeUVFunc::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "UV";
+bool VisualShaderNodeUVFunc::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
int VisualShaderNodeUVFunc::get_output_port_count() const {
@@ -2771,7 +3016,7 @@ int VisualShaderNodeUVFunc::get_output_port_count() const {
}
VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeUVFunc::get_output_port_name(int p_port) const {
@@ -2787,7 +3032,11 @@ String VisualShaderNodeUVFunc::generate_code(Shader::Mode p_mode, VisualShader::
String uv;
if (p_input_vars[0].is_empty()) {
- uv = "vec3(UV.xy, 0.0)";
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ uv = "UV";
+ } else {
+ uv = "vec2(0.0)";
+ }
} else {
uv = vformat("%s", p_input_vars[0]);
}
@@ -2813,9 +3062,9 @@ void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_fun
return;
}
if (p_func == FUNC_PANNING) {
- set_input_port_default_value(2, Vector3()); // offset
+ set_input_port_default_value(2, Vector2()); // offset
} else { // FUNC_SCALING
- set_input_port_default_value(2, Vector3(0.5, 0.5, 0.0)); // pivot
+ set_input_port_default_value(2, Vector2(0.5, 0.5)); // pivot
}
func = p_func;
emit_changed();
@@ -2843,8 +3092,8 @@ void VisualShaderNodeUVFunc::_bind_methods() {
}
VisualShaderNodeUVFunc::VisualShaderNodeUVFunc() {
- set_input_port_default_value(1, Vector3(1.0, 1.0, 0.0)); // scale
- set_input_port_default_value(2, Vector3()); // offset
+ set_input_port_default_value(1, Vector2(1.0, 1.0)); // scale
+ set_input_port_default_value(2, Vector2()); // offset
}
////////////// Dot Product
@@ -2858,7 +3107,7 @@ int VisualShaderNodeDotProduct::get_input_port_count() const {
}
VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeDotProduct::get_input_port_name(int p_port) const {
@@ -2896,10 +3145,6 @@ int VisualShaderNodeVectorLen::get_input_port_count() const {
return 1;
}
-VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorLen::get_input_port_name(int p_port) const {
return "";
}
@@ -2916,12 +3161,34 @@ String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const {
return "length";
}
+void VisualShaderNodeVectorLen::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
String VisualShaderNodeVectorLen::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
return " " + p_output_vars[0] + " = length(" + p_input_vars[0] + ");\n";
}
VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() {
- set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
}
////////////// Determinant
@@ -2962,37 +3229,57 @@ VisualShaderNodeDeterminant::VisualShaderNodeDeterminant() {
set_input_port_default_value(0, Transform3D());
}
-////////////// Scalar Derivative Function
+////////////// Derivative Function
-String VisualShaderNodeScalarDerivativeFunc::get_caption() const {
- return "ScalarDerivativeFunc";
+String VisualShaderNodeDerivativeFunc::get_caption() const {
+ return "DerivativeFunc";
}
-int VisualShaderNodeScalarDerivativeFunc::get_input_port_count() const {
+int VisualShaderNodeDerivativeFunc::get_input_port_count() const {
return 1;
}
-VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_input_port_type(int p_port) const {
+VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_input_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ break;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarDerivativeFunc::get_input_port_name(int p_port) const {
- return "";
+String VisualShaderNodeDerivativeFunc::get_input_port_name(int p_port) const {
+ return "p";
}
-int VisualShaderNodeScalarDerivativeFunc::get_output_port_count() const {
+int VisualShaderNodeDerivativeFunc::get_output_port_count() const {
return 1;
}
-VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_output_port_type(int p_port) const {
+VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_output_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ break;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarDerivativeFunc::get_output_port_name(int p_port) const {
- return "";
+String VisualShaderNodeDerivativeFunc::get_output_port_name(int p_port) const {
+ return "result";
}
-String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+String VisualShaderNodeDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
@@ -3004,84 +3291,36 @@ String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode,
return code;
}
-void VisualShaderNodeScalarDerivativeFunc::set_function(Function p_func) {
- ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
- if (func == p_func) {
+void VisualShaderNodeDerivativeFunc::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
return;
}
- func = p_func;
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
emit_changed();
}
-VisualShaderNodeScalarDerivativeFunc::Function VisualShaderNodeScalarDerivativeFunc::get_function() const {
- return func;
-}
-
-Vector<StringName> VisualShaderNodeScalarDerivativeFunc::get_editable_properties() const {
- Vector<StringName> props;
- props.push_back("function");
- return props;
-}
-
-void VisualShaderNodeScalarDerivativeFunc::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarDerivativeFunc::set_function);
- ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarDerivativeFunc::get_function);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function");
-
- BIND_ENUM_CONSTANT(FUNC_SUM);
- BIND_ENUM_CONSTANT(FUNC_X);
- BIND_ENUM_CONSTANT(FUNC_Y);
- BIND_ENUM_CONSTANT(FUNC_MAX);
-}
-
-VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() {
- set_input_port_default_value(0, 0.0);
-}
-
-////////////// Vector Derivative Function
-
-String VisualShaderNodeVectorDerivativeFunc::get_caption() const {
- return "VectorDerivativeFunc";
-}
-
-int VisualShaderNodeVectorDerivativeFunc::get_input_port_count() const {
- return 1;
-}
-
-VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorDerivativeFunc::get_input_port_name(int p_port) const {
- return "";
-}
-
-int VisualShaderNodeVectorDerivativeFunc::get_output_port_count() const {
- return 1;
-}
-
-VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorDerivativeFunc::get_output_port_name(int p_port) const {
- return "";
-}
-
-String VisualShaderNodeVectorDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *functions[FUNC_MAX] = {
- "fwidth($)",
- "dFdx($)",
- "dFdy($)"
- };
-
- String code;
- code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
- return code;
+VisualShaderNodeDerivativeFunc::OpType VisualShaderNodeDerivativeFunc::get_op_type() const {
+ return op_type;
}
-void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
+void VisualShaderNodeDerivativeFunc::set_function(Function p_func) {
ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
if (func == p_func) {
return;
@@ -3090,30 +3329,41 @@ void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
emit_changed();
}
-VisualShaderNodeVectorDerivativeFunc::Function VisualShaderNodeVectorDerivativeFunc::get_function() const {
+VisualShaderNodeDerivativeFunc::Function VisualShaderNodeDerivativeFunc::get_function() const {
return func;
}
-Vector<StringName> VisualShaderNodeVectorDerivativeFunc::get_editable_properties() const {
+Vector<StringName> VisualShaderNodeDerivativeFunc::get_editable_properties() const {
Vector<StringName> props;
+ props.push_back("op_type");
props.push_back("function");
return props;
}
-void VisualShaderNodeVectorDerivativeFunc::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorDerivativeFunc::set_function);
- ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorDerivativeFunc::get_function);
+void VisualShaderNodeDerivativeFunc::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeDerivativeFunc::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeDerivativeFunc::get_op_type);
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeDerivativeFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeDerivativeFunc::get_function);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function");
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
+
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
BIND_ENUM_CONSTANT(FUNC_MAX);
}
-VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() {
- set_input_port_default_value(0, Vector3());
+VisualShaderNodeDerivativeFunc::VisualShaderNodeDerivativeFunc() {
+ set_input_port_default_value(0, 0.0);
}
////////////// Clamp
@@ -3130,8 +3380,12 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_input_port_type(int p
switch (op_type) {
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
default:
break;
}
@@ -3157,8 +3411,12 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_output_port_type(int
switch (op_type) {
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
default:
break;
}
@@ -3180,19 +3438,29 @@ void VisualShaderNodeClamp::set_op_type(OpType p_op_type) {
}
switch (p_op_type) {
case OP_TYPE_FLOAT:
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, 0.0);
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2));
break;
case OP_TYPE_INT:
- set_input_port_default_value(0, 0);
- set_input_port_default_value(1, 0);
- set_input_port_default_value(2, 0);
+ set_input_port_default_value(0, 0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0, get_input_port_default_value(2));
+ break;
+ case OP_TYPE_VECTOR_2D:
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2));
break;
- case OP_TYPE_VECTOR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+ case OP_TYPE_VECTOR_3D:
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2));
+ break;
+ case OP_TYPE_VECTOR_4D:
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2));
break;
default:
break;
@@ -3215,11 +3483,13 @@ void VisualShaderNodeClamp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeClamp::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeClamp::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_FLOAT);
BIND_ENUM_CONSTANT(OP_TYPE_INT);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -3239,10 +3509,6 @@ int VisualShaderNodeFaceForward::get_input_port_count() const {
return 3;
}
-VisualShaderNodeFaceForward::PortType VisualShaderNodeFaceForward::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeFaceForward::get_input_port_name(int p_port) const {
switch (p_port) {
case 0:
@@ -3260,14 +3526,38 @@ int VisualShaderNodeFaceForward::get_output_port_count() const {
return 1;
}
-VisualShaderNodeFaceForward::PortType VisualShaderNodeFaceForward::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeFaceForward::get_output_port_name(int p_port) const {
return "";
}
+void VisualShaderNodeFaceForward::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
String VisualShaderNodeFaceForward::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
return " " + p_output_vars[0] + " = faceforward(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
@@ -3289,7 +3579,7 @@ int VisualShaderNodeOuterProduct::get_input_port_count() const {
}
VisualShaderNodeOuterProduct::PortType VisualShaderNodeOuterProduct::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeOuterProduct::get_input_port_name(int p_port) const {
@@ -3336,11 +3626,25 @@ int VisualShaderNodeStep::get_input_port_count() const {
VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ if (p_port == 1) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ break;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_3D_SCALAR:
if (p_port == 1) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
+ }
+ break;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ if (p_port == 1) {
+ return PORT_TYPE_VECTOR_4D;
}
break;
default:
@@ -3350,12 +3654,13 @@ VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_p
}
String VisualShaderNodeStep::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "edge";
- } else if (p_port == 1) {
- return "x";
+ switch (p_port) {
+ case 0:
+ return "edge";
+ case 1:
+ return "x";
}
- return "";
+ return String();
}
int VisualShaderNodeStep::get_output_port_count() const {
@@ -3364,10 +3669,18 @@ int VisualShaderNodeStep::get_output_port_count() const {
VisualShaderNodeStep::PortType VisualShaderNodeStep::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
- return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ return PORT_TYPE_VECTOR_4D;
default:
break;
}
@@ -3384,30 +3697,34 @@ void VisualShaderNodeStep::set_op_type(OpType p_op_type) {
return;
}
switch (p_op_type) {
- case OP_TYPE_SCALAR:
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(0, 0.0); // edge
- }
- if (op_type == OP_TYPE_VECTOR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(1, 0.0); // x
- }
- break;
- case OP_TYPE_VECTOR:
- if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // edge
- }
- if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // x
- }
- break;
- case OP_TYPE_VECTOR_SCALAR:
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(0, 0.0); // edge
- }
- if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // x
- }
- break;
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_2D_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_4D_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ } break;
default:
break;
}
@@ -3433,11 +3750,15 @@ void VisualShaderNodeStep::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeStep::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -3458,11 +3779,25 @@ int VisualShaderNodeSmoothStep::get_input_port_count() const {
VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
if (p_port == 2) {
- return PORT_TYPE_VECTOR; // x
+ return PORT_TYPE_VECTOR_2D; // x
+ }
+ break;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ if (p_port == 2) {
+ return PORT_TYPE_VECTOR_3D; // x
+ }
+ break;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ if (p_port == 2) {
+ return PORT_TYPE_VECTOR_4D; // x
}
break;
default:
@@ -3472,14 +3807,15 @@ VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_
}
String VisualShaderNodeSmoothStep::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "edge0";
- } else if (p_port == 1) {
- return "edge1";
- } else if (p_port == 2) {
- return "x";
+ switch (p_port) {
+ case 0:
+ return "edge0";
+ case 1:
+ return "edge1";
+ case 2:
+ return "x";
}
- return "";
+ return String();
}
int VisualShaderNodeSmoothStep::get_output_port_count() const {
@@ -3488,10 +3824,18 @@ int VisualShaderNodeSmoothStep::get_output_port_count() const {
VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
- return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ return PORT_TYPE_VECTOR_4D;
default:
break;
}
@@ -3509,31 +3853,39 @@ void VisualShaderNodeSmoothStep::set_op_type(OpType p_op_type) {
}
switch (p_op_type) {
case OP_TYPE_SCALAR:
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(0, 0.0); // edge0
- set_input_port_default_value(1, 0.0); // edge1
- }
- if (op_type == OP_TYPE_VECTOR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(2, 0.0); // x
- }
- break;
- case OP_TYPE_VECTOR:
- if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // edge0
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // edge1
- }
- if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); // x
- }
- break;
- case OP_TYPE_VECTOR_SCALAR:
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(0, 0.0); // edge0
- set_input_port_default_value(1, 0.0); // edge1
- }
- if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); // x
- }
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_2D:
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_3D:
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_4D:
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // x
break;
default:
break;
@@ -3560,18 +3912,22 @@ void VisualShaderNodeSmoothStep::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeSmoothStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSmoothStep::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
VisualShaderNodeSmoothStep::VisualShaderNodeSmoothStep() {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, 0.0);
+ set_input_port_default_value(0, 0.0); // edge0
+ set_input_port_default_value(1, 1.0); // edge1
+ set_input_port_default_value(2, 0.5); // x
}
////////////// Distance
@@ -3584,17 +3940,14 @@ int VisualShaderNodeVectorDistance::get_input_port_count() const {
return 2;
}
-VisualShaderNodeVectorDistance::PortType VisualShaderNodeVectorDistance::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorDistance::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "p0";
- } else if (p_port == 1) {
- return "p1";
+ switch (p_port) {
+ case 0:
+ return "a";
+ case 1:
+ return "b";
}
- return "";
+ return String();
}
int VisualShaderNodeVectorDistance::get_output_port_count() const {
@@ -3609,13 +3962,38 @@ String VisualShaderNodeVectorDistance::get_output_port_name(int p_port) const {
return "";
}
+void VisualShaderNodeVectorDistance::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); // b
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
String VisualShaderNodeVectorDistance::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
return " " + p_output_vars[0] + " = distance(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
VisualShaderNodeVectorDistance::VisualShaderNodeVectorDistance() {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // a
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // b
}
////////////// Refract Vector
@@ -3633,18 +4011,19 @@ VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_input
return PORT_TYPE_SCALAR;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVectorRefract::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "I";
- } else if (p_port == 1) {
- return "N";
- } else if (p_port == 2) {
- return "eta";
+ switch (p_port) {
+ case 0:
+ return "I";
+ case 1:
+ return "N";
+ case 2:
+ return "eta";
}
- return "";
+ return String();
}
int VisualShaderNodeVectorRefract::get_output_port_count() const {
@@ -3652,7 +4031,7 @@ int VisualShaderNodeVectorRefract::get_output_port_count() const {
}
VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVectorRefract::get_output_port_name(int p_port) const {
@@ -3681,13 +4060,27 @@ int VisualShaderNodeMix::get_input_port_count() const {
VisualShaderNodeMix::PortType VisualShaderNodeMix::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
if (p_port == 2) {
break;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ if (p_port == 2) {
+ break;
+ }
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ if (p_port == 2) {
+ break;
+ }
+ return PORT_TYPE_VECTOR_4D;
default:
break;
}
@@ -3710,10 +4103,18 @@ int VisualShaderNodeMix::get_output_port_count() const {
VisualShaderNodeMix::PortType VisualShaderNodeMix::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
- return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ return PORT_TYPE_VECTOR_4D;
default:
break;
}
@@ -3730,27 +4131,41 @@ void VisualShaderNodeMix::set_op_type(OpType p_op_type) {
return;
}
switch (p_op_type) {
- case OP_TYPE_SCALAR:
- set_input_port_default_value(0, 0.0); // a
- set_input_port_default_value(1, 1.0); // b
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(2, 0.5); // weight
- }
- break;
- case OP_TYPE_VECTOR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // a
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0)); // b
- if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(2, Vector3(0.5, 0.5, 0.5)); // weight
- }
- break;
- case OP_TYPE_VECTOR_SCALAR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // a
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0)); // b
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(2, 0.5); // weight
- }
- break;
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_2D_SCALAR: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_3D_SCALAR: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_4D_SCALAR: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight
+ } break;
default:
break;
}
@@ -3776,11 +4191,15 @@ void VisualShaderNodeMix::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeMix::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMix::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -3797,7 +4216,17 @@ String VisualShaderNodeVectorCompose::get_caption() const {
}
int VisualShaderNodeVectorCompose::get_input_port_count() const {
- return 3;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return 2;
+ case OP_TYPE_VECTOR_3D:
+ return 3;
+ case OP_TYPE_VECTOR_4D:
+ return 4;
+ default:
+ break;
+ }
+ return 0;
}
VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_input_port_type(int p_port) const {
@@ -3805,29 +4234,104 @@ VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_input
}
String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "x";
- } else if (p_port == 1) {
- return "y";
- } else {
- return "z";
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ }
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ case 2:
+ return "z";
+ }
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ case 2:
+ return "z";
+ case 3:
+ return "w";
+ }
+ } break;
+ default:
+ break;
}
+ return String();
}
int VisualShaderNodeVectorCompose::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorCompose::get_output_port_name(int p_port) const {
return "vec";
}
+void VisualShaderNodeVectorCompose::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ float p1 = get_input_port_default_value(0);
+ float p2 = get_input_port_default_value(1);
+
+ set_input_port_default_value(0, p1);
+ set_input_port_default_value(1, p2);
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ float p1 = get_input_port_default_value(0);
+ float p2 = get_input_port_default_value(1);
+
+ set_input_port_default_value(0, p1);
+ set_input_port_default_value(1, p2);
+ set_input_port_default_value(2, 0.0);
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ float p1 = get_input_port_default_value(0);
+ float p2 = get_input_port_default_value(1);
+
+ set_input_port_default_value(0, p1);
+ set_input_port_default_value(1, p2);
+ set_input_port_default_value(2, 0.0);
+ set_input_port_default_value(3, 0.0);
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
String VisualShaderNodeVectorCompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ String code;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ code += " " + p_output_vars[0] + " = vec2(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ code += " " + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ code += " " + p_output_vars[0] + " = vec4(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ", " + p_input_vars[3] + ");\n";
+ } break;
+ default:
+ break;
+ }
+ return code;
}
VisualShaderNodeVectorCompose::VisualShaderNodeVectorCompose() {
@@ -3847,7 +4351,7 @@ int VisualShaderNodeTransformCompose::get_input_port_count() const {
}
VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformCompose::get_input_port_name(int p_port) const {
@@ -3894,16 +4398,22 @@ int VisualShaderNodeVectorDecompose::get_input_port_count() const {
return 1;
}
-VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorDecompose::get_input_port_name(int p_port) const {
return "vec";
}
int VisualShaderNodeVectorDecompose::get_output_port_count() const {
- return 3;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return 2;
+ case OP_TYPE_VECTOR_3D:
+ return 3;
+ case OP_TYPE_VECTOR_4D:
+ return 4;
+ default:
+ break;
+ }
+ return 0;
}
VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_output_port_type(int p_port) const {
@@ -3911,25 +4421,91 @@ VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_o
}
String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const {
- if (p_port == 0) {
- return "x";
- } else if (p_port == 1) {
- return "y";
- } else {
- return "z";
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ }
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ case 2:
+ return "z";
+ }
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ case 2:
+ return "z";
+ case 3:
+ return "w";
+ }
+ } break;
+ default:
+ break;
}
+ return String();
+}
+
+void VisualShaderNodeVectorDecompose::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
}
String VisualShaderNodeVectorDecompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
- code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
- code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
- code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
+ code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
+ code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
+ code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
+ code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
+ code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
+ code += " " + p_output_vars[3] + " = " + p_input_vars[0] + ".w;\n";
+ } break;
+ default:
+ break;
+ }
return code;
}
VisualShaderNodeVectorDecompose::VisualShaderNodeVectorDecompose() {
- set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
}
////////////// Transform Decompose
@@ -3955,7 +4531,7 @@ int VisualShaderNodeTransformDecompose::get_output_port_count() const {
}
VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformDecompose::get_output_port_name(int p_port) const {
@@ -4486,7 +5062,7 @@ int VisualShaderNodeColorUniform::get_input_port_count() const {
}
VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const {
@@ -4494,15 +5070,22 @@ String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const {
}
int VisualShaderNodeColorUniform::get_output_port_count() const {
- return 2;
+ return 1;
}
VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const {
- return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port
+ return "color";
+}
+
+bool VisualShaderNodeColorUniform::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
}
void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) {
@@ -4539,9 +5122,7 @@ String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, Visual
}
String VisualShaderNodeColorUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
- code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
- return code;
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
bool VisualShaderNodeColorUniform::is_show_prop_names() const {
@@ -4579,10 +5160,110 @@ Vector<StringName> VisualShaderNodeColorUniform::get_editable_properties() const
VisualShaderNodeColorUniform::VisualShaderNodeColorUniform() {
}
-////////////// Vector Uniform
+////////////// Vector2 Uniform
+
+String VisualShaderNodeVec2Uniform::get_caption() const {
+ return "Vector2Uniform";
+}
+
+int VisualShaderNodeVec2Uniform::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeVec2Uniform::PortType VisualShaderNodeVec2Uniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeVec2Uniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec2Uniform::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVec2Uniform::PortType VisualShaderNodeVec2Uniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeVec2Uniform::get_output_port_name(int p_port) const {
+ return String();
+}
+
+void VisualShaderNodeVec2Uniform::set_default_value_enabled(bool p_enabled) {
+ default_value_enabled = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeVec2Uniform::is_default_value_enabled() const {
+ return default_value_enabled;
+}
+
+void VisualShaderNodeVec2Uniform::set_default_value(const Vector2 &p_value) {
+ default_value = p_value;
+ emit_changed();
+}
+
+Vector2 VisualShaderNodeVec2Uniform::get_default_value() const {
+ return default_value;
+}
+
+String VisualShaderNodeVec2Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code = _get_qual_str() + "uniform vec2 " + get_uniform_name();
+ if (default_value_enabled) {
+ code += vformat(" = vec2(%.6f, %.6f)", default_value.x, default_value.y);
+ }
+ code += ";\n";
+ return code;
+}
+
+String VisualShaderNodeVec2Uniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+void VisualShaderNodeVec2Uniform::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec2Uniform::set_default_value_enabled);
+ ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec2Uniform::is_default_value_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec2Uniform::set_default_value);
+ ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec2Uniform::get_default_value);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "default_value"), "set_default_value", "get_default_value");
+}
+
+bool VisualShaderNodeVec2Uniform::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeVec2Uniform::is_use_prop_slots() const {
+ return true;
+}
+
+bool VisualShaderNodeVec2Uniform::is_qualifier_supported(Qualifier p_qual) const {
+ return true; // all qualifiers are supported
+}
+
+bool VisualShaderNodeVec2Uniform::is_convertible_to_constant() const {
+ return true; // conversion is allowed
+}
+
+Vector<StringName> VisualShaderNodeVec2Uniform::get_editable_properties() const {
+ Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
+ props.push_back("default_value_enabled");
+ if (default_value_enabled) {
+ props.push_back("default_value");
+ }
+ return props;
+}
+
+VisualShaderNodeVec2Uniform::VisualShaderNodeVec2Uniform() {
+}
+
+////////////// Vector3 Uniform
String VisualShaderNodeVec3Uniform::get_caption() const {
- return "VectorUniform";
+ return "Vector3Uniform";
}
int VisualShaderNodeVec3Uniform::get_input_port_count() const {
@@ -4590,7 +5271,7 @@ int VisualShaderNodeVec3Uniform::get_input_port_count() const {
}
VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const {
@@ -4602,7 +5283,7 @@ int VisualShaderNodeVec3Uniform::get_output_port_count() const {
}
VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVec3Uniform::get_output_port_name(int p_port) const {
@@ -4679,6 +5360,106 @@ Vector<StringName> VisualShaderNodeVec3Uniform::get_editable_properties() const
VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() {
}
+////////////// Vector4 Uniform
+
+String VisualShaderNodeVec4Uniform::get_caption() const {
+ return "Vector4Uniform";
+}
+
+int VisualShaderNodeVec4Uniform::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeVec4Uniform::PortType VisualShaderNodeVec4Uniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_4D;
+}
+
+String VisualShaderNodeVec4Uniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec4Uniform::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVec4Uniform::PortType VisualShaderNodeVec4Uniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_4D;
+}
+
+String VisualShaderNodeVec4Uniform::get_output_port_name(int p_port) const {
+ return ""; // No output port means the editor will be used as port.
+}
+
+void VisualShaderNodeVec4Uniform::set_default_value_enabled(bool p_enabled) {
+ default_value_enabled = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeVec4Uniform::is_default_value_enabled() const {
+ return default_value_enabled;
+}
+
+void VisualShaderNodeVec4Uniform::set_default_value(const Quaternion &p_value) {
+ default_value = p_value;
+ emit_changed();
+}
+
+Quaternion VisualShaderNodeVec4Uniform::get_default_value() const {
+ return default_value;
+}
+
+String VisualShaderNodeVec4Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code = _get_qual_str() + "uniform vec4 " + get_uniform_name();
+ if (default_value_enabled) {
+ code += vformat(" = vec4(%.6f, %.6f, %.6f, %.6f)", default_value.x, default_value.y, default_value.z, default_value.w);
+ }
+ code += ";\n";
+ return code;
+}
+
+String VisualShaderNodeVec4Uniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+void VisualShaderNodeVec4Uniform::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec4Uniform::set_default_value_enabled);
+ ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec4Uniform::is_default_value_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec4Uniform::set_default_value);
+ ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec4Uniform::get_default_value);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "default_value"), "set_default_value", "get_default_value");
+}
+
+bool VisualShaderNodeVec4Uniform::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeVec4Uniform::is_use_prop_slots() const {
+ return true;
+}
+
+bool VisualShaderNodeVec4Uniform::is_qualifier_supported(Qualifier p_qual) const {
+ return true; // All qualifiers are supported.
+}
+
+bool VisualShaderNodeVec4Uniform::is_convertible_to_constant() const {
+ return true; // Conversion is allowed.
+}
+
+Vector<StringName> VisualShaderNodeVec4Uniform::get_editable_properties() const {
+ Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
+ props.push_back("default_value_enabled");
+ if (default_value_enabled) {
+ props.push_back("default_value");
+ }
+ return props;
+}
+
+VisualShaderNodeVec4Uniform::VisualShaderNodeVec4Uniform() {
+}
+
////////////// Transform Uniform
String VisualShaderNodeTransformUniform::get_caption() const {
@@ -4690,7 +5471,7 @@ int VisualShaderNodeTransformUniform::get_input_port_count() const {
}
VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const {
@@ -4730,9 +5511,9 @@ Transform3D VisualShaderNodeTransformUniform::get_default_value() const {
String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code = _get_qual_str() + "uniform mat4 " + get_uniform_name();
if (default_value_enabled) {
- Vector3 row0 = default_value.basis.get_row(0);
- Vector3 row1 = default_value.basis.get_row(1);
- Vector3 row2 = default_value.basis.get_row(2);
+ Vector3 row0 = default_value.basis.rows[0];
+ Vector3 row1 = default_value.basis.rows[1];
+ Vector3 row2 = default_value.basis.rows[2];
Vector3 origin = default_value.origin;
code += " = mat4(" + vformat("vec4(%.6f, %.6f, %.6f, 0.0)", row0.x, row0.y, row0.z) + vformat(", vec4(%.6f, %.6f, %.6f, 0.0)", row1.x, row1.y, row1.z) + vformat(", vec4(%.6f, %.6f, %.6f, 0.0)", row2.x, row2.y, row2.z) + vformat(", vec4(%.6f, %.6f, %.6f, 1.0)", origin.x, origin.y, origin.z) + ")";
}
@@ -4793,28 +5574,24 @@ String VisualShaderNodeTextureUniform::get_caption() const {
}
int VisualShaderNodeTextureUniform::get_input_port_count() const {
- return 2;
+ return 0;
}
VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const {
- return p_port == 0 ? "uv" : "lod";
+ return "";
}
int VisualShaderNodeTextureUniform::get_output_port_count() const {
- return 3;
+ return 1;
}
VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
- case 1:
- return PORT_TYPE_SCALAR;
- case 2:
return PORT_TYPE_SAMPLER;
default:
return PORT_TYPE_SCALAR;
@@ -4824,10 +5601,6 @@ VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_out
String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const {
switch (p_port) {
case 0:
- return "rgb";
- case 1:
- return "alpha";
- case 2:
return "sampler2D";
default:
return "";
@@ -4835,70 +5608,113 @@ String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const {
}
String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ bool has_colon = false;
String code = _get_qual_str() + "uniform sampler2D " + get_uniform_name();
- switch (texture_type) {
- case TYPE_DATA:
- if (color_default == COLOR_DEFAULT_BLACK) {
- code += " : hint_black;\n";
- } else {
- code += ";\n";
- }
- break;
- case TYPE_COLOR:
- if (color_default == COLOR_DEFAULT_BLACK) {
- code += " : hint_black_albedo;\n";
- } else {
- code += " : hint_albedo;\n";
- }
- break;
- case TYPE_NORMAL_MAP:
- code += " : hint_normal;\n";
- break;
- case TYPE_ANISO:
- code += " : hint_aniso;\n";
- break;
- default:
- code += ";\n";
- break;
+ // type
+ {
+ String type_code;
+
+ switch (texture_type) {
+ case TYPE_DATA:
+ if (color_default == COLOR_DEFAULT_BLACK) {
+ type_code = "hint_black";
+ }
+ break;
+ case TYPE_COLOR:
+ if (color_default == COLOR_DEFAULT_BLACK) {
+ type_code = "hint_black_albedo";
+ } else {
+ type_code = "hint_albedo";
+ }
+ break;
+ case TYPE_NORMAL_MAP:
+ type_code = "hint_normal";
+ break;
+ case TYPE_ANISOTROPY:
+ type_code = "hint_anisotropy";
+ break;
+ default:
+ break;
+ }
+
+ if (!type_code.is_empty()) {
+ code += " : " + type_code;
+ has_colon = true;
+ }
}
- return code;
-}
+ // filter
+ {
+ String filter_code;
-bool VisualShaderNodeTextureUniform::is_code_generated() const {
- return is_output_port_connected(0) || is_output_port_connected(1); // rgb or alpha
-}
+ switch (texture_filter) {
+ case FILTER_NEAREST:
+ filter_code = "filter_nearest";
+ break;
+ case FILTER_LINEAR:
+ filter_code = "filter_linear";
+ break;
+ case FILTER_NEAREST_MIPMAP:
+ filter_code = "filter_nearest_mipmap";
+ break;
+ case FILTER_LINEAR_MIPMAP:
+ filter_code = "filter_linear_mipmap";
+ break;
+ case FILTER_NEAREST_MIPMAP_ANISOTROPIC:
+ filter_code = "filter_nearest_mipmap_anisotropic";
+ break;
+ case FILTER_LINEAR_MIPMAP_ANISOTROPIC:
+ filter_code = "filter_linear_mipmap_anisotropic";
+ break;
+ default:
+ break;
+ }
-String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- String default_uv;
- if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
- default_uv = "UV.xy";
- } else {
- default_uv = "vec2(0.0)";
+ if (!filter_code.is_empty()) {
+ if (!has_colon) {
+ code += " : ";
+ has_colon = true;
+ } else {
+ code += ", ";
+ }
+ code += filter_code;
+ }
}
- String id = get_uniform_name();
- String code = " {\n";
- if (p_input_vars[0] == String()) { // Use UV by default.
- if (p_input_vars[1] == String()) {
- code += " vec4 n_tex_read = texture(" + id + ", " + default_uv + ");\n";
- } else {
- code += " vec4 n_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
+ // repeat
+ {
+ String repeat_code;
+
+ switch (texture_repeat) {
+ case REPEAT_ENABLED:
+ repeat_code = "repeat_enable";
+ break;
+ case REPEAT_DISABLED:
+ repeat_code = "repeat_disable";
+ break;
+ default:
+ break;
+ }
+
+ if (!repeat_code.is_empty()) {
+ if (!has_colon) {
+ code += " : ";
+ } else {
+ code += ", ";
+ }
+ code += repeat_code;
}
- } else if (p_input_vars[1] == String()) {
- //no lod
- code += " vec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
- } else {
- code += " vec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
}
- code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n";
- code += " " + p_output_vars[1] + " = n_tex_read.a;\n";
- code += " }\n";
+ code += ";\n";
return code;
}
+String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return "";
+}
+
void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_texture_type) {
ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
if (texture_type == p_texture_type) {
@@ -4925,13 +5741,56 @@ VisualShaderNodeTextureUniform::ColorDefault VisualShaderNodeTextureUniform::get
return color_default;
}
+void VisualShaderNodeTextureUniform::set_texture_filter(TextureFilter p_filter) {
+ ERR_FAIL_INDEX(int(p_filter), int(FILTER_MAX));
+ if (texture_filter == p_filter) {
+ return;
+ }
+ texture_filter = p_filter;
+ emit_changed();
+}
+
+VisualShaderNodeTextureUniform::TextureFilter VisualShaderNodeTextureUniform::get_texture_filter() const {
+ return texture_filter;
+}
+
+void VisualShaderNodeTextureUniform::set_texture_repeat(TextureRepeat p_repeat) {
+ ERR_FAIL_INDEX(int(p_repeat), int(REPEAT_MAX));
+ if (texture_repeat == p_repeat) {
+ return;
+ }
+ texture_repeat = p_repeat;
+ emit_changed();
+}
+
+VisualShaderNodeTextureUniform::TextureRepeat VisualShaderNodeTextureUniform::get_texture_repeat() const {
+ return texture_repeat;
+}
+
Vector<StringName> VisualShaderNodeTextureUniform::get_editable_properties() const {
Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
props.push_back("texture_type");
- props.push_back("color_default");
+ if (texture_type == TYPE_DATA || texture_type == TYPE_COLOR) {
+ props.push_back("color_default");
+ }
+ props.push_back("texture_filter");
+ props.push_back("texture_repeat");
return props;
}
+bool VisualShaderNodeTextureUniform::is_show_prop_names() const {
+ return true;
+}
+
+HashMap<StringName, String> VisualShaderNodeTextureUniform::get_editable_properties_names() const {
+ HashMap<StringName, String> names;
+ names.insert("texture_type", RTR("Type"));
+ names.insert("color_default", RTR("Default Color"));
+ names.insert("texture_filter", RTR("Filter"));
+ names.insert("texture_repeat", RTR("Repeat"));
+ return names;
+}
+
void VisualShaderNodeTextureUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture_type", "type"), &VisualShaderNodeTextureUniform::set_texture_type);
ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTextureUniform::get_texture_type);
@@ -4939,25 +5798,40 @@ void VisualShaderNodeTextureUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default);
ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default);
+ ClassDB::bind_method(D_METHOD("set_texture_filter", "filter"), &VisualShaderNodeTextureUniform::set_texture_filter);
+ ClassDB::bind_method(D_METHOD("get_texture_filter"), &VisualShaderNodeTextureUniform::get_texture_filter);
+
+ ClassDB::bind_method(D_METHOD("set_texture_repeat", "type"), &VisualShaderNodeTextureUniform::set_texture_repeat);
+ ClassDB::bind_method(D_METHOD("get_texture_repeat"), &VisualShaderNodeTextureUniform::get_texture_repeat);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map,Anisotropic"), "set_texture_type", "get_texture_type");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White,Black"), "set_color_default", "get_color_default");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Default,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Default,Enabled,Disabled"), "set_texture_repeat", "get_texture_repeat");
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
- BIND_ENUM_CONSTANT(TYPE_ANISO);
+ BIND_ENUM_CONSTANT(TYPE_ANISOTROPY);
BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_MAX);
-}
-String VisualShaderNodeTextureUniform::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "default";
- }
- return "";
+ BIND_ENUM_CONSTANT(FILTER_DEFAULT);
+ BIND_ENUM_CONSTANT(FILTER_NEAREST);
+ BIND_ENUM_CONSTANT(FILTER_LINEAR);
+ BIND_ENUM_CONSTANT(FILTER_NEAREST_MIPMAP);
+ BIND_ENUM_CONSTANT(FILTER_LINEAR_MIPMAP);
+ BIND_ENUM_CONSTANT(FILTER_NEAREST_MIPMAP_ANISOTROPIC);
+ BIND_ENUM_CONSTANT(FILTER_LINEAR_MIPMAP_ANISOTROPIC);
+ BIND_ENUM_CONSTANT(FILTER_MAX);
+
+ BIND_ENUM_CONSTANT(REPEAT_DEFAULT);
+ BIND_ENUM_CONSTANT(REPEAT_ENABLED);
+ BIND_ENUM_CONSTANT(REPEAT_DISABLED);
+ BIND_ENUM_CONSTANT(REPEAT_MAX);
}
bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) const {
@@ -4979,7 +5853,6 @@ bool VisualShaderNodeTextureUniform::is_convertible_to_constant() const {
}
VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() {
- simple_decl = false;
}
////////////// Texture Uniform (Triplanar)
@@ -4992,9 +5865,9 @@ int VisualShaderNodeTextureUniformTriplanar::get_input_port_count() const {
return 2;
}
-VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const {
+VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const {
if (p_port == 0 || p_port == 1) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR;
}
@@ -5008,10 +5881,36 @@ String VisualShaderNodeTextureUniformTriplanar::get_input_port_name(int p_port)
return "";
}
-String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+int VisualShaderNodeTextureUniformTriplanar::get_output_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniformTriplanar::get_output_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_VECTOR_4D;
+ case 1:
+ return PORT_TYPE_SAMPLER;
+ default:
+ return PORT_TYPE_SCALAR;
+ }
+}
+
+String VisualShaderNodeTextureUniformTriplanar::get_output_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "color";
+ case 1:
+ return "sampler2D";
+ default:
+ return "";
+ }
+}
+
+String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
- code += "// TRIPLANAR FUNCTION GLOBAL CODE\n";
+ code += "// " + get_caption() + "\n";
code += " vec4 triplanar_texture(sampler2D p_sampler, vec3 p_weights, vec3 p_triplanar_pos) {\n";
code += " vec4 samp = vec4(0.0);\n";
code += " samp += texture(p_sampler, p_triplanar_pos.xy) * p_weights.z;\n";
@@ -5034,11 +5933,13 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader:
String code;
if (p_type == VisualShader::TYPE_VERTEX) {
- code += " // TRIPLANAR FUNCTION VERTEX CODE\n";
+ code += "// " + get_caption() + "\n";
+ code += " {\n";
code += " triplanar_power_normal = pow(abs(NORMAL), vec3(triplanar_sharpness));\n";
code += " triplanar_power_normal /= dot(triplanar_power_normal, vec3(1.0));\n";
code += " triplanar_pos = VERTEX * triplanar_scale + triplanar_offset;\n";
code += " triplanar_pos *= vec3(1.0, -1.0, 1.0);\n";
+ code += " }\n";
}
return code;
@@ -5046,32 +5947,28 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader:
String VisualShaderNodeTextureUniformTriplanar::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String id = get_uniform_name();
- String code = " {\n";
-
- if (p_input_vars[0] == String() && p_input_vars[1] == String()) {
- code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n";
- } else if (p_input_vars[0] != String() && p_input_vars[1] == String()) {
- code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n";
- } else if (p_input_vars[0] == String() && p_input_vars[1] != String()) {
- code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n";
+
+ String code;
+ if (p_input_vars[0].is_empty() && p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n";
+ } else if (!p_input_vars[0].is_empty() && p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n";
+ } else if (p_input_vars[0].is_empty() && !p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n";
} else {
- code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
- code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n";
- code += " " + p_output_vars[1] + " = n_tex_read.a;\n";
- code += " }\n";
-
return code;
}
-String VisualShaderNodeTextureUniformTriplanar::get_input_port_default_hint(int p_port) const {
+bool VisualShaderNodeTextureUniformTriplanar::is_input_port_default(int p_port, Shader::Mode p_mode) const {
if (p_port == 0) {
- return "default";
+ return true;
} else if (p_port == 1) {
- return "default";
+ return true;
}
- return "";
+ return false;
}
VisualShaderNodeTextureUniformTriplanar::VisualShaderNodeTextureUniformTriplanar() {
@@ -5083,34 +5980,10 @@ String VisualShaderNodeTexture2DArrayUniform::get_caption() const {
return "Texture2DArrayUniform";
}
-int VisualShaderNodeTexture2DArrayUniform::get_output_port_count() const {
- return 1;
-}
-
-VisualShaderNodeTexture2DArrayUniform::PortType VisualShaderNodeTexture2DArrayUniform::get_output_port_type(int p_port) const {
- return PORT_TYPE_SAMPLER;
-}
-
String VisualShaderNodeTexture2DArrayUniform::get_output_port_name(int p_port) const {
return "sampler2DArray";
}
-int VisualShaderNodeTexture2DArrayUniform::get_input_port_count() const {
- return 0;
-}
-
-VisualShaderNodeTexture2DArrayUniform::PortType VisualShaderNodeTexture2DArrayUniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_SCALAR;
-}
-
-String VisualShaderNodeTexture2DArrayUniform::get_input_port_name(int p_port) const {
- return "";
-}
-
-String VisualShaderNodeTexture2DArrayUniform::get_input_port_default_hint(int p_port) const {
- return "";
-}
-
String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code = _get_qual_str() + "uniform sampler2DArray " + get_uniform_name();
@@ -5132,8 +6005,8 @@ String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mod
case TYPE_NORMAL_MAP:
code += " : hint_normal;\n";
break;
- case TYPE_ANISO:
- code += " : hint_aniso;\n";
+ case TYPE_ANISOTROPY:
+ code += " : hint_anisotropy;\n";
break;
default:
code += ";\n";
@@ -5156,34 +6029,10 @@ String VisualShaderNodeTexture3DUniform::get_caption() const {
return "Texture3DUniform";
}
-int VisualShaderNodeTexture3DUniform::get_output_port_count() const {
- return 1;
-}
-
-VisualShaderNodeTexture3DUniform::PortType VisualShaderNodeTexture3DUniform::get_output_port_type(int p_port) const {
- return PORT_TYPE_SAMPLER;
-}
-
String VisualShaderNodeTexture3DUniform::get_output_port_name(int p_port) const {
return "sampler3D";
}
-int VisualShaderNodeTexture3DUniform::get_input_port_count() const {
- return 0;
-}
-
-VisualShaderNodeTexture3DUniform::PortType VisualShaderNodeTexture3DUniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_SCALAR;
-}
-
-String VisualShaderNodeTexture3DUniform::get_input_port_name(int p_port) const {
- return "";
-}
-
-String VisualShaderNodeTexture3DUniform::get_input_port_default_hint(int p_port) const {
- return "";
-}
-
String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code = _get_qual_str() + "uniform sampler3D " + get_uniform_name();
@@ -5205,8 +6054,8 @@ String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, Vi
case TYPE_NORMAL_MAP:
code += " : hint_normal;\n";
break;
- case TYPE_ANISO:
- code += " : hint_aniso;\n";
+ case TYPE_ANISOTROPY:
+ code += " : hint_anisotropy;\n";
break;
default:
code += ";\n";
@@ -5229,34 +6078,10 @@ String VisualShaderNodeCubemapUniform::get_caption() const {
return "CubemapUniform";
}
-int VisualShaderNodeCubemapUniform::get_output_port_count() const {
- return 1;
-}
-
-VisualShaderNodeCubemapUniform::PortType VisualShaderNodeCubemapUniform::get_output_port_type(int p_port) const {
- return PORT_TYPE_SAMPLER;
-}
-
String VisualShaderNodeCubemapUniform::get_output_port_name(int p_port) const {
return "samplerCube";
}
-int VisualShaderNodeCubemapUniform::get_input_port_count() const {
- return 0;
-}
-
-VisualShaderNodeCubemapUniform::PortType VisualShaderNodeCubemapUniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_SCALAR;
-}
-
-String VisualShaderNodeCubemapUniform::get_input_port_name(int p_port) const {
- return "";
-}
-
-String VisualShaderNodeCubemapUniform::get_input_port_default_hint(int p_port) const {
- return "";
-}
-
String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code = _get_qual_str() + "uniform samplerCube " + get_uniform_name();
@@ -5278,8 +6103,8 @@ String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, Visu
case TYPE_NORMAL_MAP:
code += " : hint_normal;\n";
break;
- case TYPE_ANISO:
- code += " : hint_aniso;\n";
+ case TYPE_ANISOTROPY:
+ code += " : hint_anisotropy;\n";
break;
default:
code += ";\n";
@@ -5310,7 +6135,7 @@ VisualShaderNodeIf::PortType VisualShaderNodeIf::get_input_port_type(int p_port)
if (p_port == 0 || p_port == 1 || p_port == 2) {
return PORT_TYPE_SCALAR;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeIf::get_input_port_name(int p_port) const {
@@ -5337,7 +6162,7 @@ int VisualShaderNodeIf::get_output_port_count() const {
}
VisualShaderNodeIf::PortType VisualShaderNodeIf::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeIf::get_output_port_name(int p_port) const {
@@ -5389,8 +6214,12 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int
switch (op_type) {
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
case OP_TYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
case OP_TYPE_TRANSFORM:
@@ -5423,8 +6252,12 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(in
switch (op_type) {
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
- case OP_TYPE_VECTOR:
- return PORT_TYPE_VECTOR;
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
case OP_TYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
case OP_TYPE_TRANSFORM:
@@ -5446,16 +6279,24 @@ void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) {
}
switch (p_op_type) {
case OP_TYPE_FLOAT:
- set_input_port_default_value(1, 1.0);
- set_input_port_default_value(2, 0.0);
+ set_input_port_default_value(1, 1.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2));
break;
case OP_TYPE_INT:
- set_input_port_default_value(1, 1);
- set_input_port_default_value(2, 0);
+ set_input_port_default_value(1, 1, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0, get_input_port_default_value(2));
+ break;
+ case OP_TYPE_VECTOR_2D:
+ set_input_port_default_value(1, Vector2(1.0, 1.0), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(0.0, 0.0), get_input_port_default_value(2));
break;
- case OP_TYPE_VECTOR:
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+ case OP_TYPE_VECTOR_3D:
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0), get_input_port_default_value(2));
+ break;
+ case OP_TYPE_VECTOR_4D:
+ set_input_port_default_value(1, Quaternion(1.0, 1.0, 1.0, 1.0), get_input_port_default_value(1));
+ set_input_port_default_value(2, Quaternion(0.0, 0.0, 0.0, 0.0), get_input_port_default_value(2));
break;
case OP_TYPE_BOOLEAN:
set_input_port_default_value(1, true);
@@ -5486,11 +6327,13 @@ void VisualShaderNodeSwitch::_bind_methods() { // static
ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeSwitch::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSwitch::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector,Boolean,Transform"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4,Boolean,Transform"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_FLOAT);
BIND_ENUM_CONSTANT(OP_TYPE_INT);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
BIND_ENUM_CONSTANT(OP_TYPE_BOOLEAN);
BIND_ENUM_CONSTANT(OP_TYPE_TRANSFORM);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
@@ -5529,15 +6372,15 @@ int VisualShaderNodeFresnel::get_input_port_count() const {
VisualShaderNodeFresnel::PortType VisualShaderNodeFresnel::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 1:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 2:
return PORT_TYPE_BOOLEAN;
case 3:
return PORT_TYPE_SCALAR;
default:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
}
@@ -5578,13 +6421,21 @@ bool VisualShaderNodeFresnel::is_generate_input_var(int p_port) const {
String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String normal;
String view;
- if (p_input_vars[0] == String()) {
- normal = "NORMAL";
+ if (p_input_vars[0].is_empty()) {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ normal = "NORMAL";
+ } else {
+ normal = "vec3(0.0)";
+ }
} else {
normal = p_input_vars[0];
}
- if (p_input_vars[1] == String()) {
- view = "VIEW";
+ if (p_input_vars[1].is_empty()) {
+ if (p_mode == Shader::MODE_SPATIAL) {
+ view = "VIEW";
+ } else {
+ view = "vec3(0.0)";
+ }
} else {
view = p_input_vars[1];
}
@@ -5600,13 +6451,17 @@ String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader:
}
}
-String VisualShaderNodeFresnel::get_input_port_default_hint(int p_port) const {
+bool VisualShaderNodeFresnel::is_input_port_default(int p_port, Shader::Mode p_mode) const {
if (p_port == 0) {
- return "default";
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ return true;
+ }
} else if (p_port == 1) {
- return "default";
+ if (p_mode == Shader::MODE_SPATIAL) {
+ return true;
+ }
}
- return "";
+ return false;
}
VisualShaderNodeFresnel::VisualShaderNodeFresnel() {
@@ -5708,8 +6563,12 @@ VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(i
return PORT_TYPE_SCALAR;
case CTYPE_SCALAR_INT:
return PORT_TYPE_SCALAR_INT;
- case CTYPE_VECTOR:
- return PORT_TYPE_VECTOR;
+ case CTYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case CTYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case CTYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
case CTYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
case CTYPE_TRANSFORM:
@@ -5748,7 +6607,7 @@ String VisualShaderNodeCompare::get_output_port_name(int p_port) const {
String VisualShaderNodeCompare::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
if (comparison_type == CTYPE_BOOLEAN || comparison_type == CTYPE_TRANSFORM) {
if (func > FUNC_NOT_EQUAL) {
- return TTR("Invalid comparison function for that type.");
+ return RTR("Invalid comparison function for that type.");
}
}
return "";
@@ -5780,7 +6639,7 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
String code;
switch (comparison_type) {
- case CTYPE_SCALAR:
+ case CTYPE_SCALAR: {
if (func == FUNC_EQUAL) {
code += " " + p_output_vars[0] + " = (abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else if (func == FUNC_NOT_EQUAL) {
@@ -5788,33 +6647,40 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
} else {
code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
}
- break;
-
- case CTYPE_SCALAR_INT:
+ } break;
+ case CTYPE_SCALAR_INT: {
code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
- break;
-
- case CTYPE_VECTOR:
+ } break;
+ case CTYPE_VECTOR_2D: {
+ code += " {\n";
+ code += " bvec2 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n";
+ code += " }\n";
+ } break;
+ case CTYPE_VECTOR_3D: {
code += " {\n";
code += " bvec3 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n";
code += " }\n";
- break;
-
- case CTYPE_BOOLEAN:
+ } break;
+ case CTYPE_VECTOR_4D: {
+ code += " {\n";
+ code += " bvec4 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n";
+ code += " }\n";
+ } break;
+ case CTYPE_BOOLEAN: {
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
- break;
-
- case CTYPE_TRANSFORM:
+ } break;
+ case CTYPE_TRANSFORM: {
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
- break;
-
+ } break;
default:
break;
}
@@ -5828,18 +6694,28 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_comparison_ty
}
switch (p_comparison_type) {
case CTYPE_SCALAR:
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
simple_decl = true;
break;
case CTYPE_SCALAR_INT:
- set_input_port_default_value(0, 0);
- set_input_port_default_value(1, 0);
+ set_input_port_default_value(0, 0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0, get_input_port_default_value(1));
simple_decl = true;
break;
- case CTYPE_VECTOR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ case CTYPE_VECTOR_2D:
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ simple_decl = false;
+ break;
+ case CTYPE_VECTOR_3D:
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ simple_decl = false;
+ break;
+ case CTYPE_VECTOR_4D:
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
simple_decl = false;
break;
case CTYPE_BOOLEAN:
@@ -5893,7 +6769,7 @@ Vector<StringName> VisualShaderNodeCompare::get_editable_properties() const {
Vector<StringName> props;
props.push_back("type");
props.push_back("function");
- if (comparison_type == CTYPE_VECTOR) {
+ if (comparison_type == CTYPE_VECTOR_2D || comparison_type == CTYPE_VECTOR_3D || comparison_type == CTYPE_VECTOR_4D) {
props.push_back("condition");
}
return props;
@@ -5909,13 +6785,15 @@ void VisualShaderNodeCompare::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_condition", "condition"), &VisualShaderNodeCompare::set_condition);
ClassDB::bind_method(D_METHOD("get_condition"), &VisualShaderNodeCompare::get_condition);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Float,Int,Vector,Boolean,Transform"), "set_comparison_type", "get_comparison_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4,Boolean,Transform"), "set_comparison_type", "get_comparison_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "a == b,a != b,a > b,a >= b,a < b,a <= b"), "set_function", "get_function");
ADD_PROPERTY(PropertyInfo(Variant::INT, "condition", PROPERTY_HINT_ENUM, "All,Any"), "set_condition", "get_condition");
BIND_ENUM_CONSTANT(CTYPE_SCALAR);
BIND_ENUM_CONSTANT(CTYPE_SCALAR_INT);
- BIND_ENUM_CONSTANT(CTYPE_VECTOR);
+ BIND_ENUM_CONSTANT(CTYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(CTYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(CTYPE_VECTOR_4D);
BIND_ENUM_CONSTANT(CTYPE_BOOLEAN);
BIND_ENUM_CONSTANT(CTYPE_TRANSFORM);
BIND_ENUM_CONSTANT(CTYPE_MAX);
@@ -5950,8 +6828,15 @@ int VisualShaderNodeMultiplyAdd::get_input_port_count() const {
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_port_type(int p_port) const {
- if (op_type == OP_TYPE_VECTOR) {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ break;
}
return PORT_TYPE_SCALAR;
}
@@ -5972,11 +6857,17 @@ int VisualShaderNodeMultiplyAdd::get_output_port_count() const {
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_output_port_type(int p_port) const {
- if (op_type == OP_TYPE_SCALAR) {
- return PORT_TYPE_SCALAR;
- } else {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ break;
}
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeMultiplyAdd::get_output_port_name(int p_port) const {
@@ -5993,16 +6884,26 @@ void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) {
return;
}
switch (p_op_type) {
- case OP_TYPE_SCALAR:
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, 0.0);
- break;
- case OP_TYPE_VECTOR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
- break;
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2));
+ } break;
default:
break;
}
@@ -6024,10 +6925,12 @@ void VisualShaderNodeMultiplyAdd::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeMultiplyAdd::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMultiplyAdd::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -6073,29 +6976,29 @@ String VisualShaderNodeBillboard::generate_code(Shader::Mode p_mode, VisualShade
switch (billboard_type) {
case BILLBOARD_TYPE_ENABLED:
code += " {\n";
- code += " mat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], CAMERA_MATRIX[1], CAMERA_MATRIX[2], WORLD_MATRIX[3]);\n";
+ code += " mat4 __mvm = VIEW_MATRIX * mat4(INV_VIEW_MATRIX[0], INV_VIEW_MATRIX[1], INV_VIEW_MATRIX[2], MODEL_MATRIX[3]);\n";
if (keep_scale) {
- code += " __mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " __mvm = __mvm * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
}
code += " " + p_output_vars[0] + " = __mvm;\n";
code += " }\n";
break;
case BILLBOARD_TYPE_FIXED_Y:
code += " {\n";
- code += " mat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], WORLD_MATRIX[1], vec4(normalize(cross(CAMERA_MATRIX[0].xyz, WORLD_MATRIX[1].xyz)), 0.0), WORLD_MATRIX[3]);\n";
+ code += " mat4 __mvm = VIEW_MATRIX * mat4(INV_VIEW_MATRIX[0], MODEL_MATRIX[1], vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, MODEL_MATRIX[1].xyz)), 0.0), MODEL_MATRIX[3]);\n";
if (keep_scale) {
- code += " __mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " __mvm = __mvm * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
} else {
- code += " __mvm = __mvm * mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0 / length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " __mvm = __mvm * mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0 / length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
}
code += " " + p_output_vars[0] + " = __mvm;\n";
code += " }\n";
break;
case BILLBOARD_TYPE_PARTICLES:
code += " {\n";
- code += " mat4 __wm = mat4(normalize(CAMERA_MATRIX[0]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[1]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[2]) * length(WORLD_MATRIX[2]), WORLD_MATRIX[3]);\n";
+ code += " mat4 __wm = mat4(normalize(INV_VIEW_MATRIX[0]) * length(MODEL_MATRIX[0]), normalize(INV_VIEW_MATRIX[1]) * length(MODEL_MATRIX[0]), normalize(INV_VIEW_MATRIX[2]) * length(MODEL_MATRIX[2]), MODEL_MATRIX[3]);\n";
code += " __wm = __wm * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
- code += " " + p_output_vars[0] + " = INV_CAMERA_MATRIX * __wm;\n";
+ code += " " + p_output_vars[0] + " = VIEW_MATRIX * __wm;\n";
code += " }\n";
break;
default:
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 2c952300fe..b159d25eba 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,6 +34,50 @@
#include "scene/resources/visual_shader.h"
///////////////////////////////////////
+/// Vector Base Node
+///////////////////////////////////////
+
+class VisualShaderNodeVectorBase : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorBase, VisualShaderNode);
+
+public:
+ enum OpType {
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_4D,
+ OP_TYPE_MAX,
+ };
+
+protected:
+ OpType op_type = OP_TYPE_VECTOR_3D;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override = 0;
+
+ virtual int get_input_port_count() const override = 0;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override = 0;
+
+ virtual int get_output_port_count() const override = 0;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override = 0;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override = 0;
+
+ virtual void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeVectorBase();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeVectorBase::OpType)
+
+///////////////////////////////////////
/// CONSTANTS
///////////////////////////////////////
@@ -177,6 +221,36 @@ public:
///////////////////////////////////////
+class VisualShaderNodeVec2Constant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeVec2Constant, VisualShaderNodeConstant);
+ Vector2 constant;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ void set_constant(const Vector2 &p_constant);
+ Vector2 get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeVec2Constant();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeVec3Constant : public VisualShaderNodeConstant {
GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNodeConstant);
Vector3 constant;
@@ -207,6 +281,36 @@ public:
///////////////////////////////////////
+class VisualShaderNodeVec4Constant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeVec4Constant, VisualShaderNodeConstant);
+ Quaternion constant;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ void set_constant(const Quaternion &p_constant);
+ Quaternion get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeVec4Constant();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeTransformConstant : public VisualShaderNodeConstant {
GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNodeConstant);
Transform3D constant;
@@ -280,7 +384,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
virtual bool is_output_port_expandable(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
@@ -392,7 +496,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -488,7 +592,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -579,6 +683,11 @@ public:
OP_MOD,
OP_MAX,
OP_MIN,
+ OP_BITWISE_AND,
+ OP_BITWISE_OR,
+ OP_BITWISE_XOR,
+ OP_BITWISE_LEFT_SHIFT,
+ OP_BITWISE_RIGHT_SHIFT,
OP_ENUM_SIZE,
};
@@ -610,8 +719,8 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeIntOp::Operator)
-class VisualShaderNodeVectorOp : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorOp, VisualShaderNode);
+class VisualShaderNodeVectorOp : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorOp, VisualShaderNodeVectorBase);
public:
enum Operator {
@@ -639,19 +748,20 @@ public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+ virtual void set_op_type(OpType p_op_type) override;
+
void set_operator(Operator p_op);
Operator get_operator() const;
virtual Vector<StringName> get_editable_properties() const override;
+ String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override;
VisualShaderNodeVectorOp();
};
@@ -882,6 +992,7 @@ public:
FUNC_ABS,
FUNC_NEGATE,
FUNC_SIGN,
+ FUNC_BITWISE_NOT,
FUNC_MAX,
};
@@ -917,8 +1028,10 @@ VARIANT_ENUM_CAST(VisualShaderNodeIntFunc::Function)
/// VECTOR FUNC
///////////////////////////////////////
-class VisualShaderNodeVectorFunc : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNode);
+class VisualShaderNodeVectorFunc : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNodeVectorBase);
+
+ void _update_default_input_values();
public:
enum Function {
@@ -926,8 +1039,6 @@ public:
FUNC_SATURATE,
FUNC_NEGATE,
FUNC_RECIPROCAL,
- FUNC_RGB2HSV,
- FUNC_HSV2RGB,
FUNC_ABS,
FUNC_ACOS,
FUNC_ACOSH,
@@ -969,15 +1080,15 @@ public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+ virtual void set_op_type(OpType p_op_type) override;
+
void set_function(Function p_func);
Function get_function() const;
@@ -998,6 +1109,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode {
public:
enum Function {
FUNC_GRAYSCALE,
+ FUNC_HSV2RGB,
+ FUNC_RGB2HSV,
FUNC_SEPIA,
FUNC_MAX,
};
@@ -1097,7 +1210,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -1144,20 +1257,20 @@ public:
/// LENGTH
///////////////////////////////////////
-class VisualShaderNodeVectorLen : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode);
+class VisualShaderNodeVectorLen : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorLen, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeVectorLen();
@@ -1197,7 +1310,9 @@ public:
enum OpType {
OP_TYPE_FLOAT,
OP_TYPE_INT,
- OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_4D,
OP_TYPE_MAX,
};
@@ -1229,54 +1344,21 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeClamp::OpType)
///////////////////////////////////////
-/// DERIVATIVE FUNCTIONS
+/// DERIVATIVE FUNCTION
///////////////////////////////////////
-class VisualShaderNodeScalarDerivativeFunc : public VisualShaderNode {
- GDCLASS(VisualShaderNodeScalarDerivativeFunc, VisualShaderNode);
+class VisualShaderNodeDerivativeFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeDerivativeFunc, VisualShaderNode);
public:
- enum Function {
- FUNC_SUM,
- FUNC_X,
- FUNC_Y,
- FUNC_MAX,
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_4D,
+ OP_TYPE_MAX,
};
-protected:
- Function func = FUNC_SUM;
-
- static void _bind_methods();
-
-public:
- virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
-
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
- virtual String get_output_port_name(int p_port) const override;
-
- virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
-
- void set_function(Function p_func);
- Function get_function() const;
-
- virtual Vector<StringName> get_editable_properties() const override;
-
- VisualShaderNodeScalarDerivativeFunc();
-};
-
-VARIANT_ENUM_CAST(VisualShaderNodeScalarDerivativeFunc::Function)
-
-///////////////////////////////////////
-
-class VisualShaderNodeVectorDerivativeFunc : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorDerivativeFunc, VisualShaderNode);
-
-public:
enum Function {
FUNC_SUM,
FUNC_X,
@@ -1285,8 +1367,10 @@ public:
};
protected:
+ OpType op_type = OP_TYPE_SCALAR;
Function func = FUNC_SUM;
+protected:
static void _bind_methods();
public:
@@ -1302,34 +1386,37 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+ void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
+
void set_function(Function p_func);
Function get_function() const;
virtual Vector<StringName> get_editable_properties() const override;
- VisualShaderNodeVectorDerivativeFunc();
+ VisualShaderNodeDerivativeFunc();
};
-VARIANT_ENUM_CAST(VisualShaderNodeVectorDerivativeFunc::Function)
+VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::OpType)
+VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::Function)
///////////////////////////////////////
/// FACEFORWARD
///////////////////////////////////////
-class VisualShaderNodeFaceForward : public VisualShaderNode {
- GDCLASS(VisualShaderNodeFaceForward, VisualShaderNode);
+class VisualShaderNodeFaceForward : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeFaceForward, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeFaceForward();
@@ -1368,8 +1455,12 @@ class VisualShaderNodeStep : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
- OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_2D_SCALAR,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_3D_SCALAR,
+ OP_TYPE_VECTOR_4D,
+ OP_TYPE_VECTOR_4D_SCALAR,
OP_TYPE_MAX,
};
@@ -1410,8 +1501,12 @@ class VisualShaderNodeSmoothStep : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
- OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_2D_SCALAR,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_3D_SCALAR,
+ OP_TYPE_VECTOR_4D,
+ OP_TYPE_VECTOR_4D_SCALAR,
OP_TYPE_MAX,
};
@@ -1446,20 +1541,20 @@ VARIANT_ENUM_CAST(VisualShaderNodeSmoothStep::OpType)
/// DISTANCE
///////////////////////////////////////
-class VisualShaderNodeVectorDistance : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorDistance, VisualShaderNode);
+class VisualShaderNodeVectorDistance : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorDistance, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeVectorDistance();
@@ -1498,8 +1593,12 @@ class VisualShaderNodeMix : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
- OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_2D_SCALAR,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_3D_SCALAR,
+ OP_TYPE_VECTOR_4D,
+ OP_TYPE_VECTOR_4D_SCALAR,
OP_TYPE_MAX,
};
@@ -1534,8 +1633,8 @@ VARIANT_ENUM_CAST(VisualShaderNodeMix::OpType)
/// COMPOSE
///////////////////////////////////////
-class VisualShaderNodeVectorCompose : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorCompose, VisualShaderNode);
+class VisualShaderNodeVectorCompose : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorCompose, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
@@ -1545,9 +1644,9 @@ public:
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeVectorCompose();
@@ -1578,20 +1677,20 @@ public:
/// DECOMPOSE
///////////////////////////////////////
-class VisualShaderNodeVectorDecompose : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorDecompose, VisualShaderNode);
+class VisualShaderNodeVectorDecompose : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorDecompose, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeVectorDecompose();
@@ -1822,6 +1921,8 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ bool is_output_port_expandable(int p_port) const override;
+
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -1843,6 +1944,49 @@ public:
///////////////////////////////////////
+class VisualShaderNodeVec2Uniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeVec2Uniform, VisualShaderNodeUniform);
+
+private:
+ bool default_value_enabled = false;
+ Vector2 default_value;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_use_prop_slots() const override;
+
+ void set_default_value_enabled(bool p_enabled);
+ bool is_default_value_enabled() const;
+
+ void set_default_value(const Vector2 &p_value);
+ Vector2 get_default_value() const;
+
+ bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeVec2Uniform();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform);
@@ -1886,6 +2030,49 @@ public:
///////////////////////////////////////
+class VisualShaderNodeVec4Uniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeVec4Uniform, VisualShaderNodeUniform);
+
+private:
+ bool default_value_enabled = false;
+ Quaternion default_value;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_use_prop_slots() const override;
+
+ void set_default_value_enabled(bool p_enabled);
+ bool is_default_value_enabled() const;
+
+ void set_default_value(const Quaternion &p_value);
+ Quaternion get_default_value() const;
+
+ bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeVec4Uniform();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform);
@@ -1937,7 +2124,7 @@ public:
TYPE_DATA,
TYPE_COLOR,
TYPE_NORMAL_MAP,
- TYPE_ANISO,
+ TYPE_ANISOTROPY,
TYPE_MAX,
};
@@ -1947,9 +2134,29 @@ public:
COLOR_DEFAULT_MAX,
};
+ enum TextureFilter {
+ FILTER_DEFAULT,
+ FILTER_NEAREST,
+ FILTER_LINEAR,
+ FILTER_NEAREST_MIPMAP,
+ FILTER_LINEAR_MIPMAP,
+ FILTER_NEAREST_MIPMAP_ANISOTROPIC,
+ FILTER_LINEAR_MIPMAP_ANISOTROPIC,
+ FILTER_MAX,
+ };
+
+ enum TextureRepeat {
+ REPEAT_DEFAULT,
+ REPEAT_ENABLED,
+ REPEAT_DISABLED,
+ REPEAT_MAX,
+ };
+
protected:
TextureType texture_type = TYPE_DATA;
ColorDefault color_default = COLOR_DEFAULT_WHITE;
+ TextureFilter texture_filter = FILTER_DEFAULT;
+ TextureRepeat texture_repeat = REPEAT_DEFAULT;
protected:
static void _bind_methods();
@@ -1960,7 +2167,6 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -1969,7 +2175,8 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- virtual bool is_code_generated() const override;
+ virtual HashMap<StringName, String> get_editable_properties_names() const override;
+ virtual bool is_show_prop_names() const override;
Vector<StringName> get_editable_properties() const override;
@@ -1979,6 +2186,12 @@ public:
void set_color_default(ColorDefault p_default);
ColorDefault get_color_default() const;
+ void set_texture_filter(TextureFilter p_filter);
+ TextureFilter get_texture_filter() const;
+
+ void set_texture_repeat(TextureRepeat p_repeat);
+ TextureRepeat get_texture_repeat() const;
+
bool is_qualifier_supported(Qualifier p_qual) const override;
bool is_convertible_to_constant() const override;
@@ -1987,6 +2200,8 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType)
VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault)
+VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureFilter)
+VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureRepeat)
///////////////////////////////////////
@@ -2000,9 +2215,13 @@ public:
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2016,16 +2235,8 @@ class VisualShaderNodeTexture2DArrayUniform : public VisualShaderNodeTextureUnif
public:
virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
-
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2039,16 +2250,8 @@ class VisualShaderNodeTexture3DUniform : public VisualShaderNodeTextureUniform {
public:
virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
-
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2062,16 +2265,8 @@ class VisualShaderNodeCubemapUniform : public VisualShaderNodeTextureUniform {
public:
virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
-
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2112,7 +2307,9 @@ public:
enum OpType {
OP_TYPE_FLOAT,
OP_TYPE_INT,
- OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_4D,
OP_TYPE_BOOLEAN,
OP_TYPE_TRANSFORM,
OP_TYPE_MAX,
@@ -2164,7 +2361,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual bool is_generate_input_var(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2225,7 +2422,9 @@ public:
enum ComparisonType {
CTYPE_SCALAR,
CTYPE_SCALAR_INT,
- CTYPE_VECTOR,
+ CTYPE_VECTOR_2D,
+ CTYPE_VECTOR_3D,
+ CTYPE_VECTOR_4D,
CTYPE_BOOLEAN,
CTYPE_TRANSFORM,
CTYPE_MAX,
@@ -2293,7 +2492,9 @@ class VisualShaderNodeMultiplyAdd : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_4D,
OP_TYPE_MAX,
};
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 1a829968e3..54df935168 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,7 +39,10 @@ int VisualShaderNodeParticleEmitter::get_output_port_count() const {
}
VisualShaderNodeParticleEmitter::PortType VisualShaderNodeParticleEmitter::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ if (mode_2d) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const {
@@ -54,6 +57,9 @@ bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const
}
void VisualShaderNodeParticleEmitter::set_mode_2d(bool p_enabled) {
+ if (mode_2d == p_enabled) {
+ return;
+ }
mode_2d = p_enabled;
emit_changed();
}
@@ -68,9 +74,9 @@ Vector<StringName> VisualShaderNodeParticleEmitter::get_editable_properties() co
return props;
}
-Map<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const {
- Map<StringName, String> names;
- names.insert("mode_2d", TTR("2D Mode"));
+HashMap<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const {
+ HashMap<StringName, String> names;
+ names.insert("mode_2d", RTR("2D Mode"));
return names;
}
@@ -111,7 +117,7 @@ String VisualShaderNodeParticleSphereEmitter::get_input_port_name(int p_port) co
return String();
}
-String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
code += "vec2 __get_random_point_in_circle(inout uint seed, float radius, float inner_radius) {\n";
@@ -129,7 +135,7 @@ String VisualShaderNodeParticleSphereEmitter::generate_code(Shader::Mode p_mode,
String code;
if (mode_2d) {
- code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_circle(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n";
+ code += " " + p_output_vars[0] + " = __get_random_point_in_circle(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
} else {
code += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
}
@@ -154,11 +160,27 @@ int VisualShaderNodeParticleBoxEmitter::get_input_port_count() const {
VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleBoxEmitter::get_input_port_type(int p_port) const {
if (p_port == 0) {
- return PORT_TYPE_VECTOR;
+ if (mode_2d) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR;
}
+void VisualShaderNodeParticleBoxEmitter::set_mode_2d(bool p_enabled) {
+ if (mode_2d == p_enabled) {
+ return;
+ }
+ if (p_enabled) {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } else {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ }
+ mode_2d = p_enabled;
+ emit_changed();
+}
+
String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const {
if (p_port == 0) {
return "extents";
@@ -166,7 +188,7 @@ String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const
return String();
}
-String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
code += "vec2 __get_random_point_in_box2d(inout uint seed, vec2 extents) {\n";
@@ -185,7 +207,7 @@ String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode
String VisualShaderNodeParticleBoxEmitter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
if (mode_2d) {
- code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_box2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ".xy), 0.0);\n";
+ code += " " + p_output_vars[0] + " = __get_random_point_in_box2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
} else {
code += " " + p_output_vars[0] + " = __get_random_point_in_box3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
}
@@ -221,7 +243,7 @@ String VisualShaderNodeParticleRingEmitter::get_input_port_name(int p_port) cons
return String();
}
-String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
code += "vec2 __get_random_point_on_ring2d(inout uint seed, float radius, float inner_radius) {\n";
@@ -243,7 +265,7 @@ String VisualShaderNodeParticleRingEmitter::generate_code(Shader::Mode p_mode, V
String code;
if (mode_2d) {
- code = " " + p_output_vars[0] + " = vec3(__get_random_point_on_ring2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n";
+ code = " " + p_output_vars[0] + " = __get_random_point_on_ring2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
} else {
code = " " + p_output_vars[0] + " = __get_random_point_on_ring3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n";
}
@@ -269,18 +291,24 @@ int VisualShaderNodeParticleMeshEmitter::get_output_port_count() const {
VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleMeshEmitter::get_output_port_type(int p_port) const {
switch (p_port) {
- case 0:
- return PORT_TYPE_VECTOR; // position
- case 1:
- return PORT_TYPE_VECTOR; // normal
- case 2:
- return PORT_TYPE_VECTOR; // color
- case 3:
- return PORT_TYPE_SCALAR; // alpha
- case 4:
- return PORT_TYPE_VECTOR; // uv
- case 5:
- return PORT_TYPE_VECTOR; // uv2
+ case 0: // position
+ if (mode_2d) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ return PORT_TYPE_VECTOR_3D;
+ case 1: // normal
+ if (mode_2d) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ return PORT_TYPE_VECTOR_3D;
+ case 2: // color
+ return PORT_TYPE_VECTOR_3D;
+ case 3: // alpha
+ return PORT_TYPE_SCALAR;
+ case 4: // uv
+ return PORT_TYPE_VECTOR_2D;
+ case 5: // uv2
+ return PORT_TYPE_VECTOR_2D;
}
return PORT_TYPE_SCALAR;
}
@@ -318,43 +346,45 @@ String VisualShaderNodeParticleMeshEmitter::get_input_port_name(int p_port) cons
String VisualShaderNodeParticleMeshEmitter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code;
- if (mesh.is_valid()) {
- if (is_output_port_connected(0)) { // position
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_vx") + ";\n";
- }
+ if (is_output_port_connected(0)) { // position
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_vx") + ";\n";
+ }
- if (is_output_port_connected(1)) { // normal
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_nm") + ";\n";
- }
+ if (is_output_port_connected(1)) { // normal
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_nm") + ";\n";
+ }
- if (is_output_port_connected(2) || is_output_port_connected(3)) { // color & alpha
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_col") + ";\n";
- }
+ if (is_output_port_connected(2) || is_output_port_connected(3)) { // color & alpha
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_col") + ";\n";
+ }
- if (is_output_port_connected(4)) { // uv
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_uv") + ";\n";
- }
+ if (is_output_port_connected(4)) { // uv
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_uv") + ";\n";
+ }
- if (is_output_port_connected(5)) { // uv2
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_uv2") + ";\n";
- }
+ if (is_output_port_connected(5)) { // uv2
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_uv2") + ";\n";
}
return code;
}
-String VisualShaderNodeParticleMeshEmitter::_generate_code(VisualShader::Type p_type, int p_id, const String *p_output_vars, int p_index, const String &p_texture_name, bool p_ignore_mode2d) const {
+String VisualShaderNodeParticleMeshEmitter::_generate_code(VisualShader::Type p_type, int p_id, const String *p_output_vars, int p_index, const String &p_texture_name, PortType p_port_type) const {
String code;
if (is_output_port_connected(p_index)) {
- if (mode_2d && !p_ignore_mode2d) {
- code += " " + p_output_vars[p_index] + " = vec3(";
- code += "texelFetch(";
- code += make_unique_id(p_type, p_id, p_texture_name) + ", ";
- code += "ivec2(__scalar_ibuff, 0), 0).xy, 0.0);\n";
- } else {
- code += " " + p_output_vars[p_index] + " = texelFetch(";
- code += make_unique_id(p_type, p_id, p_texture_name) + ", ";
- code += "ivec2(__scalar_ibuff, 0), 0).xyz;\n";
+ switch (p_port_type) {
+ case PORT_TYPE_VECTOR_2D: {
+ code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xy;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name));
+ } break;
+ case PORT_TYPE_VECTOR_3D: {
+ if (mode_2d) {
+ code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xy;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name));
+ } else {
+ code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xyz;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name));
+ }
+ } break;
+ default:
+ break;
}
}
return code;
@@ -364,27 +394,22 @@ String VisualShaderNodeParticleMeshEmitter::generate_code(Shader::Mode p_mode, V
String code;
code += " __scalar_ibuff = int(__rand_from_seed(__seed) * 65535.0) % " + itos(position_texture->get_width()) + ";\n";
- code += _generate_code(p_type, p_id, p_output_vars, 0, "mesh_vx");
- code += _generate_code(p_type, p_id, p_output_vars, 1, "mesh_nm");
+ code += _generate_code(p_type, p_id, p_output_vars, 0, "mesh_vx", VisualShaderNode::PORT_TYPE_VECTOR_3D);
+ code += _generate_code(p_type, p_id, p_output_vars, 1, "mesh_nm", VisualShaderNode::PORT_TYPE_VECTOR_3D);
if (is_output_port_connected(2) || is_output_port_connected(3)) {
- code += " __vec4_buff = texelFetch(";
- code += make_unique_id(p_type, p_id, "mesh_col") + ", ";
- code += "ivec2(__scalar_ibuff, 0), 0);\n";
+ code += vformat(" __vec4_buff = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0);\n", make_unique_id(p_type, p_id, "mesh_col"));
+
if (is_output_port_connected(2)) {
code += " " + p_output_vars[2] + " = __vec4_buff.rgb;\n";
- } else {
- code += " " + p_output_vars[2] + " = vec3(0.0);\n";
}
if (is_output_port_connected(3)) {
code += " " + p_output_vars[3] + " = __vec4_buff.a;\n";
- } else {
- code += " " + p_output_vars[3] + " = 0.0;\n";
}
}
- code += _generate_code(p_type, p_id, p_output_vars, 4, "mesh_uv", true);
- code += _generate_code(p_type, p_id, p_output_vars, 5, "mesh_uv2", true);
+ code += _generate_code(p_type, p_id, p_output_vars, 4, "mesh_uv", VisualShaderNode::PORT_TYPE_VECTOR_2D);
+ code += _generate_code(p_type, p_id, p_output_vars, 5, "mesh_uv2", VisualShaderNode::PORT_TYPE_VECTOR_2D);
return code;
}
@@ -503,67 +528,97 @@ void VisualShaderNodeParticleMeshEmitter::_update_textures() {
Vector<Vector2> uvs;
Vector<Vector2> uvs2;
+ const int surface_count = mesh->get_surface_count();
+
if (use_all_surfaces) {
- for (int i = 0; i < max_surface_index; i++) {
+ for (int i = 0; i < surface_count; i++) {
+ const Array surface_arrays = mesh->surface_get_arrays(i);
+ const int surface_arrays_size = surface_arrays.size();
+
// position
- Array vertex_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_VERTEX];
- for (int j = 0; j < vertex_array.size(); j++) {
- vertices.push_back((Vector3)vertex_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_VERTEX) {
+ Array vertex_array = surface_arrays[Mesh::ARRAY_VERTEX];
+ for (int j = 0; j < vertex_array.size(); j++) {
+ vertices.push_back((Vector3)vertex_array[j]);
+ }
}
// normal
- Array normal_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_NORMAL];
- for (int j = 0; j < normal_array.size(); j++) {
- normals.push_back((Vector3)normal_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_NORMAL) {
+ Array normal_array = surface_arrays[Mesh::ARRAY_NORMAL];
+ for (int j = 0; j < normal_array.size(); j++) {
+ normals.push_back((Vector3)normal_array[j]);
+ }
}
// color
- Array color_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_COLOR];
- for (int j = 0; j < color_array.size(); j++) {
- colors.push_back((Color)color_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_COLOR) {
+ Array color_array = surface_arrays[Mesh::ARRAY_COLOR];
+ for (int j = 0; j < color_array.size(); j++) {
+ colors.push_back((Color)color_array[j]);
+ }
}
// uv
- Array uv_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_TEX_UV];
- for (int j = 0; j < uv_array.size(); j++) {
- uvs.push_back((Vector2)uv_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_TEX_UV) {
+ Array uv_array = surface_arrays[Mesh::ARRAY_TEX_UV];
+ for (int j = 0; j < uv_array.size(); j++) {
+ uvs.push_back((Vector2)uv_array[j]);
+ }
}
// uv2
- Array uv2_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_TEX_UV2];
- for (int j = 0; j < uv2_array.size(); j++) {
- uvs2.push_back((Vector2)uv2_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_TEX_UV2) {
+ Array uv2_array = surface_arrays[Mesh::ARRAY_TEX_UV2];
+ for (int j = 0; j < uv2_array.size(); j++) {
+ uvs2.push_back((Vector2)uv2_array[j]);
+ }
}
}
} else {
- // position
- Array vertex_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_VERTEX];
- for (int i = 0; i < vertex_array.size(); i++) {
- vertices.push_back((Vector3)vertex_array[i]);
- }
+ if (surface_index >= 0 && surface_index < surface_count) {
+ const Array surface_arrays = mesh->surface_get_arrays(surface_index);
+ const int surface_arrays_size = surface_arrays.size();
- // normal
- Array normal_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_NORMAL];
- for (int i = 0; i < normal_array.size(); i++) {
- normals.push_back((Vector3)normal_array[i]);
- }
+ // position
+ if (surface_arrays_size > Mesh::ARRAY_VERTEX) {
+ Array vertex_array = surface_arrays[Mesh::ARRAY_VERTEX];
+ for (int i = 0; i < vertex_array.size(); i++) {
+ vertices.push_back((Vector3)vertex_array[i]);
+ }
+ }
- // color
- Array color_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_COLOR];
- for (int i = 0; i < color_array.size(); i++) {
- colors.push_back((Color)color_array[i]);
- }
+ // normal
+ if (surface_arrays_size > Mesh::ARRAY_NORMAL) {
+ Array normal_array = surface_arrays[Mesh::ARRAY_NORMAL];
+ for (int i = 0; i < normal_array.size(); i++) {
+ normals.push_back((Vector3)normal_array[i]);
+ }
+ }
- // uv
- Array uv_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_TEX_UV];
- for (int j = 0; j < uv_array.size(); j++) {
- uvs.push_back((Vector2)uv_array[j]);
- }
+ // color
+ if (surface_arrays_size > Mesh::ARRAY_COLOR) {
+ Array color_array = surface_arrays[Mesh::ARRAY_COLOR];
+ for (int i = 0; i < color_array.size(); i++) {
+ colors.push_back((Color)color_array[i]);
+ }
+ }
+
+ // uv
+ if (surface_arrays_size > Mesh::ARRAY_TEX_UV) {
+ Array uv_array = surface_arrays[Mesh::ARRAY_TEX_UV];
+ for (int j = 0; j < uv_array.size(); j++) {
+ uvs.push_back((Vector2)uv_array[j]);
+ }
+ }
- // uv2
- Array uv2_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_TEX_UV2];
- for (int j = 0; j < uv2_array.size(); j++) {
- uvs2.push_back((Vector2)uv2_array[j]);
+ // uv2
+ if (surface_arrays_size > Mesh::ARRAY_TEX_UV2) {
+ Array uv2_array = surface_arrays[Mesh::ARRAY_TEX_UV2];
+ for (int j = 0; j < uv2_array.size(); j++) {
+ uvs2.push_back((Vector2)uv2_array[j]);
+ }
+ }
}
}
@@ -579,12 +634,6 @@ void VisualShaderNodeParticleMeshEmitter::set_mesh(Ref<Mesh> p_mesh) {
return;
}
- if (p_mesh.is_valid()) {
- max_surface_index = p_mesh->get_surface_count();
- } else {
- max_surface_index = 0;
- }
-
if (mesh.is_valid()) {
Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures);
@@ -623,7 +672,16 @@ bool VisualShaderNodeParticleMeshEmitter::is_use_all_surfaces() const {
}
void VisualShaderNodeParticleMeshEmitter::set_surface_index(int p_surface_index) {
- if (p_surface_index == surface_index || p_surface_index < 0 || p_surface_index >= max_surface_index) {
+ if (mesh.is_valid()) {
+ if (mesh->get_surface_count() > 0) {
+ p_surface_index = CLAMP(p_surface_index, 0, mesh->get_surface_count() - 1);
+ } else {
+ p_surface_index = 0;
+ }
+ } else if (p_surface_index < 0) {
+ p_surface_index = 0;
+ }
+ if (surface_index == p_surface_index) {
return;
}
surface_index = p_surface_index;
@@ -646,13 +704,13 @@ Vector<StringName> VisualShaderNodeParticleMeshEmitter::get_editable_properties(
return props;
}
-Map<StringName, String> VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const {
- Map<StringName, String> names = VisualShaderNodeParticleEmitter::get_editable_properties_names();
+HashMap<StringName, String> VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const {
+ HashMap<StringName, String> names = VisualShaderNodeParticleEmitter::get_editable_properties_names();
- names.insert("mesh", TTR("Mesh"));
- names.insert("use_all_surfaces", TTR("Use All Surfaces"));
+ names.insert("mesh", RTR("Mesh"));
+ names.insert("use_all_surfaces", RTR("Use All Surfaces"));
if (!use_all_surfaces) {
- names.insert("surface_index", TTR("Surface Index"));
+ names.insert("surface_index", RTR("Surface Index"));
}
return names;
@@ -700,7 +758,7 @@ int VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_count() const {
VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_type(int p_port) const {
if (p_port == 0 || p_port == 1) { // position, rotation_axis
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR; // angle (degrees/radians)
}
@@ -731,7 +789,7 @@ int VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_count() const {
}
VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_name(int p_port) const {
@@ -784,7 +842,7 @@ int VisualShaderNodeParticleConeVelocity::get_input_port_count() const {
VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_input_port_type(int p_port) const {
if (p_port == 0) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
} else if (p_port == 1) {
return PORT_TYPE_SCALAR;
}
@@ -805,7 +863,7 @@ int VisualShaderNodeParticleConeVelocity::get_output_port_count() const {
}
VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) const {
@@ -845,10 +903,11 @@ void VisualShaderNodeParticleRandomness::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeParticleRandomness::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeParticleRandomness::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -867,8 +926,13 @@ int VisualShaderNodeParticleRandomness::get_output_port_count() const {
}
VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_output_port_type(int p_port) const {
- if (op_type == OP_TYPE_VECTOR) {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ default:
+ break;
}
return PORT_TYPE_SCALAR;
}
@@ -882,8 +946,13 @@ int VisualShaderNodeParticleRandomness::get_input_port_count() const {
}
VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_input_port_type(int p_port) const {
- if (op_type == OP_TYPE_VECTOR) {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ default:
+ break;
}
return PORT_TYPE_SCALAR;
}
@@ -899,10 +968,18 @@ String VisualShaderNodeParticleRandomness::get_input_port_name(int p_port) const
String VisualShaderNodeParticleRandomness::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
- if (op_type == OP_TYPE_SCALAR) {
- code += vformat(" %s = __randf_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
- } else if (op_type == OP_TYPE_VECTOR) {
- code += vformat(" %s = __randv_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ switch (op_type) {
+ case OP_TYPE_SCALAR: {
+ code += vformat(" %s = __randf_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ code += vformat(" %s = __randv2_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ code += vformat(" %s = __randv3_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ } break;
+ default:
+ break;
}
return code;
}
@@ -912,12 +989,21 @@ void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
if (op_type == p_op_type) {
return;
}
- if (p_op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 1.0);
- } else {
- set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
+ default:
+ break;
}
op_type = p_op_type;
emit_changed();
@@ -932,7 +1018,7 @@ bool VisualShaderNodeParticleRandomness::has_output_port_preview(int p_port) con
}
VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() {
- set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(0, -1.0);
set_input_port_default_value(1, 1.0);
}
@@ -965,7 +1051,7 @@ int VisualShaderNodeParticleAccelerator::get_output_port_count() const {
}
VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeParticleAccelerator::get_output_port_name(int p_port) const {
@@ -978,11 +1064,11 @@ int VisualShaderNodeParticleAccelerator::get_input_port_count() const {
VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_input_port_type(int p_port) const {
if (p_port == 0) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
} else if (p_port == 1) {
return PORT_TYPE_SCALAR;
} else if (p_port == 2) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR;
}
@@ -1075,19 +1161,19 @@ VisualShaderNodeParticleOutput::PortType VisualShaderNodeParticleOutput::get_inp
switch (p_port) {
case 0:
if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
- return PORT_TYPE_VECTOR; // custom.rgb
+ return PORT_TYPE_VECTOR_3D; // custom.rgb
}
return PORT_TYPE_BOOLEAN; // active
case 1:
if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
break; // custom.a (scalar)
}
- return PORT_TYPE_VECTOR; // velocity
+ return PORT_TYPE_VECTOR_3D; // velocity
case 2:
- return PORT_TYPE_VECTOR; // color & velocity
+ return PORT_TYPE_VECTOR_3D; // color & velocity
case 3:
if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
- return PORT_TYPE_VECTOR; // color
+ return PORT_TYPE_VECTOR_3D; // color
}
break; // alpha (scalar)
case 4:
@@ -1100,18 +1186,18 @@ VisualShaderNodeParticleOutput::PortType VisualShaderNodeParticleOutput::get_inp
if (shader_type == VisualShader::TYPE_COLLIDE) {
return PORT_TYPE_TRANSFORM; // transform
}
- return PORT_TYPE_VECTOR; // position
+ return PORT_TYPE_VECTOR_3D; // position
case 5:
if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
return PORT_TYPE_TRANSFORM; // transform
}
if (shader_type == VisualShader::TYPE_PROCESS) {
- return PORT_TYPE_VECTOR; // rotation_axis
+ return PORT_TYPE_VECTOR_3D; // rotation_axis
}
break; // scale (scalar)
case 6:
if (shader_type == VisualShader::TYPE_START) {
- return PORT_TYPE_VECTOR; // rotation_axis
+ return PORT_TYPE_VECTOR_3D; // rotation_axis
}
break;
case 7:
@@ -1232,7 +1318,7 @@ String VisualShaderNodeParticleOutput::generate_code(Shader::Mode p_mode, Visual
code += tab + "TRANSFORM = " + p_input_vars[5] + ";\n";
}
} else {
- if (!p_input_vars[0].is_empty()) { // active (begin)
+ if (!p_input_vars[0].is_empty()) { // Active (begin).
code += tab + "ACTIVE = " + p_input_vars[0] + ";\n";
code += tab + "if(ACTIVE) {\n";
tab += " ";
@@ -1295,7 +1381,7 @@ String VisualShaderNodeParticleOutput::generate_code(Shader::Mode p_mode, Visual
code += tab + "TRANSFORM " + op + " mat4(vec4(" + p_input_vars[scale] + ", 0, 0, 0), vec4(0, " + p_input_vars[scale] + ", 0, 0), vec4(0, 0, " + p_input_vars[scale] + ", 0), vec4(0, 0, 0, 1));\n";
}
}
- if (!p_input_vars[0].is_empty()) { // active (end)
+ if (!p_input_vars[0].is_empty()) { // Active (end).
code += " }\n";
}
}
@@ -1341,13 +1427,13 @@ VisualShaderNodeParticleEmit::PortType VisualShaderNodeParticleEmit::get_input_p
case 1:
return PORT_TYPE_TRANSFORM;
case 2:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 3:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 4:
return PORT_TYPE_SCALAR;
case 5:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 6:
return PORT_TYPE_SCALAR;
}
@@ -1417,22 +1503,22 @@ bool VisualShaderNodeParticleEmit::is_generate_input_var(int p_port) const {
return true;
}
-String VisualShaderNodeParticleEmit::get_input_port_default_hint(int p_port) const {
+bool VisualShaderNodeParticleEmit::is_input_port_default(int p_port, Shader::Mode p_mode) const {
switch (p_port) {
case 1:
- return "default";
+ return true;
case 2:
- return "default";
+ return true;
case 3:
- return "default";
+ return true;
case 4:
- return "default";
+ return true;
case 5:
- return "default";
+ return true;
case 6:
- return "default";
+ return true;
}
- return String();
+ return false;
}
String VisualShaderNodeParticleEmit::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index 79459432f1..05a059373b 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -48,11 +48,11 @@ public:
virtual String get_output_port_name(int p_port) const override;
virtual bool has_output_port_preview(int p_port) const override;
- void set_mode_2d(bool p_enabled);
+ virtual void set_mode_2d(bool p_enabled);
bool is_mode_2d() const;
Vector<StringName> get_editable_properties() const override;
- virtual Map<StringName, String> get_editable_properties_names() const override;
+ virtual HashMap<StringName, String> get_editable_properties_names() const override;
bool is_show_prop_names() const override;
VisualShaderNodeParticleEmitter();
@@ -68,7 +68,7 @@ public:
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeParticleSphereEmitter();
@@ -83,8 +83,9 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
+ virtual void set_mode_2d(bool p_enabled) override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeParticleBoxEmitter();
@@ -100,7 +101,7 @@ public:
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeParticleRingEmitter();
@@ -111,7 +112,6 @@ class VisualShaderNodeParticleMeshEmitter : public VisualShaderNodeParticleEmitt
Ref<Mesh> mesh;
bool use_all_surfaces = true;
int surface_index = 0;
- int max_surface_index = 0;
Ref<ImageTexture> position_texture;
Ref<ImageTexture> normal_texture;
@@ -119,7 +119,7 @@ class VisualShaderNodeParticleMeshEmitter : public VisualShaderNodeParticleEmitt
Ref<ImageTexture> uv_texture;
Ref<ImageTexture> uv2_texture;
- String _generate_code(VisualShader::Type p_type, int p_id, const String *p_output_vars, int p_index, const String &p_texture_name, bool p_ignore_mode2d = false) const;
+ String _generate_code(VisualShader::Type p_type, int p_id, const String *p_output_vars, int p_index, const String &p_texture_name, PortType p_port_type) const;
void _update_texture(const Vector<Vector2> &p_array, Ref<ImageTexture> &r_texture);
void _update_texture(const Vector<Vector3> &p_array, Ref<ImageTexture> &r_texture);
@@ -153,7 +153,7 @@ public:
int get_surface_index() const;
Vector<StringName> get_editable_properties() const override;
- Map<StringName, String> get_editable_properties_names() const override;
+ HashMap<StringName, String> get_editable_properties_names() const override;
Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
VisualShaderNodeParticleMeshEmitter();
@@ -214,7 +214,8 @@ class VisualShaderNodeParticleRandomness : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
OP_TYPE_MAX,
};
@@ -343,7 +344,7 @@ public:
virtual bool is_show_prop_names() const override;
virtual bool is_generate_input_var(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeParticleEmit();
diff --git a/scene/resources/visual_shader_sdf_nodes.cpp b/scene/resources/visual_shader_sdf_nodes.cpp
index 14c655b129..cbd3ebd83b 100644
--- a/scene/resources/visual_shader_sdf_nodes.cpp
+++ b/scene/resources/visual_shader_sdf_nodes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,7 +41,7 @@ int VisualShaderNodeSDFToScreenUV::get_input_port_count() const {
}
VisualShaderNodeSDFToScreenUV::PortType VisualShaderNodeSDFToScreenUV::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeSDFToScreenUV::get_input_port_name(int p_port) const {
@@ -53,7 +53,7 @@ int VisualShaderNodeSDFToScreenUV::get_output_port_count() const {
}
VisualShaderNodeSDFToScreenUV::PortType VisualShaderNodeSDFToScreenUV::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeSDFToScreenUV::get_output_port_name(int p_port) const {
@@ -61,7 +61,7 @@ String VisualShaderNodeSDFToScreenUV::get_output_port_name(int p_port) const {
}
String VisualShaderNodeSDFToScreenUV::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = vec3(sdf_to_screen_uv(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+ return " " + p_output_vars[0] + " = sdf_to_screen_uv(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0]) + ");\n";
}
VisualShaderNodeSDFToScreenUV::VisualShaderNodeSDFToScreenUV() {
@@ -78,7 +78,7 @@ int VisualShaderNodeScreenUVToSDF::get_input_port_count() const {
}
VisualShaderNodeScreenUVToSDF::PortType VisualShaderNodeScreenUVToSDF::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeScreenUVToSDF::get_input_port_name(int p_port) const {
@@ -90,22 +90,22 @@ int VisualShaderNodeScreenUVToSDF::get_output_port_count() const {
}
VisualShaderNodeScreenUVToSDF::PortType VisualShaderNodeScreenUVToSDF::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeScreenUVToSDF::get_output_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeScreenUVToSDF::get_input_port_default_hint(int p_port) const {
+bool VisualShaderNodeScreenUVToSDF::is_input_port_default(int p_port, Shader::Mode p_mode) const {
if (p_port == 0) {
- return "default";
+ return true;
}
- return "";
+ return false;
}
String VisualShaderNodeScreenUVToSDF::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = vec3(screen_uv_to_sdf(" + (p_input_vars[0] == String() ? "SCREEN_UV" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+ return " " + p_output_vars[0] + " = screen_uv_to_sdf(" + (p_input_vars[0].is_empty() ? "SCREEN_UV" : p_input_vars[0]) + ");\n";
}
VisualShaderNodeScreenUVToSDF::VisualShaderNodeScreenUVToSDF() {
@@ -122,7 +122,7 @@ int VisualShaderNodeTextureSDF::get_input_port_count() const {
}
VisualShaderNodeTextureSDF::PortType VisualShaderNodeTextureSDF::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeTextureSDF::get_input_port_name(int p_port) const {
@@ -142,7 +142,7 @@ String VisualShaderNodeTextureSDF::get_output_port_name(int p_port) const {
}
String VisualShaderNodeTextureSDF::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = texture_sdf(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + ");\n";
+ return " " + p_output_vars[0] + " = texture_sdf(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0]) + ");\n";
}
VisualShaderNodeTextureSDF::VisualShaderNodeTextureSDF() {
@@ -159,7 +159,7 @@ int VisualShaderNodeTextureSDFNormal::get_input_port_count() const {
}
VisualShaderNodeTextureSDFNormal::PortType VisualShaderNodeTextureSDFNormal::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeTextureSDFNormal::get_input_port_name(int p_port) const {
@@ -171,7 +171,7 @@ int VisualShaderNodeTextureSDFNormal::get_output_port_count() const {
}
VisualShaderNodeTextureSDFNormal::PortType VisualShaderNodeTextureSDFNormal::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeTextureSDFNormal::get_output_port_name(int p_port) const {
@@ -179,7 +179,7 @@ String VisualShaderNodeTextureSDFNormal::get_output_port_name(int p_port) const
}
String VisualShaderNodeTextureSDFNormal::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = vec3(texture_sdf_normal(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+ return " " + p_output_vars[0] + " = texture_sdf_normal(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0]) + ");\n";
}
VisualShaderNodeTextureSDFNormal::VisualShaderNodeTextureSDFNormal() {
@@ -197,7 +197,7 @@ int VisualShaderNodeSDFRaymarch::get_input_port_count() const {
VisualShaderNodeSDFRaymarch::PortType VisualShaderNodeSDFRaymarch::get_input_port_type(int p_port) const {
if (p_port == 0 || p_port == 1) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
return PORT_TYPE_SCALAR;
}
@@ -221,7 +221,7 @@ VisualShaderNodeSDFRaymarch::PortType VisualShaderNodeSDFRaymarch::get_output_po
} else if (p_port == 1) {
return PORT_TYPE_BOOLEAN;
} else if (p_port == 2) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
return PORT_TYPE_SCALAR;
}
@@ -242,16 +242,16 @@ String VisualShaderNodeSDFRaymarch::generate_code(Shader::Mode p_mode, VisualSha
code += " {\n";
- if (p_input_vars[0] == String()) {
+ if (p_input_vars[0].is_empty()) {
code += " vec2 __from_pos = vec2(0.0f);\n";
} else {
- code += " vec2 __from_pos = " + p_input_vars[0] + ".xy;\n";
+ code += " vec2 __from_pos = " + p_input_vars[0] + ";\n";
}
- if (p_input_vars[1] == String()) {
+ if (p_input_vars[1].is_empty()) {
code += " vec2 __to_pos = vec2(0.0f);\n";
} else {
- code += " vec2 __to_pos = " + p_input_vars[1] + ".xy;\n";
+ code += " vec2 __to_pos = " + p_input_vars[1] + ";\n";
}
code += "\n vec2 __at = __from_pos;\n";
@@ -271,7 +271,7 @@ String VisualShaderNodeSDFRaymarch::generate_code(Shader::Mode p_mode, VisualSha
code += " float __dist = min(__max_dist, __accum);\n";
code += " " + p_output_vars[0] + " = __dist;\n";
code += " " + p_output_vars[1] + " = __accum < __max_dist;\n";
- code += " " + p_output_vars[2] + " = vec3(__from_pos + __dir * __dist, 0.0f);\n";
+ code += " " + p_output_vars[2] + " = __from_pos + __dir * __dist;\n";
code += " }\n";
diff --git a/scene/resources/visual_shader_sdf_nodes.h b/scene/resources/visual_shader_sdf_nodes.h
index 0fcf5ec0b5..7c1f695423 100644
--- a/scene/resources/visual_shader_sdf_nodes.h
+++ b/scene/resources/visual_shader_sdf_nodes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -66,7 +66,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeScreenUVToSDF();
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index eceb42ee14..9d8e0f7547 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -83,8 +83,8 @@ World2D::World2D() {
// Create and configure the navigation_map to be more friendly with pixels than meters.
navigation_map = NavigationServer2D::get_singleton()->map_create();
NavigationServer2D::get_singleton()->map_set_active(navigation_map, true);
- NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 10));
- NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 5));
+ NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 1));
+ NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 1));
}
World2D::~World2D() {
diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h
index 65f89c8f64..71ae40ec82 100644
--- a/scene/resources/world_2d.h
+++ b/scene/resources/world_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef WORLD_2D_H
#define WORLD_2D_H
-#include "core/config/project_settings.h"
#include "core/io/resource.h"
+#include "scene/resources/world_2d.h"
#include "servers/physics_server_2d.h"
class VisibleOnScreenNotifier2D;
@@ -46,7 +46,7 @@ class World2D : public Resource {
RID space;
RID navigation_map;
- Set<Viewport *> viewports;
+ RBSet<Viewport *> viewports;
protected:
static void _bind_methods();
@@ -62,7 +62,7 @@ public:
PhysicsDirectSpaceState2D *get_direct_space_state();
- _FORCE_INLINE_ const Set<Viewport *> &get_viewports() { return viewports; }
+ _FORCE_INLINE_ const RBSet<Viewport *> &get_viewports() { return viewports; }
World2D();
~World2D();
diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp
index 0e1b343eac..0088236112 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "world_3d.h"
-#include "core/math/camera_matrix.h"
+#include "core/config/project_settings.h"
#include "core/math/octree.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/visible_on_screen_notifier_3d.h"
diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h
index 2c5be35609..18d28e812f 100644
--- a/scene/resources/world_3d.h
+++ b/scene/resources/world_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -53,7 +53,7 @@ private:
Ref<Environment> fallback_environment;
Ref<CameraEffects> camera_effects;
- Set<Camera3D *> cameras;
+ RBSet<Camera3D *> cameras;
protected:
static void _bind_methods();
@@ -77,7 +77,7 @@ public:
void set_camera_effects(const Ref<CameraEffects> &p_camera_effects);
Ref<CameraEffects> get_camera_effects() const;
- _FORCE_INLINE_ const Set<Camera3D *> &get_cameras() const { return cameras; }
+ _FORCE_INLINE_ const RBSet<Camera3D *> &get_cameras() const { return cameras; }
PhysicsDirectSpaceState3D *get_direct_space_state();
diff --git a/scene/resources/world_boundary_shape_2d.cpp b/scene/resources/world_boundary_shape_2d.cpp
index 39af92793f..ac5be79d24 100644
--- a/scene/resources/world_boundary_shape_2d.cpp
+++ b/scene/resources/world_boundary_shape_2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -108,7 +108,7 @@ void WorldBoundaryShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_distance"), &WorldBoundaryShape2D::get_distance);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_distance", "get_distance");
}
WorldBoundaryShape2D::WorldBoundaryShape2D() :
diff --git a/scene/resources/world_boundary_shape_2d.h b/scene/resources/world_boundary_shape_2d.h
index 4cc60f5985..3275e8c916 100644
--- a/scene/resources/world_boundary_shape_2d.h
+++ b/scene/resources/world_boundary_shape_2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/resources/world_boundary_shape_3d.cpp b/scene/resources/world_boundary_shape_3d.cpp
index 8cde537164..09d41e8291 100644
--- a/scene/resources/world_boundary_shape_3d.cpp
+++ b/scene/resources/world_boundary_shape_3d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,6 @@
Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const {
Plane p = get_plane();
- Vector<Vector3> points;
Vector3 n1 = p.get_any_perpendicular_normal();
Vector3 n2 = p.normal.cross(n1).normalized();
@@ -46,16 +45,18 @@ Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const {
p.normal * p.d + n1 * -10.0 + n2 * 10.0,
};
- points.push_back(pface[0]);
- points.push_back(pface[1]);
- points.push_back(pface[1]);
- points.push_back(pface[2]);
- points.push_back(pface[2]);
- points.push_back(pface[3]);
- points.push_back(pface[3]);
- points.push_back(pface[0]);
- points.push_back(p.normal * p.d);
- points.push_back(p.normal * p.d + p.normal * 3);
+ Vector<Vector3> points = {
+ pface[0],
+ pface[1],
+ pface[1],
+ pface[2],
+ pface[2],
+ pface[3],
+ pface[3],
+ pface[0],
+ p.normal * p.d,
+ p.normal * p.d + p.normal * 3
+ };
return points;
}
diff --git a/scene/resources/world_boundary_shape_3d.h b/scene/resources/world_boundary_shape_3d.h
index 853f555ebc..5378bc52c7 100644
--- a/scene/resources/world_boundary_shape_3d.h
+++ b/scene/resources/world_boundary_shape_3d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 186764e69e..a15c03d675 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -61,6 +61,7 @@ SceneStringNames::SceneStringNames() {
animation_finished = StaticCString::create("animation_finished");
animation_changed = StaticCString::create("animation_changed");
animation_started = StaticCString::create("animation_started");
+ RESET = StaticCString::create("RESET");
pose_updated = StaticCString::create("pose_updated");
bone_pose_changed = StaticCString::create("bone_pose_changed");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 67007c85e0..5589ab327f 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -97,6 +97,7 @@ public:
StringName animation_finished;
StringName animation_changed;
StringName animation_started;
+ StringName RESET;
StringName pose_updated;
StringName bone_pose_changed;