summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml24
-rw-r--r--DONORS.md76
-rw-r--r--SConstruct9
-rw-r--r--core/color.cpp2
-rw-r--r--core/engine.cpp1
-rw-r--r--core/global_constants.cpp16
-rw-r--r--core/hash_map.h19
-rw-r--r--core/image.cpp4
-rw-r--r--core/input_map.cpp2
-rw-r--r--core/io/config_file.cpp4
-rw-r--r--core/io/file_access_encrypted.cpp4
-rw-r--r--core/io/file_access_network.cpp6
-rw-r--r--core/io/http_client.cpp6
-rw-r--r--core/io/xml_parser.cpp2
-rw-r--r--core/list.h3
-rw-r--r--core/math/a_star.cpp28
-rw-r--r--core/math/a_star.h1
-rw-r--r--core/math/expression.cpp9
-rw-r--r--core/math/expression.h1
-rw-r--r--core/math/math_funcs.h2
-rw-r--r--core/math/random_number_generator.h5
-rw-r--r--core/math/random_pcg.cpp8
-rw-r--r--core/math/random_pcg.h62
-rw-r--r--core/math/vector2.cpp7
-rw-r--r--core/math/vector2.h1
-rw-r--r--core/math/vector3.cpp7
-rw-r--r--core/math/vector3.h3
-rw-r--r--core/os/input_event.cpp13
-rw-r--r--core/os/input_event.h20
-rw-r--r--core/os/os.h2
-rw-r--r--core/project_settings.cpp10
-rw-r--r--core/script_language.h4
-rw-r--r--core/sort_array.h6
-rw-r--r--core/ustring.cpp51
-rw-r--r--core/variant_call.cpp12
-rw-r--r--core/vector.h2
-rw-r--r--doc/classes/@GDScript.xml27
-rw-r--r--doc/classes/@GlobalScope.xml36
-rw-r--r--doc/classes/ARVRInterface.xml7
-rw-r--r--doc/classes/Animation.xml13
-rw-r--r--doc/classes/AnimationNode.xml1
-rw-r--r--doc/classes/AnimationNodeAdd2.xml3
-rw-r--r--doc/classes/AnimationNodeAdd3.xml7
-rw-r--r--doc/classes/AnimationNodeAnimation.xml3
-rw-r--r--doc/classes/AnimationNodeBlend2.xml3
-rw-r--r--doc/classes/AnimationNodeBlend3.xml7
-rw-r--r--doc/classes/AnimationNodeBlendSpace1D.xml16
-rw-r--r--doc/classes/AnimationNodeBlendSpace2D.xml28
-rw-r--r--doc/classes/ArrayMesh.xml16
-rw-r--r--doc/classes/AudioEffectReverb.xml2
-rw-r--r--doc/classes/Bone2D.xml10
-rw-r--r--doc/classes/CPUParticles.xml121
-rw-r--r--doc/classes/CPUParticles2D.xml99
-rw-r--r--doc/classes/CameraFeed.xml64
-rw-r--r--doc/classes/CameraServer.xml83
-rw-r--r--doc/classes/CameraTexture.xml25
-rw-r--r--doc/classes/ClippedCamera.xml6
-rw-r--r--doc/classes/Color.xml2
-rw-r--r--doc/classes/Control.xml6
-rw-r--r--doc/classes/EditorPlugin.xml4
-rw-r--r--doc/classes/Environment.xml10
-rw-r--r--doc/classes/FileDialog.xml2
-rw-r--r--doc/classes/Image.xml2
-rw-r--r--doc/classes/InputEventAction.xml3
-rw-r--r--doc/classes/InputEventKey.xml2
-rw-r--r--doc/classes/LineEdit.xml1
-rw-r--r--doc/classes/Mesh.xml11
-rw-r--r--doc/classes/MeshInstance.xml1
-rw-r--r--doc/classes/MeshInstance2D.xml11
-rw-r--r--doc/classes/MultiMeshInstance2D.xml33
-rw-r--r--doc/classes/Navigation.xml2
-rw-r--r--doc/classes/OS.xml16
-rw-r--r--doc/classes/Object.xml15
-rw-r--r--doc/classes/Particles.xml5
-rw-r--r--doc/classes/Particles2D.xml4
-rw-r--r--doc/classes/ParticlesMaterial.xml17
-rw-r--r--doc/classes/Polygon2D.xml2
-rw-r--r--doc/classes/PopupMenu.xml3
-rw-r--r--doc/classes/Position2D.xml2
-rw-r--r--doc/classes/PrimitiveMesh.xml5
-rw-r--r--doc/classes/ProjectSettings.xml9
-rw-r--r--doc/classes/QuadMesh.xml1
-rw-r--r--doc/classes/Resource.xml2
-rw-r--r--doc/classes/RigidBody.xml4
-rw-r--r--doc/classes/String.xml32
-rw-r--r--doc/classes/TextEdit.xml4
-rw-r--r--doc/classes/Theme.xml2
-rw-r--r--doc/classes/TileSet.xml12
-rw-r--r--doc/classes/Translation.xml4
-rw-r--r--doc/classes/TranslationServer.xml4
-rw-r--r--doc/classes/TreeItem.xml6
-rw-r--r--doc/classes/TriangleMesh.xml2
-rw-r--r--doc/classes/Vector2.xml11
-rw-r--r--doc/classes/Vector3.xml11
-rw-r--r--doc/classes/VehicleWheel.xml2
-rw-r--r--doc/classes/VisualServer.xml13
-rwxr-xr-xdoc/tools/makerst.py47
-rw-r--r--drivers/dummy/rasterizer_dummy.h2
-rw-r--r--drivers/gles2/rasterizer_canvas_gles2.cpp2
-rw-r--r--drivers/gles2/rasterizer_scene_gles2.cpp96
-rw-r--r--drivers/gles2/rasterizer_scene_gles2.h4
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.cpp22
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.h1
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp15
-rw-r--r--drivers/gles2/shader_gles2.h2
-rw-r--r--drivers/gles2/shaders/canvas.glsl5
-rw-r--r--drivers/gles2/shaders/copy.glsl29
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp85
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h4
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp47
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h1
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp13
-rw-r--r--drivers/gles3/shaders/canvas.glsl5
-rw-r--r--drivers/gles3/shaders/copy.glsl36
-rw-r--r--drivers/unix/net_socket_posix.cpp6
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp16
-rw-r--r--drivers/windows/file_access_windows.cpp8
-rw-r--r--editor/animation_track_editor.cpp10
-rw-r--r--editor/animation_track_editor_plugins.cpp33
-rw-r--r--editor/audio_stream_preview.cpp2
-rw-r--r--editor/code_editor.cpp34
-rw-r--r--editor/code_editor.h1
-rw-r--r--editor/collada/collada.cpp4
-rw-r--r--editor/connections_dialog.cpp134
-rw-r--r--editor/connections_dialog.h10
-rw-r--r--editor/create_dialog.cpp9
-rw-r--r--editor/dependency_editor.cpp42
-rw-r--r--editor/dependency_editor.h1
-rw-r--r--editor/doc/doc_data.cpp3
-rw-r--r--editor/editor_about.cpp2
-rw-r--r--editor/editor_atlas_packer.cpp30
-rw-r--r--editor/editor_atlas_packer.h30
-rw-r--r--editor/editor_audio_buses.cpp417
-rw-r--r--editor/editor_audio_buses.h18
-rw-r--r--editor/editor_data.cpp1
-rw-r--r--editor/editor_export.cpp6
-rw-r--r--editor/editor_feature_profile.cpp30
-rw-r--r--editor/editor_feature_profile.h30
-rw-r--r--editor/editor_file_dialog.cpp53
-rw-r--r--editor/editor_file_dialog.h4
-rw-r--r--editor/editor_fonts.cpp22
-rw-r--r--editor/editor_help.cpp17
-rw-r--r--editor/editor_help_search.cpp95
-rw-r--r--editor/editor_help_search.h2
-rw-r--r--editor/editor_inspector.cpp5
-rw-r--r--editor/editor_node.cpp109
-rw-r--r--editor/editor_node.h13
-rw-r--r--editor/editor_plugin.cpp43
-rw-r--r--editor/editor_plugin.h2
-rw-r--r--editor/editor_run_native.cpp12
-rw-r--r--editor/editor_run_native.h5
-rw-r--r--editor/editor_settings.cpp52
-rw-r--r--editor/editor_settings.h2
-rw-r--r--editor/filesystem_dock.cpp35
-rw-r--r--editor/filesystem_dock.h1
-rw-r--r--editor/icons/icon_multi_mesh_instance_2d.svg4
-rw-r--r--editor/icons/icon_project_icon_loading.svg1
-rw-r--r--editor/icons/icon_visual_shader.svg113
-rw-r--r--editor/import/editor_scene_importer_gltf.cpp13
-rw-r--r--editor/import/resource_importer_texture_atlas.cpp30
-rw-r--r--editor/import/resource_importer_texture_atlas.h30
-rw-r--r--editor/import/resource_importer_wav.cpp2
-rw-r--r--editor/inspector_dock.cpp6
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp46
-rw-r--r--editor/plugins/animation_player_editor_plugin.h2
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp2
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp78
-rw-r--r--editor/plugins/asset_library_editor_plugin.h5
-rw-r--r--editor/plugins/audio_stream_editor_plugin.cpp13
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp41
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp23
-rw-r--r--editor/plugins/editor_preview_plugins.cpp2
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp30
-rw-r--r--editor/plugins/multimesh_editor_plugin.cpp2
-rw-r--r--editor/plugins/particles_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/particles_editor_plugin.cpp10
-rw-r--r--editor/plugins/script_editor_plugin.cpp71
-rw-r--r--editor/plugins/script_editor_plugin.h2
-rw-r--r--editor/plugins/script_text_editor.cpp101
-rw-r--r--editor/plugins/script_text_editor.h1
-rw-r--r--editor/plugins/shader_editor_plugin.cpp85
-rw-r--r--editor/plugins/shader_editor_plugin.h8
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp2
-rw-r--r--editor/plugins/text_editor.cpp5
-rw-r--r--editor/plugins/text_editor.h1
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp128
-rw-r--r--editor/plugins/tile_map_editor_plugin.h3
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp1
-rw-r--r--editor/project_manager.cpp27
-rw-r--r--editor/project_manager.h1
-rw-r--r--editor/scene_tree_dock.cpp3
-rw-r--r--editor/scene_tree_editor.cpp37
-rw-r--r--editor/scene_tree_editor.h2
-rw-r--r--editor/script_create_dialog.cpp62
-rw-r--r--editor/script_create_dialog.h3
-rw-r--r--editor/script_editor_debugger.cpp23
-rw-r--r--editor/spatial_editor_gizmos.cpp9
-rw-r--r--main/main.cpp8
-rw-r--r--misc/dist/html/full-size.html9
-rwxr-xr-xmisc/scripts/fix_headers.py8
-rw-r--r--modules/bullet/SCsub6
-rw-r--r--modules/bullet/bullet_physics_server.cpp2
-rw-r--r--modules/bullet/cone_twist_joint_bullet.cpp2
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.cpp4
-rw-r--r--modules/bullet/hinge_joint_bullet.cpp2
-rw-r--r--modules/bullet/pin_joint_bullet.cpp2
-rw-r--r--modules/csg/csg.cpp2
-rw-r--r--modules/csg/csg_shape.h2
-rw-r--r--modules/dds/texture_loader_dds.cpp25
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp5
-rw-r--r--modules/gdnative/arvr/arvr_interface_gdnative.cpp11
-rw-r--r--modules/gdnative/arvr/arvr_interface_gdnative.h1
-rw-r--r--modules/gdnative/gdnative/vector2.cpp8
-rw-r--r--modules/gdnative/gdnative/vector3.cpp8
-rw-r--r--modules/gdnative/gdnative_api.json18
-rw-r--r--modules/gdnative/include/arvr/godot_arvr.h1
-rw-r--r--modules/gdnative/include/gdnative/vector2.h2
-rw-r--r--modules/gdnative/include/gdnative/vector3.h2
-rw-r--r--modules/gdnative/include/pluginscript/godot_pluginscript.h2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.h2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp12
-rw-r--r--modules/gdscript/gdscript.h4
-rw-r--r--modules/gdscript/gdscript_compiler.cpp8
-rw-r--r--modules/gdscript/gdscript_editor.cpp14
-rw-r--r--modules/gdscript/gdscript_functions.cpp16
-rw-r--r--modules/gdscript/gdscript_functions.h1
-rw-r--r--modules/gdscript/gdscript_parser.cpp53
-rw-r--r--modules/gridmap/grid_map.cpp4
-rw-r--r--modules/mono/SCsub5
-rw-r--r--modules/mono/build_scripts/godotsharptools_build.py35
-rw-r--r--modules/mono/build_scripts/mono_configure.py133
-rw-r--r--modules/mono/build_scripts/patches/fix-mono-android-tkill.diff70
-rw-r--r--modules/mono/config.py10
-rw-r--r--modules/mono/csharp_script.h1
-rw-r--r--modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs3
-rw-r--r--modules/mono/editor/bindings_generator.cpp9
-rw-r--r--modules/mono/editor/godotsharp_export.cpp22
-rw-r--r--modules/mono/glue/Managed/Files/Mathf.cs5
-rw-r--r--modules/mono/glue/Managed/Files/Vector2.cs8
-rw-r--r--modules/mono/glue/Managed/Files/Vector3.cs8
-rw-r--r--modules/mono/glue/base_object_glue.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp13
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp14
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp26
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h18
-rw-r--r--modules/mono/signal_awaiter_utils.cpp2
-rw-r--r--modules/opus/audio_stream_opus.cpp2
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.cpp4
-rw-r--r--modules/recast/navigation_mesh_generator.cpp18
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml75
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp20
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h1
-rw-r--r--modules/visual_script/visual_script_editor.cpp3
-rw-r--r--modules/visual_script/visual_script_editor.h1
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp3
-rw-r--r--modules/webm/video_stream_webm.cpp7
-rw-r--r--modules/webrtc/config.py3
-rw-r--r--modules/webrtc/doc_classes/WebRTCDataChannel.xml21
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayer.xml85
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml61
-rw-r--r--modules/webrtc/register_types.cpp2
-rw-r--r--modules/webrtc/webrtc_data_channel_js.cpp35
-rw-r--r--modules/webrtc/webrtc_multiplayer.cpp384
-rw-r--r--modules/webrtc/webrtc_multiplayer.h116
-rw-r--r--platform/android/export/export.cpp2
-rw-r--r--platform/android/os_android.cpp6
-rw-r--r--platform/android/os_android.h3
-rw-r--r--platform/haiku/os_haiku.cpp4
-rw-r--r--platform/haiku/os_haiku.h2
-rw-r--r--platform/iphone/SCsub1
-rw-r--r--platform/iphone/camera_ios.h47
-rw-r--r--platform/iphone/camera_ios.mm429
-rw-r--r--platform/iphone/detect.py1
-rw-r--r--platform/iphone/export/export.cpp2
-rw-r--r--platform/iphone/icloud.mm2
-rw-r--r--platform/iphone/os_iphone.cpp7
-rw-r--r--platform/iphone/os_iphone.h3
-rw-r--r--platform/javascript/audio_driver_javascript.cpp2
-rw-r--r--platform/javascript/os_javascript.cpp73
-rw-r--r--platform/javascript/os_javascript.h6
-rw-r--r--platform/osx/SCsub1
-rw-r--r--platform/osx/camera_osx.h47
-rw-r--r--platform/osx/camera_osx.mm362
-rw-r--r--platform/osx/detect.py2
-rw-r--r--platform/osx/os_osx.h4
-rw-r--r--platform/osx/os_osx.mm143
-rw-r--r--platform/server/detect.py6
-rw-r--r--platform/uwp/export/export.cpp4
-rw-r--r--platform/uwp/os_uwp.cpp6
-rw-r--r--platform/uwp/os_uwp.h3
-rw-r--r--platform/windows/SCsub1
-rw-r--r--platform/windows/camera_win.cpp94
-rw-r--r--platform/windows/camera_win.h46
-rw-r--r--platform/windows/export/export.cpp4
-rw-r--r--platform/windows/godot_res.rc2
-rw-r--r--platform/windows/joypad_windows.cpp18
-rw-r--r--platform/windows/os_windows.cpp30
-rw-r--r--platform/windows/os_windows.h2
-rw-r--r--platform/windows/power_windows.cpp2
-rw-r--r--platform/windows/windows_terminal_logger.cpp4
-rw-r--r--platform/x11/detect.py6
-rw-r--r--platform/x11/joypad_linux.cpp6
-rw-r--r--platform/x11/os_x11.cpp36
-rw-r--r--platform/x11/os_x11.h3
-rw-r--r--scene/2d/animated_sprite.cpp1
-rw-r--r--scene/2d/camera_2d.cpp10
-rw-r--r--scene/2d/cpu_particles_2d.cpp72
-rw-r--r--scene/2d/cpu_particles_2d.h4
-rw-r--r--scene/2d/line_2d.cpp1
-rw-r--r--scene/2d/mesh_instance_2d.cpp2
-rw-r--r--scene/2d/multimesh_instance_2d.cpp111
-rw-r--r--scene/2d/multimesh_instance_2d.h65
-rw-r--r--scene/2d/physics_body_2d.cpp32
-rw-r--r--scene/2d/position_2d.cpp36
-rw-r--r--scene/2d/position_2d.h5
-rw-r--r--scene/2d/tile_map.cpp16
-rw-r--r--scene/3d/arvr_nodes.cpp4
-rw-r--r--scene/3d/camera.cpp7
-rw-r--r--scene/3d/camera.h2
-rw-r--r--scene/3d/collision_shape.cpp4
-rw-r--r--scene/3d/cpu_particles.cpp62
-rw-r--r--scene/3d/cpu_particles.h2
-rw-r--r--scene/3d/physics_body.cpp32
-rw-r--r--scene/3d/visual_instance.h1
-rw-r--r--scene/animation/animation_blend_tree.cpp2
-rw-r--r--scene/animation/animation_tree.cpp2
-rw-r--r--scene/animation/animation_tree_player.cpp2
-rw-r--r--scene/animation/tween.cpp595
-rw-r--r--scene/animation/tween.h1
-rw-r--r--scene/gui/base_button.cpp12
-rw-r--r--scene/gui/control.cpp10
-rw-r--r--scene/gui/file_dialog.cpp23
-rw-r--r--scene/gui/file_dialog.h1
-rw-r--r--scene/gui/gradient_edit.cpp9
-rw-r--r--scene/gui/gradient_edit.h2
-rw-r--r--scene/gui/graph_node.cpp4
-rw-r--r--scene/gui/item_list.cpp20
-rw-r--r--scene/gui/line_edit.cpp7
-rw-r--r--scene/gui/option_button.cpp1
-rw-r--r--scene/gui/popup_menu.cpp56
-rw-r--r--scene/gui/popup_menu.h7
-rw-r--r--scene/gui/rich_text_label.cpp19
-rw-r--r--scene/gui/text_edit.cpp10
-rw-r--r--scene/gui/text_edit.h4
-rw-r--r--scene/gui/tree.cpp61
-rw-r--r--scene/gui/tree.h4
-rw-r--r--scene/main/node.cpp4
-rw-r--r--scene/main/scene_tree.cpp2
-rw-r--r--scene/register_scene_types.cpp17
-rw-r--r--scene/resources/animation.cpp75
-rw-r--r--scene/resources/animation.h1
-rw-r--r--scene/resources/default_theme/default_theme.cpp15
-rw-r--r--scene/resources/default_theme/icon_visibility.pngbin0 -> 488 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h4
-rw-r--r--scene/resources/environment.cpp22
-rw-r--r--scene/resources/environment.h4
-rw-r--r--scene/resources/height_map_shape.cpp1
-rw-r--r--scene/resources/mesh.cpp2
-rw-r--r--scene/resources/mesh.h5
-rw-r--r--scene/resources/packed_scene.cpp4
-rw-r--r--scene/resources/particles_material.cpp1
-rw-r--r--scene/resources/primitive_meshes.cpp6
-rw-r--r--scene/resources/primitive_meshes.h1
-rw-r--r--scene/resources/resource_format_text.cpp6
-rw-r--r--scene/resources/texture.cpp107
-rw-r--r--scene/resources/texture.h35
-rw-r--r--scene/resources/tile_set.cpp32
-rw-r--r--scene/resources/tile_set.h1
-rw-r--r--scene/resources/visual_shader.cpp2
-rw-r--r--servers/SCsub1
-rw-r--r--servers/arvr/arvr_interface.cpp7
-rw-r--r--servers/arvr/arvr_interface.h1
-rw-r--r--servers/audio/effects/audio_effect_spectrum_analyzer.cpp30
-rw-r--r--servers/audio/effects/audio_effect_spectrum_analyzer.h30
-rw-r--r--servers/audio/effects/audio_stream_generator.cpp30
-rw-r--r--servers/audio/effects/audio_stream_generator.h36
-rw-r--r--servers/camera/SCsub7
-rw-r--r--servers/camera/camera_feed.cpp266
-rw-r--r--servers/camera/camera_feed.h115
-rw-r--r--servers/camera_server.cpp169
-rw-r--r--servers/camera_server.h96
-rw-r--r--servers/physics_2d/joints_2d_sw.cpp2
-rw-r--r--servers/register_server_types.cpp6
-rw-r--r--servers/visual/rasterizer.h2
-rw-r--r--servers/visual/shader_language.cpp101
-rw-r--r--servers/visual/shader_language.h10
-rw-r--r--servers/visual/visual_server_raster.h2
-rw-r--r--servers/visual/visual_server_wrap_mt.h2
-rw-r--r--servers/visual_server.cpp1
-rw-r--r--servers/visual_server.h3
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/bullet/Bullet3Common/b3Quaternion.h5
-rw-r--r--thirdparty/bullet/Bullet3Common/b3Vector3.h2
-rw-r--r--thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvt.cpp5
-rw-r--r--thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp4
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp1
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h12
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp14
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp6
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp206
-rw-r--r--thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h27
-rw-r--r--thirdparty/bullet/BulletCollision/Gimpact/btGImpactBvhStructs.h23
-rw-r--r--thirdparty/bullet/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp16
-rw-r--r--thirdparty/bullet/BulletCollision/Gimpact/btGImpactShape.h2
-rw-r--r--thirdparty/bullet/BulletCollision/Gimpact/gim_box_set.h23
-rw-r--r--thirdparty/bullet/BulletCollision/Gimpact/gim_pair.h28
-rw-r--r--thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btConvexCast.h12
-rw-r--r--thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp2
-rw-r--r--thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h1
-rw-r--r--thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp4
-rw-r--r--thirdparty/bullet/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp5
-rw-r--r--thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h1
-rw-r--r--thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h2
-rw-r--r--thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp13
-rw-r--r--thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h2
-rw-r--r--thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp877
-rw-r--r--thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h164
-rw-r--r--thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp4
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp15
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h45
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp86
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h5
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp37
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h2
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h4
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp12
-rw-r--r--thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h2
-rw-r--r--thirdparty/bullet/BulletDynamics/MLCPSolvers/btLemkeSolver.h6
-rw-r--r--thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp2
-rw-r--r--thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp33
-rw-r--r--thirdparty/bullet/BulletSoftBody/btSoftBody.cpp4
-rw-r--r--thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportPosix.cpp6
-rw-r--r--thirdparty/bullet/LinearMath/btAlignedObjectArray.h15
-rw-r--r--thirdparty/bullet/LinearMath/btMatrixX.h7
-rw-r--r--thirdparty/bullet/LinearMath/btScalar.h4
-rw-r--r--thirdparty/bullet/LinearMath/btVector3.h2
-rw-r--r--thirdparty/bullet/btBulletCollisionAll.cpp96
-rw-r--r--thirdparty/bullet/btBulletDynamicsAll.cpp42
-rw-r--r--thirdparty/bullet/btLinearMathAll.cpp14
447 files changed, 9116 insertions, 2366 deletions
diff --git a/.travis.yml b/.travis.yml
index b8e02a5be8..587e57c741 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,7 +17,8 @@ cache:
matrix:
include:
- - env: STATIC_CHECKS=yes
+ - name: Static checks (clang-format)
+ env: STATIC_CHECKS=yes
os: linux
compiler: gcc
addons:
@@ -27,7 +28,8 @@ matrix:
packages:
- clang-format-8
- - env: PLATFORM=x11 TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-mono-gcc-8 MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" EXTRA_ARGS="module_mono_enabled=yes mono_glue=no warnings=extra werror=yes"
+ - name: Linux editor (debug, GCC 8, with Mono)
+ env: PLATFORM=x11 TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-mono-gcc-8 MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" EXTRA_ARGS="module_mono_enabled=yes mono_glue=no warnings=extra werror=yes"
os: linux
compiler: gcc-8
addons:
@@ -49,7 +51,8 @@ matrix:
build_command: "scons p=x11 -j2 $OPTIONS"
branch_pattern: coverity_scan
- - env: PLATFORM=x11 TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes"
+ - name: Linux export template (release, Clang)
+ env: PLATFORM=x11 TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes"
os: linux
compiler: clang
addons:
@@ -57,19 +60,23 @@ matrix:
packages:
- *linux_deps
- - env: PLATFORM=android TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes"
+ - name: Android export template (release_debug, Clang)
+ env: PLATFORM=android TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes"
os: linux
compiler: clang
- - env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang
+ - name: macOS editor (debug, Clang)
+ env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang
os: osx
compiler: clang
- - env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang
+ - name: iOS export template (debug, Clang)
+ env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang
os: osx
compiler: clang
- - env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc-8 MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" EXTRA_ARGS="warnings=extra werror=yes"
+ - name: Linux headless editor (release_debug, GCC 8)
+ env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc-8 MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" EXTRA_ARGS="warnings=extra werror=yes"
os: linux
compiler: gcc-8
addons:
@@ -80,7 +87,8 @@ matrix:
- *gcc8_deps
- *linux_deps
- - env: PLATFORM=x11 TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-gcc-5
+ - name: Linux export template (release_debug, GCC 5, without 3D support)
+ env: PLATFORM=x11 TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-gcc-5 EXTRA_ARGS="disable_3d=yes"
os: linux
compiler: gcc
addons:
diff --git a/DONORS.md b/DONORS.md
index 95c46c139b..30eb5c220f 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -17,25 +17,24 @@ generous deed immortalized in the next stable release of Godot Engine.
## Gold sponsors
Gamblify <https://www.gamblify.com>
- Image Campus <https://www.imagecampus.edu.ar>
## Mini sponsors
Alan Beauchamp
+ Aleksandar Kordic
Anandarup Mallik
Andrew Dunai
Brandon Lamb
+ Christian Chipont
Christian Uldall Pedersen
Christoph Woinke
- Connor Hill
Denis Malyavin
Edward Flick
GameDev.net
GameDev.tv
Hein-Pieter van Braam
Jacob McKenney
- Javary Games
- Jay Sistar
+ Javary Co.
Justin Arnold
Kyle Szklenski
Leonard Meagher
@@ -44,15 +43,13 @@ generous deed immortalized in the next stable release of Godot Engine.
Mike King
Neal Gompa (Conan Kudo)
Patrick Aarstad
- "Rainway "
Slobodan Milnovic
- StarFlare Software
Stephan Lanfermann
Stephen Telford
Steve
- TigerJ
VilliHaukka
Xananax
+ Y8.com
Zashi
## Gold donors
@@ -77,12 +74,14 @@ generous deed immortalized in the next stable release of Godot Engine.
Andreas Schüle
Asher Glick
Austen McRae
+ Brian van der Stel
+ Carlo Cabanilla
Daniel James
David Giardi
- David Graham
Edward E
Florian Breisch
Gero
+ Javier Roman
Jay Horton
Jon Smith
Jon Woodward
@@ -107,12 +106,12 @@ generous deed immortalized in the next stable release of Godot Engine.
William Wold
Wyatt Goodin
+ Alex Khayrullin
Chris Goddard
Chris Serino
Christian Padilla
Conrad Curry
Craig Smith
- Daniel Egger
Dean Harmon
Ian Richard Kunert
Ivan Trombley
@@ -135,17 +134,18 @@ generous deed immortalized in the next stable release of Godot Engine.
Wojciech Chojnacki
Xavier PATRICELLI
+ Adam Neumann
Alessandra Pereyra
+ Alexander J Maynard
Alexey Dyadchenko
Andrew Bowen
Asdf
- Benjamin W Flint
+ Ben Botwin
Carlos de Sousa Marques
Chris Petrich
Christian Leth Jeppesen
Christoph Schröder
Cody Parker
- ComicSads
D
Daniel
Daniel Eichler
@@ -163,6 +163,8 @@ generous deed immortalized in the next stable release of Godot Engine.
Guilherme Felipe de C. G. da Silva
Heath Hayes
Hysteria
+ Idzard Kwadijk
+ Jared White
Jose Malheiro
Joshua Flores
Juan T Chen
@@ -173,7 +175,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Leandro Voltolino
Maarten Elings
Malcolm Peralty
- Marius Kamm
Markus Fehr
Markus Wiesner
Martin Eigel
@@ -184,6 +185,7 @@ generous deed immortalized in the next stable release of Godot Engine.
M H
Nick Nikitin
Oliver Dick
+ Paolo Munoz
Paul Hocker
Paul Von Zimmerman
Pete Goodwin
@@ -197,7 +199,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Urho
WytRabbit
Xavier Fumado Beltran
- yuanzhe zhou
## Silver donors
@@ -208,6 +209,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Adam Nakonieczny
Adam Smeltzer
Adisibio
+ Agustinus Arya
Aidan O'Flannagain
Alder Stefano
Alessandro Senese
@@ -231,21 +233,23 @@ generous deed immortalized in the next stable release of Godot Engine.
Benedikt
Ben Phelan
Ben Vercammen
+ Ben Woodley
+ Berbank
Bernd Jänichen
Black Block
Blair Allen
Bobby CC Wong
Boyquotes
- Branwen Zak
+ Branwyn Tylwyth
Bryan Stevenson
- Carl Winder
Carwyn Edwards
Chris Brown
Chris Chapin
+ Chris Gonzales
Christian Baune
Christian Winter
Christoffer Sundbom
- Christopher Fisher
+ Christopher Schmitt
Chris Wilson
Clay Heaton
Cobaltum
@@ -262,7 +266,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Dominik Wetzel
Duobix
Edward Herbert
- E.G.
Egon Elbre
Ellen Marie Dash
Elmeri '- Duy Kevin Nguyen
@@ -271,30 +274,33 @@ generous deed immortalized in the next stable release of Godot Engine.
Eric Martini
Eric McCarthy
Eric Williams
- Fabian Lökes
+ Evan Rose
Felix Kollmann
fengjiongmax
- Foomf
+ Flaredown
G3Dev sàrl
Gary Hulst
Gerrit Großkopf
gmmath
Grant Clarke
Greg Olson
- Greg Pennefather
+ Greg P
Guldoman
Heribert Hirth
Hiroshi Naruo
HMan
Hunter Jones
+ Hylpher
ialex32x
Igor Buzatovic
Iiari
+ IndustrialRobot
Isaac Morton
Jaime Ruiz-Borau Vizárraga
Jako Danar
James A F Manley
Jax
+ Jed
Jeff Hungerford
Jeff Nyte
Jeremy Kahn
@@ -302,6 +308,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Joe Alden
Joel Fivat
Joel Setterberg
+ Johannes Eichler
Johannes Wuensch
Jonas Rudlang
Jonas Yamazaki
@@ -316,6 +323,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Juan Negrier
Judd
Julian Murgia
+ Kasier Bald0
KC Chan
kickmaniac
Kiyohiro Kawamura (kyorohiro)
@@ -324,22 +332,24 @@ generous deed immortalized in the next stable release of Godot Engine.
KsyTek Games
Kuan Cheang
kycho
+ Lavik1988
Levi Lindsey
Linus Lind Lundgren
Lionel Gaillard
Luis Moraes
+ LunaticInAHat
+ Lurkars
Macil
- magodev
Major Haul
Malcolm
Malik Ahmed
+ Malik Nejer
+ Marcus Richter
Markus Michael Egger
Martin Holas
Matthew Little
Maxwell
medecau
- Menno Finlay-Smits
- Mertcan Mermerkaya
mhilbrunner
Michael Dürwald
Michael Gringauz
@@ -347,9 +357,9 @@ generous deed immortalized in the next stable release of Godot Engine.
Mikael Olsson
Mikayla Hutchinson
Mike Cunningham
+ mlevin cantu
MoM
Moritz Laass
- Moritz Weissenberger
MuffinManKen
Natrim
nee
@@ -360,6 +370,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Nicolas SAN AGUSTIN
Nithin Jino
NZ
+ Omar Delarosa
Oscar Norlander
Pan Ip
Patrick Forringer
@@ -370,30 +381,30 @@ generous deed immortalized in the next stable release of Godot Engine.
Philip O. Staiger
Pierre-Igor Berthet
Pietro Vertechi
- Piotr Kaczmarski
Pitsanu Tongprasin
Poryg
+ Rafa Laguna
+ Rafal Wyszomirski
Raphael Leroux
- Red Hara
Rémi Verschelde
Ricardo Alcantara
- Rob Crowle
Robert Farr (Larington)
Robert Hernandez
Rodrigo Loli
- Roger Burgess
Roger Smith
Roland RzÄ…sa
Roman Tinkov
Ryan Groom
Ryan Hentz
Saad Khoudmi
- Samdze
+ Samuele Zolfanelli
+ Sanka.X
Sasori Olkof
Scott D. Yelich
Sebastian Michailidis
Shane Sicienski
Shane Spoor
+ Simon Ledam
Simon Wenner
SK
Sootstone
@@ -408,13 +419,18 @@ generous deed immortalized in the next stable release of Godot Engine.
Tim Drumheller
Tim Gudex
Timo Schmidt
+ Timothy B. MacDonald
+ Tobbun
Tom Larrow
Torsten Crass
Travis O'Brien
+ Trent Skinner
Tryggve Sollid
- Tyler Strafos
+ Turgut Temucin
+ Tyler Stafos
UltyX
Vaiktorg
+ Valeria Viana Gusmao
Veodok
Victor
Vigilant Watch
diff --git a/SConstruct b/SConstruct
index 17cf779d4a..ce2566559c 100644
--- a/SConstruct
+++ b/SConstruct
@@ -125,7 +125,7 @@ opts.Add(BoolVariable('verbose', "Enable verbose output for the compilation", Fa
opts.Add(BoolVariable('progress', "Show a progress indicator during compilation", True))
opts.Add(EnumVariable('warnings', "Set the level of warnings emitted during compilation", 'all', ('extra', 'all', 'moderate', 'no')))
opts.Add(BoolVariable('werror', "Treat compiler warnings as errors. Depends on the level of warnings set with 'warnings'", False))
-opts.Add(BoolVariable('dev', "If yes, alias for verbose=yes warnings=all", False))
+opts.Add(BoolVariable('dev', "If yes, alias for verbose=yes warnings=extra werror=yes", False))
opts.Add('extra_suffix', "Custom extra suffix added to the base filename of all generated binary files", '')
opts.Add(BoolVariable('vsproj', "Generate a Visual Studio solution", False))
opts.Add(EnumVariable('macports_clang', "Build using Clang from MacPorts", 'no', ('no', '5.0', 'devel')))
@@ -254,8 +254,9 @@ if selected_platform in platform_list:
env = env_base.Clone()
if env['dev']:
- env["warnings"] = "all"
env['verbose'] = True
+ env['warnings'] = "extra"
+ env['werror'] = True
if env['vsproj']:
env.vs_incs = []
@@ -337,7 +338,6 @@ if selected_platform in platform_list:
if (env["warnings"] == 'extra'):
# FIXME: enable -Wclobbered once #26351 is fixed
- # FIXME: enable -Wduplicated-branches once #27594 is merged
# Note: enable -Wimplicit-fallthrough for Clang (already part of -Wextra for GCC)
# once we switch to C++11 or later (necessary for our FALLTHROUGH macro).
env.Append(CCFLAGS=['-Wall', '-Wextra', '-Wno-unused-parameter']
@@ -345,7 +345,8 @@ if selected_platform in platform_list:
env.Append(CXXFLAGS=['-Wctor-dtor-privacy', '-Wnon-virtual-dtor'])
if methods.using_gcc(env):
env.Append(CCFLAGS=['-Wno-clobbered', '-Walloc-zero',
- '-Wduplicated-cond', '-Wstringop-overflow=4', '-Wlogical-op'])
+ '-Wduplicated-branches', '-Wduplicated-cond',
+ '-Wstringop-overflow=4', '-Wlogical-op'])
env.Append(CXXFLAGS=['-Wnoexcept', '-Wplacement-new=1'])
version = methods.get_compiler_version(env)
if version != None and version[0] >= '9':
diff --git a/core/color.cpp b/core/color.cpp
index efd2941b47..8959fce4e3 100644
--- a/core/color.cpp
+++ b/core/color.cpp
@@ -525,7 +525,7 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const {
float Color::gray() const {
ERR_EXPLAIN("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation.");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
return (r + g + b) / 3.0;
}
diff --git a/core/engine.cpp b/core/engine.cpp
index 9607dedb3c..50822244cf 100644
--- a/core/engine.cpp
+++ b/core/engine.cpp
@@ -38,6 +38,7 @@
void Engine::set_iterations_per_second(int p_ips) {
+ ERR_FAIL_COND(p_ips <= 0);
ips = p_ips;
}
int Engine::get_iterations_per_second() const {
diff --git a/core/global_constants.cpp b/core/global_constants.cpp
index fb90403226..671b3c545b 100644
--- a/core/global_constants.cpp
+++ b/core/global_constants.cpp
@@ -425,6 +425,16 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(JOY_DS_X);
BIND_GLOBAL_ENUM_CONSTANT(JOY_DS_Y);
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_GRIP);
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_PAD);
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_TRIGGER);
+
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_AX);
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_BY);
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_OCULUS_MENU);
+
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_MENU);
+
BIND_GLOBAL_ENUM_CONSTANT(JOY_SELECT);
BIND_GLOBAL_ENUM_CONSTANT(JOY_START);
BIND_GLOBAL_ENUM_CONSTANT(JOY_DPAD_UP);
@@ -459,6 +469,12 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2);
BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2);
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_ANALOG_TRIGGER);
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_VR_ANALOG_GRIP);
+
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_TOUCHPADX);
+ BIND_GLOBAL_ENUM_CONSTANT(JOY_OPENVR_TOUCHPADY);
+
// midi
BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF);
BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON);
diff --git a/core/hash_map.h b/core/hash_map.h
index 44459a3080..31332991de 100644
--- a/core/hash_map.h
+++ b/core/hash_map.h
@@ -162,20 +162,21 @@ private:
new_hash_table[i] = 0;
}
- for (int i = 0; i < (1 << hash_table_power); i++) {
+ if (hash_table) {
+ for (int i = 0; i < (1 << hash_table_power); i++) {
- while (hash_table[i]) {
+ while (hash_table[i]) {
- Element *se = hash_table[i];
- hash_table[i] = se->next;
- int new_pos = se->hash & ((1 << new_hash_table_power) - 1);
- se->next = new_hash_table[new_pos];
- new_hash_table[new_pos] = se;
+ Element *se = hash_table[i];
+ hash_table[i] = se->next;
+ int new_pos = se->hash & ((1 << new_hash_table_power) - 1);
+ se->next = new_hash_table[new_pos];
+ new_hash_table[new_pos] = se;
+ }
}
- }
- if (hash_table)
memdelete_arr(hash_table);
+ }
hash_table = new_hash_table;
hash_table_power = new_hash_table_power;
}
diff --git a/core/image.cpp b/core/image.cpp
index 30af724de9..c85d7f6bcc 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -749,7 +749,7 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict
float scale_factor = MAX(x_scale, 1); // A larger kernel is required only when downscaling
int32_t half_kernel = LANCZOS_TYPE * scale_factor;
- float *kernel = memnew_arr(float, half_kernel * 2 - 1);
+ float *kernel = memnew_arr(float, half_kernel * 2);
for (int32_t buffer_x = 0; buffer_x < dst_width; buffer_x++) {
@@ -800,7 +800,7 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict
float scale_factor = MAX(y_scale, 1);
int32_t half_kernel = LANCZOS_TYPE * scale_factor;
- float *kernel = memnew_arr(float, half_kernel * 2 - 1);
+ float *kernel = memnew_arr(float, half_kernel * 2);
for (int32_t dst_y = 0; dst_y < dst_height; dst_y++) {
diff --git a/core/input_map.cpp b/core/input_map.cpp
index 15f68f9c2a..012c6a7c4f 100644
--- a/core/input_map.cpp
+++ b/core/input_map.cpp
@@ -202,7 +202,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str
if (p_pressed != NULL)
*p_pressed = input_event_action->is_pressed();
if (p_strength != NULL)
- *p_strength = (*p_pressed) ? 1.0f : 0.0f;
+ *p_strength = (*p_pressed) ? input_event_action->get_strength() : 0.0f;
return input_event_action->get_action() == p_action;
}
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index 414742deeb..871e21df3e 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -198,10 +198,6 @@ Error ConfigFile::load(const String &p_path) {
section = next_tag.name;
}
}
-
- memdelete(f);
-
- return OK;
}
void ConfigFile::_bind_methods() {
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index 3ff9fa569c..7dea749a43 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -308,8 +308,8 @@ uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) {
}
Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
-
- return FAILED;
+ ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet");
+ return ERR_UNAVAILABLE;
}
FileAccessEncrypted::FileAccessEncrypted() {
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 2572602e16..5dd167c581 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -501,13 +501,13 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) {
}
uint32_t FileAccessNetwork::_get_unix_permissions(const String &p_file) {
- //could be implemented, not sure if worth it
+ ERR_PRINT("Getting UNIX permissions from network drives is not implemented yet");
return 0;
}
Error FileAccessNetwork::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
-
- return FAILED;
+ ERR_PRINT("Setting UNIX permissions on network drives is not implemented yet");
+ return ERR_UNAVAILABLE;
}
void FileAccessNetwork::configure() {
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index ce2054db36..891fb7b0ca 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -346,6 +346,12 @@ Error HTTPClient::poll() {
} else {
// We are already handshaking, which means we can use your already active SSL connection
ssl = static_cast<Ref<StreamPeerSSL> >(connection);
+ if (ssl.is_null()) {
+ close();
+ status = STATUS_SSL_HANDSHAKE_ERROR;
+ return ERR_CANT_CONNECT;
+ }
+
ssl->poll(); // Try to finish the handshake
}
diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp
index 4638ddcc09..f55af5a96a 100644
--- a/core/io/xml_parser.cpp
+++ b/core/io/xml_parser.cpp
@@ -348,7 +348,7 @@ uint64_t XMLParser::get_node_offset() const {
Error XMLParser::seek(uint64_t p_pos) {
- ERR_FAIL_COND_V(!data, ERR_FILE_EOF)
+ ERR_FAIL_COND_V(!data, ERR_FILE_EOF);
ERR_FAIL_COND_V(p_pos >= length, ERR_FILE_EOF);
P = data + p_pos;
diff --git a/core/list.h b/core/list.h
index 103a82a31d..d1b528562d 100644
--- a/core/list.h
+++ b/core/list.h
@@ -602,9 +602,6 @@ public:
Element *next = current->next_ptr;
- //disconnect
- current->next_ptr = NULL;
-
if (from != current) {
current->prev_ptr = NULL;
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index 3d71e66f80..0b6e9ae929 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -99,14 +99,22 @@ void AStar::remove_point(int p_id) {
Point *p = points[p_id];
- Map<int, Point *>::Element *PE = points.front();
- while (PE) {
- for (Set<Point *>::Element *E = PE->get()->neighbours.front(); E; E = E->next()) {
- Segment s(p_id, E->get()->id);
- segments.erase(s);
- E->get()->neighbours.erase(p);
- }
- PE = PE->next();
+ for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
+
+ Segment s(p_id, E->get()->id);
+ segments.erase(s);
+
+ E->get()->neighbours.erase(p);
+ E->get()->unlinked_neighbours.erase(p);
+ }
+
+ for (Set<Point *>::Element *E = p->unlinked_neighbours.front(); E; E = E->next()) {
+
+ Segment s(p_id, E->get()->id);
+ segments.erase(s);
+
+ E->get()->neighbours.erase(p);
+ E->get()->unlinked_neighbours.erase(p);
}
memdelete(p);
@@ -125,6 +133,8 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
if (bidirectional)
b->neighbours.insert(a);
+ else
+ b->unlinked_neighbours.insert(a);
Segment s(p_id, p_with_id);
if (s.from == p_id) {
@@ -147,7 +157,9 @@ void AStar::disconnect_points(int p_id, int p_with_id) {
Point *a = points[p_id];
Point *b = points[p_with_id];
a->neighbours.erase(b);
+ a->unlinked_neighbours.erase(b);
b->neighbours.erase(a);
+ b->unlinked_neighbours.erase(a);
}
bool AStar::has_point(int p_id) const {
diff --git a/core/math/a_star.h b/core/math/a_star.h
index fac8a9d312..ba35d929b3 100644
--- a/core/math/a_star.h
+++ b/core/math/a_star.h
@@ -54,6 +54,7 @@ class AStar : public Reference {
bool enabled;
Set<Point *> neighbours;
+ Set<Point *> unlinked_neighbours;
// Used for pathfinding
Point *prev_point;
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 079c9b524f..e484e9194d 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -70,6 +70,7 @@ const char *Expression::func_name[Expression::FUNC_MAX] = {
"inverse_lerp",
"range_lerp",
"smoothstep",
+ "move_toward",
"dectime",
"randomize",
"randi",
@@ -189,6 +190,7 @@ int Expression::get_func_argument_count(BuiltinFunc p_func) {
case MATH_LERP:
case MATH_INVERSE_LERP:
case MATH_SMOOTHSTEP:
+ case MATH_MOVE_TOWARD:
case MATH_DECTIME:
case MATH_WRAP:
case MATH_WRAPF:
@@ -407,6 +409,13 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant
VALIDATE_ARG_NUM(2);
*r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
} break;
+ case MATH_MOVE_TOWARD: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *r_return = Math::move_toward((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
+ } break;
case MATH_DECTIME: {
VALIDATE_ARG_NUM(0);
diff --git a/core/math/expression.h b/core/math/expression.h
index f20619f0b6..79f6f3989d 100644
--- a/core/math/expression.h
+++ b/core/math/expression.h
@@ -68,6 +68,7 @@ public:
MATH_INVERSE_LERP,
MATH_RANGE_LERP,
MATH_SMOOTHSTEP,
+ MATH_MOVE_TOWARD,
MATH_DECTIME,
MATH_RANDOMIZE,
MATH_RAND,
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 82b5b56c01..0e3bd8a318 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -224,6 +224,8 @@ public:
float x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0f, 1.0f);
return x * x * (3.0f - 2.0f * x);
}
+ static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; }
+ static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; }
static _ALWAYS_INLINE_ double linear2db(double p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; }
static _ALWAYS_INLINE_ float linear2db(float p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; }
diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h
index 6b6bcdd2cd..a6182a4b33 100644
--- a/core/math/random_number_generator.h
+++ b/core/math/random_number_generator.h
@@ -59,7 +59,10 @@ public:
_FORCE_INLINE_ int randi_range(int from, int to) {
unsigned int ret = randbase.rand();
- return ret % (to - from + 1) + from;
+ if (to < from)
+ return ret % (from - to + 1) + to;
+ else
+ return ret % (to - from + 1) + from;
}
RandomNumberGenerator();
diff --git a/core/math/random_pcg.cpp b/core/math/random_pcg.cpp
index 8351bd138e..00c0af515d 100644
--- a/core/math/random_pcg.cpp
+++ b/core/math/random_pcg.cpp
@@ -43,13 +43,9 @@ void RandomPCG::randomize() {
}
double RandomPCG::random(double p_from, double p_to) {
- unsigned int r = rand();
- double ret = (double)r / (double)RANDOM_MAX;
- return (ret) * (p_to - p_from) + p_from;
+ return randd() * (p_to - p_from) + p_from;
}
float RandomPCG::random(float p_from, float p_to) {
- unsigned int r = rand();
- float ret = (float)r / (float)RANDOM_MAX;
- return (ret) * (p_to - p_from) + p_from;
+ return randf() * (p_to - p_from) + p_from;
}
diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h
index 0d1b311c0d..aa25914638 100644
--- a/core/math/random_pcg.h
+++ b/core/math/random_pcg.h
@@ -37,6 +37,28 @@
#include "thirdparty/misc/pcg.h"
+#if defined(__GNUC__) || (_llvm_has_builtin(__builtin_clz))
+#define CLZ32(x) __builtin_clz(x)
+#elif defined(_MSC_VER)
+#include "intrin.h"
+static int __bsr_clz32(uint32_t x) {
+ unsigned long index;
+ _BitScanReverse(&index, x);
+ return 31 - index;
+}
+#define CLZ32(x) __bsr_clz32(x)
+#else
+#endif
+
+#if defined(__GNUC__) || (_llvm_has_builtin(__builtin_ldexp) && _llvm_has_builtin(__builtin_ldexpf))
+#define LDEXP(s, e) __builtin_ldexp(s, e)
+#define LDEXPF(s, e) __builtin_ldexpf(s, e)
+#else
+#include "math.h"
+#define LDEXP(s, e) ldexp(s, e)
+#define LDEXPF(s, e) ldexp(s, e)
+#endif
+
class RandomPCG {
pcg32_random_t pcg;
uint64_t current_seed; // seed with this to get the same state
@@ -60,8 +82,44 @@ public:
current_seed = pcg.state;
return pcg32_random_r(&pcg);
}
- _FORCE_INLINE_ double randd() { return (double)rand() / (double)RANDOM_MAX; }
- _FORCE_INLINE_ float randf() { return (float)rand() / (float)RANDOM_MAX; }
+
+ // Obtaining floating point numbers in [0, 1] range with "good enough" uniformity.
+ // These functions sample the output of rand() as the fraction part of an infinite binary number,
+ // with some tricks applied to reduce ops and branching:
+ // 1. Instead of shifting to the first 1 and connecting random bits, we simply set the MSB and LSB to 1.
+ // Provided that the RNG is actually uniform bit by bit, this should have the exact same effect.
+ // 2. In order to compensate for exponent info loss, we count zeros from another random number,
+ // and just add that to the initial offset.
+ // This has the same probability as counting and shifting an actual bit stream: 2^-n for n zeroes.
+ // For all numbers above 2^-96 (2^-64 for floats), the functions should be uniform.
+ // However, all numbers below that threshold are floored to 0.
+ // The thresholds are chosen to minimize rand() calls while keeping the numbers within a totally subjective quality standard.
+ // If clz or ldexp isn't available, fall back to bit truncation for performance, sacrificing uniformity.
+ _FORCE_INLINE_ double randd() {
+#if defined(CLZ32)
+ uint32_t proto_exp_offset = rand();
+ if (unlikely(proto_exp_offset == 0)) {
+ return 0;
+ }
+ uint64_t significand = (((uint64_t)rand()) << 32) | rand() | 0x8000000000000001U;
+ return LDEXP((double)significand, -64 - CLZ32(proto_exp_offset));
+#else
+#pragma message("RandomPCG::randd - intrinsic clz is not available, falling back to bit truncation")
+ return (double)(((((uint64_t)rand()) << 32) | rand()) & 0x1FFFFFFFFFFFFFU) / (double)0x1FFFFFFFFFFFFFU;
+#endif
+ }
+ _FORCE_INLINE_ float randf() {
+#if defined(CLZ32)
+ uint32_t proto_exp_offset = rand();
+ if (unlikely(proto_exp_offset == 0)) {
+ return 0;
+ }
+ return LDEXPF((float)(rand() | 0x80000001), -32 - CLZ32(proto_exp_offset));
+#else
+#pragma message("RandomPCG::randf - intrinsic clz is not available, falling back to bit truncation")
+ return (float)(rand() & 0xFFFFFF) / (float)0xFFFFFF;
+#endif
+ }
_FORCE_INLINE_ double randfn(double p_mean, double p_deviation) {
return p_mean + p_deviation * (cos(Math_TAU * randd()) * sqrt(-2.0 * log(randd()))); // Box-Muller transform
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index 5c1ea5943d..779a28be66 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -164,6 +164,13 @@ Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, c
return out;
}
+Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const {
+ Vector2 v = *this;
+ Vector2 vd = p_to - v;
+ real_t len = vd.length();
+ return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta;
+}
+
// slide returns the component of the vector along the given plane, specified by its normal vector.
Vector2 Vector2::slide(const Vector2 &p_normal) const {
#ifdef MATH_CHECKS
diff --git a/core/math/vector2.h b/core/math/vector2.h
index a0c6024c9f..78a1641c1e 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -79,6 +79,7 @@ struct Vector2 {
_FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const;
_FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const;
Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const;
+ Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const;
Vector2 slide(const Vector2 &p_normal) const;
Vector2 bounce(const Vector2 &p_normal) const;
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index 1c28934422..73927821cf 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -127,6 +127,13 @@ Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, c
return out;
}
+Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const {
+ Vector3 v = *this;
+ Vector3 vd = p_to - v;
+ real_t len = vd.length();
+ return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta;
+}
+
Vector3::operator String() const {
return (rtos(x) + ", " + rtos(y) + ", " + rtos(z));
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 21fc09653f..811a207138 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -94,6 +94,7 @@ struct Vector3 {
_FORCE_INLINE_ Vector3 slerp(const Vector3 &p_b, real_t p_t) const;
Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const;
Vector3 cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_t) const;
+ Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const;
_FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const;
_FORCE_INLINE_ real_t dot(const Vector3 &p_b) const;
@@ -223,7 +224,7 @@ Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const {
#endif
real_t theta = angle_to(p_b);
- return rotated(cross(p_b), theta * p_t);
+ return rotated(cross(p_b).normalized(), theta * p_t);
}
real_t Vector3::distance_to(const Vector3 &p_b) const {
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index a072017353..9c5066da3d 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -1010,6 +1010,14 @@ bool InputEventAction::is_pressed() const {
return pressed;
}
+void InputEventAction::set_strength(float p_strength) {
+ strength = CLAMP(p_strength, 0.0f, 1.0f);
+}
+
+float InputEventAction::get_strength() const {
+ return strength;
+}
+
bool InputEventAction::shortcut_match(const Ref<InputEvent> &p_event) const {
if (p_event.is_null())
return false;
@@ -1051,14 +1059,19 @@ void InputEventAction::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventAction::set_pressed);
//ClassDB::bind_method(D_METHOD("is_pressed"), &InputEventAction::is_pressed);
+ ClassDB::bind_method(D_METHOD("set_strength", "strength"), &InputEventAction::set_strength);
+ ClassDB::bind_method(D_METHOD("get_strength"), &InputEventAction::get_strength);
+
// ClassDB::bind_method(D_METHOD("is_action", "name"), &InputEventAction::is_action);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "action"), "set_action", "get_action");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_strength", "get_strength");
}
InputEventAction::InputEventAction() {
pressed = false;
+ strength = 1.0f;
}
/////////////////////////////
diff --git a/core/os/input_event.h b/core/os/input_event.h
index ba01516519..2eb321f134 100644
--- a/core/os/input_event.h
+++ b/core/os/input_event.h
@@ -117,6 +117,16 @@ enum JoystickList {
JOY_WII_MINUS = JOY_BUTTON_10,
JOY_WII_PLUS = JOY_BUTTON_11,
+ JOY_VR_GRIP = JOY_BUTTON_2,
+ JOY_VR_PAD = JOY_BUTTON_14,
+ JOY_VR_TRIGGER = JOY_BUTTON_15,
+
+ JOY_OCULUS_AX = JOY_BUTTON_7,
+ JOY_OCULUS_BY = JOY_BUTTON_1,
+ JOY_OCULUS_MENU = JOY_BUTTON_3,
+
+ JOY_OPENVR_MENU = JOY_BUTTON_1,
+
// end of history
JOY_AXIS_0 = 0,
@@ -139,6 +149,12 @@ enum JoystickList {
JOY_ANALOG_L2 = JOY_AXIS_6,
JOY_ANALOG_R2 = JOY_AXIS_7,
+
+ JOY_VR_ANALOG_TRIGGER = JOY_AXIS_2,
+ JOY_VR_ANALOG_GRIP = JOY_AXIS_4,
+
+ JOY_OPENVR_TOUCHPADX = JOY_AXIS_0,
+ JOY_OPENVR_TOUCHPADY = JOY_AXIS_1,
};
enum MidiMessageList {
@@ -475,6 +491,7 @@ class InputEventAction : public InputEvent {
StringName action;
bool pressed;
+ float strength;
protected:
static void _bind_methods();
@@ -486,6 +503,9 @@ public:
void set_pressed(bool p_pressed);
virtual bool is_pressed() const;
+ void set_strength(float p_strength);
+ float get_strength() const;
+
virtual bool is_action(const StringName &p_action) const;
virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
diff --git a/core/os/os.h b/core/os/os.h
index 4f6a539e78..b128e6424c 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -104,7 +104,6 @@ public:
bool maximized;
bool always_on_top;
bool use_vsync;
- bool layered_splash;
bool layered;
float get_aspect() const { return (float)width / (float)height; }
VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false) {
@@ -117,7 +116,6 @@ public:
always_on_top = p_always_on_top;
use_vsync = p_use_vsync;
layered = false;
- layered_splash = false;
}
};
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index 4c37142ffd..0508806a35 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -489,7 +489,7 @@ Error ProjectSettings::_load_settings_binary(const String p_path) {
memdelete(f);
ERR_EXPLAIN("Corrupted header in binary project.binary (not ECFG)");
- ERR_FAIL_V(ERR_FILE_CORRUPT;)
+ ERR_FAIL_V(ERR_FILE_CORRUPT);
}
uint32_t count = f->get_32();
@@ -579,10 +579,6 @@ Error ProjectSettings::_load_settings_text(const String p_path) {
section = next_tag.name;
}
}
-
- memdelete(f);
-
- return OK;
}
Error ProjectSettings::_load_settings_text_or_binary(const String p_text_path, const String p_bin_path) {
@@ -640,7 +636,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str
if (err != OK) {
ERR_EXPLAIN("Couldn't save project.binary at " + p_file);
- ERR_FAIL_COND_V(err, err)
+ ERR_FAIL_COND_V(err, err);
}
uint8_t hdr[4] = { 'E', 'C', 'F', 'G' };
@@ -732,7 +728,7 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin
if (err) {
ERR_EXPLAIN("Couldn't save project.godot - " + p_file);
- ERR_FAIL_COND_V(err, err)
+ ERR_FAIL_COND_V(err, err);
}
file->store_line("; Engine configuration file.");
diff --git a/core/script_language.h b/core/script_language.h
index b2dab666c4..b0c60b4e90 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -250,7 +250,7 @@ public:
virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; }
virtual bool overrides_external_editor() { return false; }
- virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; }
+ virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; }
struct LookupResult {
enum Type {
@@ -269,7 +269,7 @@ public:
int location;
};
- virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result) { return ERR_UNAVAILABLE; }
+ virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { return ERR_UNAVAILABLE; }
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const = 0;
virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) = 0;
diff --git a/core/sort_array.h b/core/sort_array.h
index 0f258aec3e..8660ee3333 100644
--- a/core/sort_array.h
+++ b/core/sort_array.h
@@ -179,14 +179,14 @@ public:
while (true) {
while (compare(p_array[p_first], p_pivot)) {
if (Validate) {
- ERR_BAD_COMPARE(p_first == unmodified_last - 1)
+ ERR_BAD_COMPARE(p_first == unmodified_last - 1);
}
p_first++;
}
p_last--;
while (compare(p_pivot, p_array[p_last])) {
if (Validate) {
- ERR_BAD_COMPARE(p_last == unmodified_first)
+ ERR_BAD_COMPARE(p_last == unmodified_first);
}
p_last--;
}
@@ -259,7 +259,7 @@ public:
int next = p_last - 1;
while (compare(p_value, p_array[next])) {
if (Validate) {
- ERR_BAD_COMPARE(next == 0)
+ ERR_BAD_COMPARE(next == 0);
}
p_array[p_last] = p_array[next];
p_last = next;
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 954c39c150..35b817b1d2 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -2956,26 +2956,12 @@ String String::replace(const char *p_key, const char *p_with) const {
String String::replace_first(const String &p_key, const String &p_with) const {
- String new_string;
- int search_from = 0;
- int result = 0;
-
- while ((result = find(p_key, search_from)) >= 0) {
-
- new_string += substr(search_from, result - search_from);
- new_string += p_with;
- search_from = result + p_key.length();
- break;
+ int pos = find(p_key);
+ if (pos >= 0) {
+ return substr(0, pos) + p_with + substr(pos + p_key.length(), length());
}
- if (search_from == 0) {
-
- return *this;
- }
-
- new_string += substr(search_from, length() - search_from);
-
- return new_string;
+ return *this;
}
String String::replacen(const String &p_key, const String &p_with) const {
@@ -3102,29 +3088,16 @@ String String::strip_edges(bool left, bool right) const {
String String::strip_escapes() const {
- int len = length();
- int beg = 0, end = len;
-
+ String new_string;
for (int i = 0; i < length(); i++) {
- if (operator[](i) <= 31)
- beg++;
- else
- break;
- }
-
- for (int i = (int)(length() - 1); i >= 0; i--) {
-
- if (operator[](i) <= 31)
- end--;
- else
- break;
+ // Escape characters on first page of the ASCII table, before 32 (Space).
+ if (operator[](i) < 32)
+ continue;
+ new_string += operator[](i);
}
- if (beg == 0 && end == len)
- return *this;
-
- return substr(beg, end - beg);
+ return new_string;
}
String String::lstrip(const String &p_chars) const {
@@ -3248,7 +3221,7 @@ static int _humanize_digits(int p_num) {
String String::humanize_size(size_t p_size) {
uint64_t _div = 1;
- static const char *prefix[] = { " Bytes", " KB", " MB", " GB", "TB", " PB", "HB", "" };
+ static const char *prefix[] = { " Bytes", " KB", " MB", " GB", " TB", " PB", " EB", "" };
int prefix_idx = 0;
while (p_size > (_div * 1024) && prefix[prefix_idx][0]) {
@@ -3259,7 +3232,7 @@ String String::humanize_size(size_t p_size) {
int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0;
double divisor = prefix_idx > 0 ? _div : 1;
- return String::num(p_size / divisor, digits) + prefix[prefix_idx];
+ return String::num(p_size / divisor).pad_decimals(digits) + prefix[prefix_idx];
}
bool String::is_abs_path() const {
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index f9f73b4e51..b3a4a13b08 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -265,6 +265,7 @@ struct _VariantCall {
VCALL_LOCALMEM1R(String, right);
VCALL_LOCALMEM0R(String, dedent);
VCALL_LOCALMEM2R(String, strip_edges);
+ VCALL_LOCALMEM0R(String, strip_escapes);
VCALL_LOCALMEM1R(String, lstrip);
VCALL_LOCALMEM1R(String, rstrip);
VCALL_LOCALMEM0R(String, get_extension);
@@ -347,6 +348,7 @@ struct _VariantCall {
VCALL_LOCALMEM2R(Vector2, linear_interpolate);
VCALL_LOCALMEM2R(Vector2, slerp);
VCALL_LOCALMEM4R(Vector2, cubic_interpolate);
+ VCALL_LOCALMEM2R(Vector2, move_toward);
VCALL_LOCALMEM1R(Vector2, rotated);
VCALL_LOCALMEM0R(Vector2, tangent);
VCALL_LOCALMEM0R(Vector2, floor);
@@ -388,6 +390,7 @@ struct _VariantCall {
VCALL_LOCALMEM2R(Vector3, linear_interpolate);
VCALL_LOCALMEM2R(Vector3, slerp);
VCALL_LOCALMEM4R(Vector3, cubic_interpolate);
+ VCALL_LOCALMEM2R(Vector3, move_toward);
VCALL_LOCALMEM1R(Vector3, dot);
VCALL_LOCALMEM1R(Vector3, cross);
VCALL_LOCALMEM1R(Vector3, outer);
@@ -1516,9 +1519,9 @@ void register_variant_methods() {
ADDFUNC2R(STRING, STRING, String, replacen, STRING, "what", STRING, "forwhat", varray());
ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray());
ADDFUNC0R(STRING, STRING, String, capitalize, varray());
- ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0));
- ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, rsplit, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0));
- ADDFUNC2R(STRING, POOL_REAL_ARRAY, String, split_floats, STRING, "divisor", BOOL, "allow_empty", varray(true));
+ ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "delimiter", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0));
+ ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, rsplit, STRING, "delimiter", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0));
+ ADDFUNC2R(STRING, POOL_REAL_ARRAY, String, split_floats, STRING, "delimiter", BOOL, "allow_empty", varray(true));
ADDFUNC0R(STRING, STRING, String, to_upper, varray());
ADDFUNC0R(STRING, STRING, String, to_lower, varray());
@@ -1526,6 +1529,7 @@ void register_variant_methods() {
ADDFUNC1R(STRING, STRING, String, left, INT, "position", varray());
ADDFUNC1R(STRING, STRING, String, right, INT, "position", varray());
ADDFUNC2R(STRING, STRING, String, strip_edges, BOOL, "left", BOOL, "right", varray(true, true));
+ ADDFUNC0R(STRING, STRING, String, strip_escapes, varray());
ADDFUNC1R(STRING, STRING, String, lstrip, STRING, "chars", varray());
ADDFUNC1R(STRING, STRING, String, rstrip, STRING, "chars", varray());
ADDFUNC0R(STRING, STRING, String, get_extension, varray());
@@ -1583,6 +1587,7 @@ void register_variant_methods() {
ADDFUNC2R(VECTOR2, VECTOR2, Vector2, linear_interpolate, VECTOR2, "b", REAL, "t", varray());
ADDFUNC2R(VECTOR2, VECTOR2, Vector2, slerp, VECTOR2, "b", REAL, "t", varray());
ADDFUNC4R(VECTOR2, VECTOR2, Vector2, cubic_interpolate, VECTOR2, "b", VECTOR2, "pre_a", VECTOR2, "post_b", REAL, "t", varray());
+ ADDFUNC2R(VECTOR2, VECTOR2, Vector2, move_toward, VECTOR2, "to", REAL, "delta", varray());
ADDFUNC1R(VECTOR2, VECTOR2, Vector2, rotated, REAL, "phi", varray());
ADDFUNC0R(VECTOR2, VECTOR2, Vector2, tangent, varray());
ADDFUNC0R(VECTOR2, VECTOR2, Vector2, floor, varray());
@@ -1624,6 +1629,7 @@ void register_variant_methods() {
ADDFUNC2R(VECTOR3, VECTOR3, Vector3, slerp, VECTOR3, "b", REAL, "t", varray());
ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", REAL, "t", varray());
ADDFUNC1R(VECTOR3, VECTOR3, Vector3, direction_to, VECTOR3, "b", varray());
+ ADDFUNC2R(VECTOR3, VECTOR3, Vector3, move_toward, VECTOR3, "to", REAL, "delta", varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, dot, VECTOR3, "b", varray());
ADDFUNC1R(VECTOR3, VECTOR3, Vector3, cross, VECTOR3, "b", varray());
ADDFUNC1R(VECTOR3, BASIS, Vector3, outer, VECTOR3, "b", varray());
diff --git a/core/vector.h b/core/vector.h
index 93ee003519..e6bb4a96fc 100644
--- a/core/vector.h
+++ b/core/vector.h
@@ -150,7 +150,7 @@ template <class T>
bool Vector<T>::push_back(const T &p_elem) {
Error err = resize(size() + 1);
- ERR_FAIL_COND_V(err, true)
+ ERR_FAIL_COND_V(err, true);
set(size() - 1, p_elem);
return false;
diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml
index 63b9ef13fd..b6de5dbf62 100644
--- a/doc/classes/@GDScript.xml
+++ b/doc/classes/@GDScript.xml
@@ -92,13 +92,13 @@
<argument index="0" name="condition" type="bool">
</argument>
<description>
- Assert that the [code]condition[/code] is [code]true[/code] . If the [code]condition[/code] is [code]false[/code] a fatal error is generated and the program is halted. Useful for debugging to make sure a value is always [code]true[/code].
+ Asserts that the [code]condition[/code] is [code]true[/code] . If the [code]condition[/code] is [code]false[/code], an error is generated and the program is halted until you resume it. Only executes in debug builds, or when running the game from the editor. Use it for debugging purposes, to make sure a statement is [code]true[/code] during development.
[codeblock]
- # Speed should always be between 0 and 20
+ # Imagine we always want speed to be between 0 and 20
speed = -10
- assert(speed &lt; 20) # Is true and program continues
- assert(speed &gt;= 0) # Is false and program stops
- assert(speed &gt;= 0 &amp;&amp; speed &lt; 20) # Or combined
+ assert(speed &lt; 20) # True, the program will continue
+ assert(speed &gt;= 0) # False, the program will stop
+ assert(speed &gt;= 0 &amp;&amp; speed &lt; 20) # You can also combine the two conditional statements in one check
[/codeblock]
</description>
</method>
@@ -640,6 +640,23 @@
[/codeblock]
</description>
</method>
+ <method name="move_toward">
+ <return type="float">
+ </return>
+ <argument index="0" name="from" type="float">
+ </argument>
+ <argument index="1" name="to" type="float">
+ </argument>
+ <argument index="2" name="delta" type="float">
+ </argument>
+ <description>
+ Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value.
+ Use a negative [code]delta[/code] value to move away.
+ [codeblock]
+ move_toward(10, 5, 4) # returns 6
+ [/codeblock]
+ </description>
+ </method>
<method name="nearest_po2">
<return type="int">
</return>
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 760287e1b8..d36a545c56 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -18,6 +18,9 @@
<member name="AudioServer" type="AudioServer" setter="" getter="">
[AudioServer] singleton
</member>
+ <member name="CameraServer" type="CameraServer" setter="" getter="">
+ [CameraServer] singleton
+ </member>
<member name="ClassDB" type="ClassDB" setter="" getter="">
[ClassDB] singleton
</member>
@@ -1003,6 +1006,27 @@
<constant name="JOY_DS_Y" value="2" enum="JoystickList">
DualShock controller Y button
</constant>
+ <constant name="JOY_VR_GRIP" value="2" enum="JoystickList">
+ Grip (side) buttons on a VR controller
+ </constant>
+ <constant name="JOY_VR_PAD" value="14" enum="JoystickList">
+ Push down on the touchpad or main joystick on a VR controller
+ </constant>
+ <constant name="JOY_VR_TRIGGER" value="15" enum="JoystickList">
+ Trigger on a VR controller
+ </constant>
+ <constant name="JOY_OCULUS_AX" value="7" enum="JoystickList">
+ A button on the right Oculus Touch controller, X button on the left controller (also when used in OpenVR)
+ </constant>
+ <constant name="JOY_OCULUS_BY" value="1" enum="JoystickList">
+ B button on the right Oculus Touch controller, Y button on the left controller (also when used in OpenVR)
+ </constant>
+ <constant name="JOY_OCULUS_MENU" value="3" enum="JoystickList">
+ Menu button on either Oculus Touch controller.
+ </constant>
+ <constant name="JOY_OPENVR_MENU" value="1" enum="JoystickList">
+ Menu button in OpenVR (Except when Oculus Touch controllers are used)
+ </constant>
<constant name="JOY_SELECT" value="10" enum="JoystickList">
Joypad Button Select
</constant>
@@ -1085,6 +1109,18 @@
<constant name="JOY_ANALOG_R2" value="7" enum="JoystickList">
Joypad Right Analog Trigger
</constant>
+ <constant name="JOY_VR_ANALOG_TRIGGER" value="2" enum="JoystickList">
+ VR Controller Analog Trigger
+ </constant>
+ <constant name="JOY_VR_ANALOG_GRIP" value="4" enum="JoystickList">
+ VR Controller Analog Grip (side buttons)
+ </constant>
+ <constant name="JOY_OPENVR_TOUCHPADX" value="0" enum="JoystickList">
+ OpenVR touchpad X axis (Joystick axis on Oculus Touch and Windows MR controllers)
+ </constant>
+ <constant name="JOY_OPENVR_TOUCHPADY" value="1" enum="JoystickList">
+ OpenVR touchpad Y axis (Joystick axis on Oculus Touch and Windows MR controllers)
+ </constant>
<constant name="MIDI_MESSAGE_NOTE_OFF" value="8" enum="MidiMessageList">
</constant>
<constant name="MIDI_MESSAGE_NOTE_ON" value="9" enum="MidiMessageList">
diff --git a/doc/classes/ARVRInterface.xml b/doc/classes/ARVRInterface.xml
index 11084fb98e..c286811b5d 100644
--- a/doc/classes/ARVRInterface.xml
+++ b/doc/classes/ARVRInterface.xml
@@ -10,6 +10,13 @@
<tutorials>
</tutorials>
<methods>
+ <method name="get_camera_feed_id">
+ <return type="int">
+ </return>
+ <description>
+ If this is an AR interface that requires displaying a camera feed as the background, this method returns the feed id in the [CameraServer] for this interface.
+ </description>
+ </method>
<method name="get_capabilities" qualifiers="const">
<return type="int">
</return>
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index 9885f30883..c79903cb80 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -536,6 +536,19 @@
Set the interpolation type of a given track, from the INTERPOLATION_* enum.
</description>
</method>
+ <method name="track_set_key_time">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="key_idx" type="int">
+ </argument>
+ <argument index="2" name="time" type="float">
+ </argument>
+ <description>
+ Set the time of an existing key.
+ </description>
+ </method>
<method name="track_set_key_transition">
<return type="void">
</return>
diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml
index 9854d4c27e..23f7a316d9 100644
--- a/doc/classes/AnimationNode.xml
+++ b/doc/classes/AnimationNode.xml
@@ -215,6 +215,7 @@
</signal>
<signal name="tree_changed">
<description>
+ Emitted by nodes that inherit from this class and that have an internal tree when one of their nodes changes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], and [AnimationNodeBlendTree].
</description>
</signal>
</signals>
diff --git a/doc/classes/AnimationNodeAdd2.xml b/doc/classes/AnimationNodeAdd2.xml
index ac2a28e396..890d6f8b49 100644
--- a/doc/classes/AnimationNodeAdd2.xml
+++ b/doc/classes/AnimationNodeAdd2.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeAdd2" inherits="AnimationNode" category="Core" version="3.2">
<brief_description>
+ Blends two animations additively inside of an [AnimationNodeBlendTree].
</brief_description>
<description>
+ A resource to add to an [AnimationNodeBlendTree]. Blends two animations additively based on an amount value in the [code][0.0, 1.0][/code] range.
</description>
<tutorials>
</tutorials>
@@ -10,6 +12,7 @@
</methods>
<members>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync">
+ If [code]true[/code], sets the [code]optimization[/code] to [code]false[/code] when calling [method AnimationNode.blend_input], forcing the blended animations to update every frame.
</member>
</members>
<constants>
diff --git a/doc/classes/AnimationNodeAdd3.xml b/doc/classes/AnimationNodeAdd3.xml
index 9cfeb47378..4e36f0bae4 100644
--- a/doc/classes/AnimationNodeAdd3.xml
+++ b/doc/classes/AnimationNodeAdd3.xml
@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeAdd3" inherits="AnimationNode" category="Core" version="3.2">
<brief_description>
+ Blends two of three animations additively inside of an [AnimationNodeBlendTree].
</brief_description>
<description>
+ A resource to add to an [AnimationNodeBlendTree]. Blends two animations together additively out of three based on a value in the [-1.0, 1.0] range.
+ This node has three inputs:
+ - The base animation to add to
+ - A -add animation to blend with when the blend amount is in the [-1.0, 0.0] range.
+ - A +add animation to blend with when the blend amount is in the [code][0.0, 1.0][/code] range
</description>
<tutorials>
</tutorials>
@@ -10,6 +16,7 @@
</methods>
<members>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync">
+ If [code]true[/code], sets the [code]optimization[/code] to [code]false[/code] when calling [method AnimationNode.blend_input], forcing the blended animations to update every frame.
</member>
</members>
<constants>
diff --git a/doc/classes/AnimationNodeAnimation.xml b/doc/classes/AnimationNodeAnimation.xml
index e7d53fc7b3..420a702d38 100644
--- a/doc/classes/AnimationNodeAnimation.xml
+++ b/doc/classes/AnimationNodeAnimation.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeAnimation" inherits="AnimationRootNode" category="Core" version="3.2">
<brief_description>
+ Input animation to use in an [AnimationNodeBlendTree].
</brief_description>
<description>
+ A resource to add to an [AnimationNodeBlendTree]. Only features one output set using the [member animation] property. Use it as an input for [AnimationNode] that blend animations together.
</description>
<tutorials>
</tutorials>
@@ -10,6 +12,7 @@
</methods>
<members>
<member name="animation" type="String" setter="set_animation" getter="get_animation">
+ Animation to use as an output. It is one of the animations provided by [member AnimationTree.anim_player].
</member>
</members>
<constants>
diff --git a/doc/classes/AnimationNodeBlend2.xml b/doc/classes/AnimationNodeBlend2.xml
index f15fa44f39..9358c5eeef 100644
--- a/doc/classes/AnimationNodeBlend2.xml
+++ b/doc/classes/AnimationNodeBlend2.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeBlend2" inherits="AnimationNode" category="Core" version="3.2">
<brief_description>
+ Blends two animations linearly inside of an [AnimationNodeBlendTree].
</brief_description>
<description>
+ A resource to add to an [AnimationNodeBlendTree]. Blends two animations linearly based on an amount value in the [code][0.0, 1.0][/code] range.
</description>
<tutorials>
</tutorials>
@@ -10,6 +12,7 @@
</methods>
<members>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync">
+ If [code]true[/code], sets the [code]optimization[/code] to [code]false[/code] when calling [method AnimationNode.blend_input], forcing the blended animations to update every frame.
</member>
</members>
<constants>
diff --git a/doc/classes/AnimationNodeBlend3.xml b/doc/classes/AnimationNodeBlend3.xml
index 2f82eea041..9b9cf80968 100644
--- a/doc/classes/AnimationNodeBlend3.xml
+++ b/doc/classes/AnimationNodeBlend3.xml
@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeBlend3" inherits="AnimationNode" category="Core" version="3.2">
<brief_description>
+ Blends two of three animations linearly inside of an [AnimationNodeBlendTree].
</brief_description>
<description>
+ A resource to add to an [AnimationNodeBlendTree]. Blends two animations together linearly out of three based on a value in the [-1.0, 1.0] range.
+ This node has three inputs:
+ - The base animation
+ - A -blend animation to blend with when the blend amount is in the [-1.0, 0.0] range.
+ - A +blend animation to blend with when the blend amount is in the [code][0.0, 1.0][/code] range
</description>
<tutorials>
</tutorials>
@@ -10,6 +16,7 @@
</methods>
<members>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync">
+ If [code]true[/code], sets the [code]optimization[/code] to [code]false[/code] when calling [method AnimationNode.blend_input], forcing the blended animations to update every frame.
</member>
</members>
<constants>
diff --git a/doc/classes/AnimationNodeBlendSpace1D.xml b/doc/classes/AnimationNodeBlendSpace1D.xml
index b13aee277e..6fb5c6312b 100644
--- a/doc/classes/AnimationNodeBlendSpace1D.xml
+++ b/doc/classes/AnimationNodeBlendSpace1D.xml
@@ -1,8 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeBlendSpace1D" inherits="AnimationRootNode" category="Core" version="3.2">
<brief_description>
+ Blends linearly between two of any number of [AnimationNode] of any type placed on a virtual axis.
</brief_description>
<description>
+ A resource to add to an [AnimationNodeBlendTree].
+ This is a virtual axis on which you can add any type of [AnimationNode] using [method add_blend_point].
+ Outputs the linear blend of the two [code]AnimationNode[/code] closest to the node's current [code]value[/code].
+ You can set the extents of the axis using the [member min_space] and [member max_space].
</description>
<tutorials>
</tutorials>
@@ -17,12 +22,14 @@
<argument index="2" name="at_index" type="int" default="-1">
</argument>
<description>
+ Add a new point that represents a [code]node[/code] on the virtual axis at a given position set by [code]pos[/code]. You can insert it at a specific index using the [code]at_index[/code] argument. If you use the default value for [code]at_index[/code] , the point is inserted at the end of the blend points array.
</description>
</method>
<method name="get_blend_point_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of points on the blend axis.
</description>
</method>
<method name="get_blend_point_node" qualifiers="const">
@@ -31,6 +38,7 @@
<argument index="0" name="point" type="int">
</argument>
<description>
+ Returns the [code]AnimationNode[/code] referenced by the point at index [code]point[/code].
</description>
</method>
<method name="get_blend_point_position" qualifiers="const">
@@ -39,6 +47,7 @@
<argument index="0" name="point" type="int">
</argument>
<description>
+ Returns the position of the point at index [code]point[/code].
</description>
</method>
<method name="remove_blend_point">
@@ -47,6 +56,7 @@
<argument index="0" name="point" type="int">
</argument>
<description>
+ Removes the point at index [code]point[/code] from the blend axis.
</description>
</method>
<method name="set_blend_point_node">
@@ -57,6 +67,7 @@
<argument index="1" name="node" type="AnimationRootNode">
</argument>
<description>
+ Changes the AnimationNode referenced by the point at index [code]point[/code].
</description>
</method>
<method name="set_blend_point_position">
@@ -67,17 +78,22 @@
<argument index="1" name="pos" type="float">
</argument>
<description>
+ Updates the position of the point at index [code]point[/code] on the blend axis.
</description>
</method>
</methods>
<members>
<member name="max_space" type="float" setter="set_max_space" getter="get_max_space">
+ The blend space's axis's upper limit for the points' position. See [method add_blend_point].
</member>
<member name="min_space" type="float" setter="set_min_space" getter="get_min_space">
+ The blend space's axis's lower limit for the points' position. See [method add_blend_point].
</member>
<member name="snap" type="float" setter="set_snap" getter="get_snap">
+ Position increment to snap to when moving a point on the axis.
</member>
<member name="value_label" type="String" setter="set_value_label" getter="get_value_label">
+ Label of the virtual axis of the blend space.
</member>
</members>
<constants>
diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml
index 2ec5977301..74d4f1c60d 100644
--- a/doc/classes/AnimationNodeBlendSpace2D.xml
+++ b/doc/classes/AnimationNodeBlendSpace2D.xml
@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeBlendSpace2D" inherits="AnimationRootNode" category="Core" version="3.2">
<brief_description>
+ Blends linearly between three [AnimationNode] of any type placed in a 2d space.
</brief_description>
<description>
+ A resource to add to an [AnimationNodeBlendTree].
+ This node allows you to blend linearly between three animations using a [Vector2] weight.
+ You can add vertices to the blend space with [method add_blend_point] and automatically triangulate it by setting [member auto_triangles] to [code]true[/code]. Otherwise, use [method add_triangle] and [method remove_triangle] to create up the blend space by hand.
</description>
<tutorials>
</tutorials>
@@ -17,6 +21,7 @@
<argument index="2" name="at_index" type="int" default="-1">
</argument>
<description>
+ Add a new point that represents a [code]node[/code] at the position set by [code]pos[/code]. You can insert it at a specific index using the [code]at_index[/code] argument. If you use the default value for [code]at_index[/code] , the point is inserted at the end of the blend points array.
</description>
</method>
<method name="add_triangle">
@@ -31,12 +36,14 @@
<argument index="3" name="at_index" type="int" default="-1">
</argument>
<description>
+ Creates a new triangle using three points [code]x[/code], [code]y[/code], and [code]z[/code]. Triangles can overlap. You can insert the triangle at a specific index using the [code]at_index[/code] argument. If you use the default value for [code]at_index[/code] , the point is inserted at the end of the blend points array.
</description>
</method>
<method name="get_blend_point_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of points in the blend space.
</description>
</method>
<method name="get_blend_point_node" qualifiers="const">
@@ -45,6 +52,7 @@
<argument index="0" name="point" type="int">
</argument>
<description>
+ Returns the [code]AnimationRootNode[/code] referenced by the point at index [code]point[/code].
</description>
</method>
<method name="get_blend_point_position" qualifiers="const">
@@ -53,12 +61,14 @@
<argument index="0" name="point" type="int">
</argument>
<description>
+ Returns the position of the point at index [code]point[/code].
</description>
</method>
<method name="get_triangle_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of triangles in the blend space.
</description>
</method>
<method name="get_triangle_point">
@@ -69,6 +79,7 @@
<argument index="1" name="point" type="int">
</argument>
<description>
+ Returns the position of the point at index [code]point[/code] in the triangle of index [code]triangle[/code].
</description>
</method>
<method name="remove_blend_point">
@@ -77,6 +88,7 @@
<argument index="0" name="point" type="int">
</argument>
<description>
+ Removes the point at index [code]point[/code] from the blend space.
</description>
</method>
<method name="remove_triangle">
@@ -85,6 +97,7 @@
<argument index="0" name="triangle" type="int">
</argument>
<description>
+ Removes the triangle at index [code]triangle[/code] from the blend space.
</description>
</method>
<method name="set_blend_point_node">
@@ -95,6 +108,7 @@
<argument index="1" name="node" type="AnimationRootNode">
</argument>
<description>
+ Changes the AnimationNode referenced by the point at index [code]point[/code].
</description>
</method>
<method name="set_blend_point_position">
@@ -105,39 +119,49 @@
<argument index="1" name="pos" type="Vector2">
</argument>
<description>
+ Updates the position of the point at index [code]point[/code] on the blend axis.
</description>
</method>
</methods>
<members>
<member name="auto_triangles" type="bool" setter="set_auto_triangles" getter="get_auto_triangles">
+ If true, the blend space is triangulated automatically. The mesh updates every time you add or remove points with [method add_blend_point] and [method remove_blend_point].
</member>
<member name="blend_mode" type="int" setter="set_blend_mode" getter="get_blend_mode" enum="AnimationNodeBlendSpace2D.BlendMode">
+ Controls the interpolation between animations. See [enum BlendMode] constants.
</member>
<member name="max_space" type="Vector2" setter="set_max_space" getter="get_max_space">
+ The blend space's X and Y axes' upper limit for the points' position. See [method add_blend_point].
</member>
<member name="min_space" type="Vector2" setter="set_min_space" getter="get_min_space">
+ The blend space's X and Y axes' lower limit for the points' position. See [method add_blend_point].
</member>
<member name="snap" type="Vector2" setter="set_snap" getter="get_snap">
+ Position increment to snap to when moving a point.
</member>
<member name="x_label" type="String" setter="set_x_label" getter="get_x_label">
+ Name of the blend space's X axis.
</member>
<member name="y_label" type="String" setter="set_y_label" getter="get_y_label">
+ Name of the blend space's Y axis.
</member>
</members>
<signals>
<signal name="triangles_updated">
<description>
+ Emitted every time the blend space's triangles are created, removed, or when one of their vertices changes position.
</description>
</signal>
</signals>
<constants>
<constant name="BLEND_MODE_INTERPOLATED" value="0" enum="BlendMode">
+ The interpolation between animations is linear.
</constant>
<constant name="BLEND_MODE_DISCRETE" value="1" enum="BlendMode">
- Useful for frame-by-frame 2D animations.
+ The blend space plays the animation of the node the blending position is closest to. Useful for frame-by-frame 2D animations.
</constant>
<constant name="BLEND_MODE_DISCRETE_CARRY" value="2" enum="BlendMode">
- Keep the current play position when switching between discrete animations.
+ Similar to [const BLEND_MODE_DISCRETE], but starts the new animation at the last animation's playback position.
</constant>
</constants>
</class>
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index 1707ea07e0..d44e3c54c9 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ArrayMesh" inherits="Mesh" category="Core" version="3.2">
<brief_description>
+ [Mesh] type that provides utility for constructing a surface from arrays.
</brief_description>
<description>
The [ArrayMesh] is used to construct a [Mesh] by specifying the attributes as arrays. The most basic example is the creation of a single triangle
@@ -30,6 +31,7 @@
<argument index="0" name="name" type="String">
</argument>
<description>
+ Add name for a blend shape that will be added with [method add_surface_from_arrays]. Must be called before surface is added.
</description>
</method>
<method name="add_surface_from_arrays">
@@ -155,17 +157,6 @@
Remove a surface at position surf_idx, shifting greater surfaces one surf_idx slot down.
</description>
</method>
- <method name="surface_set_material">
- <return type="void">
- </return>
- <argument index="0" name="surf_idx" type="int">
- </argument>
- <argument index="1" name="material" type="Material">
- </argument>
- <description>
- Set a [Material] for a given surface. Surface will be rendered using this material.
- </description>
- </method>
<method name="surface_set_name">
<return type="void">
</return>
@@ -187,6 +178,7 @@
<argument index="2" name="data" type="PoolByteArray">
</argument>
<description>
+ Updates a specified region of mesh arrays on GPU. Warning: only use if you know what you are doing. You can easily cause crashes by calling this function with improper arguments.
</description>
</method>
</methods>
@@ -194,7 +186,7 @@
<member name="blend_shape_mode" type="int" setter="set_blend_shape_mode" getter="get_blend_shape_mode" enum="Mesh.BlendShapeMode">
</member>
<member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb">
- An overriding bounding box for this mesh.
+ Overrides the [AABB] with one defined by user for use with frustum culling. Especially useful to avoid unnexpected culling when using a shader to offset vertices.
</member>
</members>
<constants>
diff --git a/doc/classes/AudioEffectReverb.xml b/doc/classes/AudioEffectReverb.xml
index fde22c9238..8c9652eee2 100644
--- a/doc/classes/AudioEffectReverb.xml
+++ b/doc/classes/AudioEffectReverb.xml
@@ -21,7 +21,7 @@
<member name="hipass" type="float" setter="set_hpf" getter="get_hpf">
High-pass filter passes signals with a frequency higher than a certain cutoff frequency and attenuates signals with frequencies lower than the cutoff frequency. Value can range from 0 to 1. Default value: [code]0[/code].
</member>
- <member name="predelay_feedback" type="float" setter="set_predelay_msec" getter="get_predelay_msec">
+ <member name="predelay_feedback" type="float" setter="set_predelay_feedback" getter="get_predelay_feedback">
Output percent of predelay. Value can range from 0 to 1. Default value: [code]1[/code].
</member>
<member name="predelay_msec" type="float" setter="set_predelay_msec" getter="get_predelay_msec">
diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml
index 59f7bec889..757c6c6a34 100644
--- a/doc/classes/Bone2D.xml
+++ b/doc/classes/Bone2D.xml
@@ -1,8 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Bone2D" inherits="Node2D" category="Core" version="3.2">
<brief_description>
+ Joint used with [Skeleton2D] to control and animate other nodes.
</brief_description>
<description>
+ Use a hierarchy of [code]Bone2D[/code] bound to a [Skeleton2D] to control, and animate other [Node2D] nodes.
+ You can use [code]Bone2D[/code] and [code]Skeleton2D[/code] nodes to animate 2D meshes created with the Polygon 2D UV editor.
+ Each bone has a [member rest] transform that you can reset to with [method apply_rest]. These rest poses are relative to the bone's parent.
+ If in the editor, you can set the rest pose of an entire skeleton using a menu option, from the code, you need to iterate over the bones to set their individual rest poses.
</description>
<tutorials>
</tutorials>
@@ -11,25 +16,30 @@
<return type="void">
</return>
<description>
+ Stores the node's current transforms in [member rest].
</description>
</method>
<method name="get_index_in_skeleton" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the node's index as part of the entire skeleton. See [Skeleton2D].
</description>
</method>
<method name="get_skeleton_rest" qualifiers="const">
<return type="Transform2D">
</return>
<description>
+ Returns the node's [member rest] [code]Transform2D[/code] if it doesn't have a parent, or its rest pose relative to its parent.
</description>
</method>
</methods>
<members>
<member name="default_length" type="float" setter="set_default_length" getter="get_default_length">
+ Length of the bone's representation drawn in the editor's viewport in pixels.
</member>
<member name="rest" type="Transform2D" setter="set_rest" getter="get_rest">
+ Rest transform of the bone. You can reset the node's transforms to this value using [method apply_rest].
</member>
</members>
<constants>
diff --git a/doc/classes/CPUParticles.xml b/doc/classes/CPUParticles.xml
index 599c067328..c9c92102f3 100644
--- a/doc/classes/CPUParticles.xml
+++ b/doc/classes/CPUParticles.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CPUParticles" inherits="GeometryInstance" category="Core" version="3.2">
<brief_description>
+ CPU-based 3D particle emitter.
</brief_description>
<description>
+ CPU-based 3D particle node used to create a variety of particle systems and effects.
+ See also [Particles], which provides the same functionality with hardware acceleration, but may not run on older devices.
</description>
<tutorials>
</tutorials>
@@ -13,56 +16,77 @@
<argument index="0" name="particles" type="Node">
</argument>
<description>
+ Sets this node's properties to match a given [Particles] node with an assigned [ParticlesMaterial].
</description>
</method>
<method name="restart">
<return type="void">
</return>
<description>
+ Restarts the particle emitter.
</description>
</method>
</methods>
<members>
<member name="amount" type="int" setter="set_amount" getter="get_amount">
+ Number of particles emitted in one emission cycle.
</member>
<member name="angle" type="float" setter="set_param" getter="get_param">
+ Initial rotation applied to each particle, in degrees.
</member>
<member name="angle_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's rotation will be animated along this [Curve].
</member>
<member name="angle_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Rotation randomness ratio. Default value: [code]0[/code].
</member>
<member name="angular_velocity" type="float" setter="set_param" getter="get_param">
+ Initial angular velocity applied to each particle. Sets the speed of rotation of the particle.
</member>
<member name="angular_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's angular velocity will vary along this [Curve].
</member>
<member name="angular_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Angular velocity randomness ratio. Default value: [code]0[/code].
</member>
<member name="anim_offset" type="float" setter="set_param" getter="get_param">
+ Particle animation offset.
</member>
<member name="anim_offset_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's animation offset will vary along this [Curve].
</member>
<member name="anim_offset_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Animation offset randomness ratio. Default value: [code]0[/code].
</member>
<member name="anim_speed" type="float" setter="set_param" getter="get_param">
+ Particle animation speed.
</member>
<member name="anim_speed_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's animation speed will vary along this [Curve].
</member>
<member name="anim_speed_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Animation speed randomness ratio. Default value: [code]0[/code].
</member>
<member name="color" type="Color" setter="set_color" getter="get_color">
+ Unused for 3D particles.
</member>
<member name="color_ramp" type="Gradient" setter="set_color_ramp" getter="get_color_ramp">
- Each particle's vertex color will vary along this [GradientTexture].
+ Unused for 3D particles.
</member>
<member name="damping" type="float" setter="set_param" getter="get_param">
+ The rate at which particles lose velocity.
</member>
<member name="damping_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Damping will vary along this [Curve].
</member>
<member name="damping_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Damping randomness ratio. Default value: [code]0[/code].
</member>
<member name="draw_order" type="int" setter="set_draw_order" getter="get_draw_order" enum="CPUParticles.DrawOrder">
+ Particle draw order. Uses [enum DrawOrder] values. Default value: [constant DRAW_ORDER_INDEX].
</member>
<member name="emission_box_extents" type="Vector3" setter="set_emission_box_extents" getter="get_emission_box_extents">
+ The rectangle's extents if [member emission_shape] is set to [constant EMISSION_SHAPE_BOX].
</member>
<member name="emission_colors" type="PoolColorArray" setter="set_emission_colors" getter="get_emission_colors">
</member>
@@ -71,124 +95,199 @@
<member name="emission_points" type="PoolVector3Array" setter="set_emission_points" getter="get_emission_points">
</member>
<member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="CPUParticles.EmissionShape">
+ Particles will be emitted inside this region. Use [enum EmissionShape] for values. Default value: [constant EMISSION_SHAPE_POINT].
</member>
<member name="emission_sphere_radius" type="float" setter="set_emission_sphere_radius" getter="get_emission_sphere_radius">
+ The sphere's radius if [enum EmissionShape] is set to [constant EMISSION_SHAPE_SPHERE].
</member>
<member name="emitting" type="bool" setter="set_emitting" getter="is_emitting">
+ If [code]true[/code], particles are being emitted. Default value: [code]true[/code].
</member>
<member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio">
+ How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins. Default value: [code]0[/code].
</member>
<member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps">
+ The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the particle system itself.
</member>
<member name="flag_align_y" type="bool" setter="set_particle_flag" getter="get_particle_flag">
+ Align y-axis of particle with the direction of its velocity.
</member>
<member name="flag_disable_z" type="bool" setter="set_particle_flag" getter="get_particle_flag">
+ If [code]true[/code], particles will not move on the z axis. Default value: [code]false[/code].
</member>
<member name="flag_rotate_y" type="bool" setter="set_particle_flag" getter="get_particle_flag">
+ If [code]true[/code], particles rotate around y-axis by [member angle].
</member>
<member name="flatness" type="float" setter="set_flatness" getter="get_flatness">
+ Amount of [member spread] in Y/Z plane. A value of [code]1[/code] restricts particles to X/Z plane. Default [code]0[/code].
</member>
<member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta">
+ If [code]true[/code], results in fractional delta calculation which has a smoother particles display effect. Default value: [code]true[/code]
</member>
<member name="gravity" type="Vector3" setter="set_gravity" getter="get_gravity">
+ Gravity applied to every particle. Default value: [code](0, -9.8, 0)[/code].
</member>
<member name="hue_variation" type="float" setter="set_param" getter="get_param">
+ Initial hue variation applied to each particle.
</member>
<member name="hue_variation_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's hue will vary along this [Curve].
</member>
<member name="hue_variation_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Hue variation randomness ratio. Default value: [code]0[/code].
</member>
<member name="initial_velocity" type="float" setter="set_param" getter="get_param">
+ Initial velocity magnitude for each particle. Direction comes from [member spread] and the node's orientation.
</member>
<member name="initial_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Initial velocity randomness ratio. Default value: [code]0[/code].
</member>
<member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime">
+ Amount of time each particle will exist. Default value: [code]1[/code].
</member>
<member name="linear_accel" type="float" setter="set_param" getter="get_param">
+ Linear acceleration applied to each particle in the direction of motion.
</member>
<member name="linear_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's linear acceleration will vary along this [Curve].
</member>
<member name="linear_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Linear acceleration randomness ratio. Default value: [code]0[/code].
</member>
<member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates">
+ If [code]true[/code], particles use the parent node's coordinate space. If [code]false[/code], they use global coordinates. Default value: [code]true[/code].
</member>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
+ The [Mesh] used for each particle. If [code]null[/code], particles will be spheres.
</member>
<member name="one_shot" type="bool" setter="set_one_shot" getter="get_one_shot">
+ If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end. Default value: [code]false[/code].
+ </member>
+ <member name="orbit_velocity" type="float" setter="set_param" getter="get_param">
+ Orbital velocity applied to each particle. Makes the particles circle around origin in the local XY plane. Specified in number of full rotations around origin per second.
+ This property is only available when [member flag_disable_z] is [code]true[/code].
+ </member>
+ <member name="orbit_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's orbital velocity will vary along this [Curve].
+ </member>
+ <member name="orbit_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Orbital velocity randomness ratio. Default value: [code]0[/code].
</member>
<member name="preprocess" type="float" setter="set_pre_process_time" getter="get_pre_process_time">
+ Particle system starts as if it had already run for this many seconds.
</member>
<member name="radial_accel" type="float" setter="set_param" getter="get_param">
+ Radial acceleration applied to each particle. Makes particle accelerate away from origin.
</member>
<member name="radial_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's radial acceleration will vary along this [Curve].
</member>
<member name="radial_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Radial acceleration randomness ratio. Default value: [code]0[/code].
</member>
<member name="randomness" type="float" setter="set_randomness_ratio" getter="get_randomness_ratio">
+ Emission lifetime randomness ratio. Default value: [code]0[/code].
</member>
<member name="scale_amount" type="float" setter="set_param" getter="get_param">
+ Initial scale applied to each particle.
</member>
<member name="scale_amount_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's scale will vary along this [Curve].
</member>
<member name="scale_amount_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Scale randomness ratio. Default value: [code]0[/code].
</member>
<member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale">
+ Particle system's running speed scaling ratio. Default value: [code]1[/code]. A value of [code]0[/code] can be used to pause the particles.
</member>
<member name="spread" type="float" setter="set_spread" getter="get_spread">
+ Each particle's initial direction range from [code]+spread[/code] to [code]-spread[/code] degrees. Default value: [code]45[/code].
</member>
<member name="tangential_accel" type="float" setter="set_param" getter="get_param">
+ Tangential acceleration applied to each particle. Tangential acceleration is perpendicular to the particle's velocity giving the particles a swirling motion.
</member>
<member name="tangential_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's tangential acceleration will vary along this [Curve].
</member>
<member name="tangential_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Tangential acceleration randomness ratio. Default value: [code]0[/code].
</member>
</members>
<constants>
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
+ Particles are drawn in the order emitted.
</constant>
<constant name="DRAW_ORDER_LIFETIME" value="1" enum="DrawOrder">
+ Particles are drawn in order of remaining lifetime.
</constant>
<constant name="DRAW_ORDER_VIEW_DEPTH" value="2" enum="DrawOrder">
+ Particles are drawn in order of depth.
</constant>
<constant name="PARAM_INITIAL_LINEAR_VELOCITY" value="0" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set initial velocity properties.
</constant>
<constant name="PARAM_ANGULAR_VELOCITY" value="1" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set angular velocity properties.
</constant>
- <constant name="PARAM_LINEAR_ACCEL" value="2" enum="Parameter">
+ <constant name="PARAM_ORBIT_VELOCITY" value="2" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set orbital velocity properties.
</constant>
- <constant name="PARAM_RADIAL_ACCEL" value="3" enum="Parameter">
+ <constant name="PARAM_LINEAR_ACCEL" value="3" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set linear acceleration properties.
</constant>
- <constant name="PARAM_TANGENTIAL_ACCEL" value="4" enum="Parameter">
+ <constant name="PARAM_RADIAL_ACCEL" value="4" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set radial acceleration properties.
</constant>
- <constant name="PARAM_DAMPING" value="5" enum="Parameter">
+ <constant name="PARAM_TANGENTIAL_ACCEL" value="5" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set tangential acceleration properties.
</constant>
- <constant name="PARAM_ANGLE" value="6" enum="Parameter">
+ <constant name="PARAM_DAMPING" value="6" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set damping properties.
</constant>
- <constant name="PARAM_SCALE" value="7" enum="Parameter">
+ <constant name="PARAM_ANGLE" value="7" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set angle properties.
</constant>
- <constant name="PARAM_HUE_VARIATION" value="8" enum="Parameter">
+ <constant name="PARAM_SCALE" value="8" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set scale properties.
</constant>
- <constant name="PARAM_ANIM_SPEED" value="9" enum="Parameter">
+ <constant name="PARAM_HUE_VARIATION" value="9" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set hue variation properties.
</constant>
- <constant name="PARAM_ANIM_OFFSET" value="10" enum="Parameter">
+ <constant name="PARAM_ANIM_SPEED" value="10" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set animation speed properties.
</constant>
- <constant name="PARAM_MAX" value="11" enum="Parameter">
+ <constant name="PARAM_ANIM_OFFSET" value="11" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set animation offset properties.
+ </constant>
+ <constant name="PARAM_MAX" value="12" enum="Parameter">
+ Represents the size of the [enum Parameter] enum.
</constant>
<constant name="FLAG_ALIGN_Y_TO_VELOCITY" value="0" enum="Flags">
+ Use with [method set_flag] to set [member flag_align_y].
</constant>
<constant name="FLAG_ROTATE_Y" value="1" enum="Flags">
+ Use with [method set_flag] to set [member flag_rotate_y].
+ </constant>
+ <constant name="FLAG_DISABLE_Z" value="2" enum="Flags">
+ Use with [method set_flag] to set [member flag_disable_z].
</constant>
<constant name="FLAG_MAX" value="3" enum="Flags">
+ Represents the size of the [enum Flags] enum.
</constant>
<constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape">
+ All particles will be emitted from a single point.
</constant>
<constant name="EMISSION_SHAPE_SPHERE" value="1" enum="EmissionShape">
+ Particles will be emitted in the volume of a sphere.
</constant>
<constant name="EMISSION_SHAPE_BOX" value="2" enum="EmissionShape">
+ Particles will be emitted in the volume of a box.
</constant>
<constant name="EMISSION_SHAPE_POINTS" value="3" enum="EmissionShape">
+ Particles will be emitted at a position chosen randomly among [member emission_points]. Particle color will be modulated by [member emission_colors].
</constant>
<constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape">
+ Particles will be emitted at a position chosen randomly among [member emission_points]. Particle velocity and rotation will be set based on [member emission_normals]. Particle color will be modulated by [member emission_colors].
</constant>
</constants>
</class>
diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index e1f71e3600..4351a0b4d4 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CPUParticles2D" inherits="Node2D" category="Core" version="3.2">
<brief_description>
+ CPU-based 2D particle emitter.
</brief_description>
<description>
+ CPU-based 2D particle node used to create a variety of particle systems and effects.
+ See also [Particles2D], which provides the same functionality with hardware acceleration, but may not run on older devices.
</description>
<tutorials>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/particle_systems_2d.html</link>
</tutorials>
<methods>
<method name="convert_from_particles">
@@ -13,53 +17,74 @@
<argument index="0" name="particles" type="Node">
</argument>
<description>
+ Sets this node's properties to match a given [Particles2D] node with an assigned [ParticlesMaterial].
</description>
</method>
<method name="restart">
<return type="void">
</return>
<description>
+ Restarts the particle emitter.
</description>
</method>
</methods>
<members>
<member name="amount" type="int" setter="set_amount" getter="get_amount">
+ Number of particles emitted in one emission cycle.
</member>
<member name="angle" type="float" setter="set_param" getter="get_param">
+ Initial rotation applied to each particle, in degrees.
</member>
<member name="angle_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's rotation will be animated along this [Curve].
</member>
<member name="angle_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Rotation randomness ratio. Default value: [code]0[/code].
</member>
<member name="angular_velocity" type="float" setter="set_param" getter="get_param">
+ Initial angular velocity applied to each particle. Sets the speed of rotation of the particle.
</member>
<member name="angular_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's angular velocity will vary along this [Curve].
</member>
<member name="angular_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Angular velocity randomness ratio. Default value: [code]0[/code].
</member>
<member name="anim_offset" type="float" setter="set_param" getter="get_param">
+ Particle animation offset.
</member>
<member name="anim_offset_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's animation offset will vary along this [Curve].
</member>
<member name="anim_offset_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Animation offset randomness ratio. Default value: [code]0[/code].
</member>
<member name="anim_speed" type="float" setter="set_param" getter="get_param">
+ Particle animation speed.
</member>
<member name="anim_speed_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's animation speed will vary along this [Curve].
</member>
<member name="anim_speed_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Animation speed randomness ratio. Default value: [code]0[/code].
</member>
<member name="color" type="Color" setter="set_color" getter="get_color">
+ Each particle's initial color. If [member texture] is defined, it will be multiplied by this color.
</member>
<member name="color_ramp" type="Gradient" setter="set_color_ramp" getter="get_color_ramp">
+ Each particle's color will vary along this [Gradient].
</member>
<member name="damping" type="float" setter="set_param" getter="get_param">
+ The rate at which particles lose velocity.
</member>
<member name="damping_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Damping will vary along this [Curve].
</member>
<member name="damping_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Damping randomness ratio. Default value: [code]0[/code].
</member>
<member name="draw_order" type="int" setter="set_draw_order" getter="get_draw_order" enum="CPUParticles2D.DrawOrder">
+ Particle draw order. Uses [enum DrawOrder] values. Default value: [constant DRAW_ORDER_INDEX].
</member>
<member name="emission_colors" type="PoolColorArray" setter="set_emission_colors" getter="get_emission_colors">
</member>
@@ -68,122 +93,194 @@
<member name="emission_points" type="PoolVector2Array" setter="set_emission_points" getter="get_emission_points">
</member>
<member name="emission_rect_extents" type="Vector2" setter="set_emission_rect_extents" getter="get_emission_rect_extents">
+ The rectangle's extents if [member emission_shape] is set to [constant EMISSION_SHAPE_RECTANGLE].
</member>
<member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="CPUParticles2D.EmissionShape">
+ Particles will be emitted inside this region. Use [enum EmissionShape] for values. Default value: [constant EMISSION_SHAPE_POINT].
</member>
<member name="emission_sphere_radius" type="float" setter="set_emission_sphere_radius" getter="get_emission_sphere_radius">
+ The circle's radius if [member emission_shape] is set to [constant EMISSION_SHAPE_CIRCLE].
</member>
<member name="emitting" type="bool" setter="set_emitting" getter="is_emitting">
+ If [code]true[/code], particles are being emitted. Default value: [code]true[/code].
</member>
<member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio">
+ How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins. Default value: [code]0[/code].
</member>
<member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps">
+ The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself.
</member>
<member name="flag_align_y" type="bool" setter="set_particle_flag" getter="get_particle_flag">
+ Align y-axis of particle with the direction of its velocity.
</member>
<member name="flatness" type="float" setter="set_flatness" getter="get_flatness">
</member>
<member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta">
+ If [code]true[/code], results in fractional delta calculation which has a smoother particles display effect. Default value: [code]true[/code]
</member>
<member name="gravity" type="Vector2" setter="set_gravity" getter="get_gravity">
+ Gravity applied to every particle. Default value: [code](0, 98)[/code].
</member>
<member name="hue_variation" type="float" setter="set_param" getter="get_param">
+ Initial hue variation applied to each particle.
</member>
<member name="hue_variation_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's hue will vary along this [Curve].
</member>
<member name="hue_variation_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Hue variation randomness ratio. Default value: [code]0[/code].
</member>
<member name="initial_velocity" type="float" setter="set_param" getter="get_param">
+ Initial velocity magnitude for each particle. Direction comes from [member spread] and the node's orientation.
</member>
<member name="initial_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Initial velocity randomness ratio. Default value: [code]0[/code].
</member>
<member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime">
+ Amount of time each particle will exist. Default value: [code]1[/code].
</member>
<member name="linear_accel" type="float" setter="set_param" getter="get_param">
+ Linear acceleration applied to each particle in the direction of motion.
</member>
<member name="linear_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's linear acceleration will vary along this [Curve].
</member>
<member name="linear_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Linear acceleration randomness ratio. Default value: [code]0[/code].
</member>
<member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates">
+ If [code]true[/code], particles use the parent node's coordinate space. If [code]false[/code], they use global coordinates. Default value: [code]true[/code].
</member>
<member name="normalmap" type="Texture" setter="set_normalmap" getter="get_normalmap">
+ Normal map to be used for the [member texture] property.
</member>
<member name="one_shot" type="bool" setter="set_one_shot" getter="get_one_shot">
+ If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end. Default value: [code]false[/code].
+ </member>
+ <member name="orbit_velocity" type="float" setter="set_param" getter="get_param">
+ Orbital velocity applied to each particle. Makes the particles circle around origin. Specified in number of full rotations around origin per second.
+ </member>
+ <member name="orbit_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's orbital velocity will vary along this [Curve].
+ </member>
+ <member name="orbit_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Orbital velocity randomness ratio. Default value: [code]0[/code].
</member>
<member name="preprocess" type="float" setter="set_pre_process_time" getter="get_pre_process_time">
+ Particle system starts as if it had already run for this many seconds.
</member>
<member name="radial_accel" type="float" setter="set_param" getter="get_param">
+ Radial acceleration applied to each particle. Makes particle accelerate away from origin.
</member>
<member name="radial_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's radial acceleration will vary along this [Curve].
</member>
<member name="radial_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Radial acceleration randomness ratio. Default value: [code]0[/code].
</member>
<member name="randomness" type="float" setter="set_randomness_ratio" getter="get_randomness_ratio">
+ Emission lifetime randomness ratio. Default value: [code]0[/code].
</member>
<member name="scale_amount" type="float" setter="set_param" getter="get_param">
+ Initial scale applied to each particle.
</member>
<member name="scale_amount_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's scale will vary along this [Curve].
</member>
<member name="scale_amount_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Scale randomness ratio. Default value: [code]0[/code].
</member>
<member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale">
+ Particle system's running speed scaling ratio. Default value: [code]1[/code]. A value of [code]0[/code] can be used to pause the particles.
</member>
<member name="spread" type="float" setter="set_spread" getter="get_spread">
+ Each particle's initial direction range from [code]+spread[/code] to [code]-spread[/code] degrees. Default value: [code]45[/code].
</member>
<member name="tangential_accel" type="float" setter="set_param" getter="get_param">
+ Tangential acceleration applied to each particle. Tangential acceleration is perpendicular to the particle's velocity giving the particles a swirling motion.
</member>
<member name="tangential_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
+ Each particle's tangential acceleration will vary along this [Curve].
</member>
<member name="tangential_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
+ Tangential acceleration randomness ratio. Default value: [code]0[/code].
</member>
<member name="texture" type="Texture" setter="set_texture" getter="get_texture">
+ Particle texture. If [code]null[/code] particles will be squares.
</member>
</members>
<constants>
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
+ Particles are drawn in the order emitted.
</constant>
<constant name="DRAW_ORDER_LIFETIME" value="1" enum="DrawOrder">
+ Particles are drawn in order of remaining lifetime.
</constant>
<constant name="PARAM_INITIAL_LINEAR_VELOCITY" value="0" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set initial velocity properties.
</constant>
<constant name="PARAM_ANGULAR_VELOCITY" value="1" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set angular velocity properties.
</constant>
<constant name="PARAM_ORBIT_VELOCITY" value="2" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set orbital velocity properties.
</constant>
<constant name="PARAM_LINEAR_ACCEL" value="3" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set linear acceleration properties.
</constant>
<constant name="PARAM_RADIAL_ACCEL" value="4" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set radial acceleration properties.
</constant>
<constant name="PARAM_TANGENTIAL_ACCEL" value="5" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set tangential acceleration properties.
</constant>
<constant name="PARAM_DAMPING" value="6" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set damping properties.
</constant>
<constant name="PARAM_ANGLE" value="7" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set angle properties.
</constant>
<constant name="PARAM_SCALE" value="8" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set scale properties.
</constant>
<constant name="PARAM_HUE_VARIATION" value="9" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set hue variation properties.
</constant>
<constant name="PARAM_ANIM_SPEED" value="10" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set animation speed properties.
</constant>
<constant name="PARAM_ANIM_OFFSET" value="11" enum="Parameter">
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set animation offset properties.
</constant>
<constant name="PARAM_MAX" value="12" enum="Parameter">
+ Represents the size of the [enum Parameter] enum.
</constant>
<constant name="FLAG_ALIGN_Y_TO_VELOCITY" value="0" enum="Flags">
+ Use with [method set_flag] to set [member flag_align_y].
+ </constant>
+ <constant name="FLAG_ROTATE_Y" value="1" enum="Flags">
+ Present for consistency with 3D particle nodes, not used in 2D.
+ </constant>
+ <constant name="FLAG_DISABLE_Z" value="2" enum="Flags">
+ Present for consistency with 3D particle nodes, not used in 2D.
</constant>
- <constant name="FLAG_MAX" value="1" enum="Flags">
+ <constant name="FLAG_MAX" value="3" enum="Flags">
+ Represents the size of the [enum Flags] enum.
</constant>
<constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape">
+ All particles will be emitted from a single point.
</constant>
<constant name="EMISSION_SHAPE_CIRCLE" value="1" enum="EmissionShape">
+ Particles will be emitted on the perimeter of a circle.
</constant>
<constant name="EMISSION_SHAPE_RECTANGLE" value="2" enum="EmissionShape">
+ Particles will be emitted in the area of a rectangle.
</constant>
<constant name="EMISSION_SHAPE_POINTS" value="3" enum="EmissionShape">
+ Particles will be emitted at a position chosen randomly among [member emission_points]. Particle color will be modulated by [member emission_colors].
</constant>
<constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape">
+ Particles will be emitted at a position chosen randomly among [member emission_points]. Particle velocity and rotation will be set based on [member emission_normals]. Particle color will be modulated by [member emission_colors].
</constant>
</constants>
</class>
diff --git a/doc/classes/CameraFeed.xml b/doc/classes/CameraFeed.xml
new file mode 100644
index 0000000000..7f353f7a05
--- /dev/null
+++ b/doc/classes/CameraFeed.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CameraFeed" inherits="Reference" category="Core" version="3.2">
+ <brief_description>
+ A camera feed gives you access to a single physical camera attached to your device.
+ </brief_description>
+ <description>
+ A camera feed gives you access to a single physical camera attached to your device.
+ When enabled Godot will start capturing frames from the camera which can then be used. Do note that many cameras will return YCbCr images which are split into two textures and need to be combined in a shader. Godot does this automatically for you if you set the environment to show the camera image in the background.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Get unique id for this feed
+ </description>
+ </method>
+ <method name="get_name" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Get name of the camera
+ </description>
+ </method>
+ <method name="get_position" qualifiers="const">
+ <return type="int" enum="CameraFeed.FeedPosition">
+ </return>
+ <description>
+ Position of camera on the device.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="feed_is_active" type="bool" setter="set_active" getter="is_active">
+ </member>
+ <member name="feed_transform" type="Transform2D" setter="set_transform" getter="get_transform">
+ </member>
+ </members>
+ <constants>
+ <constant name="FEED_NOIMAGE" value="0" enum="FeedDataType">
+ No image set for the feed.
+ </constant>
+ <constant name="FEED_RGB" value="1" enum="FeedDataType">
+ Feed supplies RGB images.
+ </constant>
+ <constant name="FEED_YCbCr" value="2" enum="FeedDataType">
+ Feed supplies YCbCr images that need to be converted to RGB.
+ </constant>
+ <constant name="FEED_YCbCr_Sep" value="3" enum="FeedDataType">
+ Feed supplies separate Y and CbCr images that need to be combined and converted to RGB.
+ </constant>
+ <constant name="FEED_UNSPECIFIED" value="0" enum="FeedPosition">
+ Unspecified position.
+ </constant>
+ <constant name="FEED_FRONT" value="1" enum="FeedPosition">
+ Camera is mounted at the front of the device.
+ </constant>
+ <constant name="FEED_BACK" value="2" enum="FeedPosition">
+ Camera is moutned at the back of the device.
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/CameraServer.xml b/doc/classes/CameraServer.xml
new file mode 100644
index 0000000000..bc76c306fb
--- /dev/null
+++ b/doc/classes/CameraServer.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CameraServer" inherits="Object" category="Core" version="3.2">
+ <brief_description>
+ Our camera server keeps track of different cameras accessible in Godot. These are external cameras such as webcams or the cameras on your phone.
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_feed">
+ <return type="void">
+ </return>
+ <argument index="0" name="feed" type="CameraFeed">
+ </argument>
+ <description>
+ Adds a camera feed to the camera server.
+ </description>
+ </method>
+ <method name="feeds">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an array of [CameraFeed]s.
+ </description>
+ </method>
+ <method name="get_feed">
+ <return type="CameraFeed">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ Returns the [CameraFeed] with this id.
+ </description>
+ </method>
+ <method name="get_feed_count">
+ <return type="int">
+ </return>
+ <description>
+ Returns the number of [CameraFeed]s registered.
+ </description>
+ </method>
+ <method name="remove_feed">
+ <return type="void">
+ </return>
+ <argument index="0" name="feed" type="CameraFeed">
+ </argument>
+ <description>
+ Removes a [CameraFeed].
+ </description>
+ </method>
+ </methods>
+ <signals>
+ <signal name="camera_feed_added">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Emitted when a [CameraFeed] is added (webcam is plugged in).
+ </description>
+ </signal>
+ <signal name="camera_feed_removed">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Emitted when a [CameraFeed] is removed (webcam is removed).
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ <constant name="FEED_RGBA_IMAGE" value="0" enum="FeedImage">
+ The RGBA camera image.
+ </constant>
+ <constant name="FEED_YCbCr_IMAGE" value="0" enum="FeedImage">
+ The YCbCr camera image.
+ </constant>
+ <constant name="FEED_Y_IMAGE" value="0" enum="FeedImage">
+ The Y component camera image.
+ </constant>
+ <constant name="FEED_CbCr_IMAGE" value="1" enum="FeedImage">
+ The CbCr component camera image.
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/CameraTexture.xml b/doc/classes/CameraTexture.xml
new file mode 100644
index 0000000000..7477be7825
--- /dev/null
+++ b/doc/classes/CameraTexture.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CameraTexture" inherits="Texture" category="Core" version="3.2">
+ <brief_description>
+ This texture gives access to the camera texture provided by a [CameraFeed]. Note that many cameras supply YCbCr images which need to be converted in a shader.
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="camera_feed_id" type="int" setter="set_camera_feed_id" getter="get_camera_feed_id">
+ Id of the [CameraFeed] for which we want to display the image.
+ </member>
+ <member name="camera_is_active" type="bool" setter="set_camera_active" getter="get_camera_active">
+ Convenience property that gives access to the active property of the [CameraFeed].
+ </member>
+ <member name="which_feed" type="int" setter="set_which_feed" getter="get_which_feed" enum="CameraServer.FeedImage">
+ Which image within the [CameraFeed] we want access to, important if the camera image is split in a Y and CbCr component.
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/ClippedCamera.xml b/doc/classes/ClippedCamera.xml
index b7f158dd65..c6dcd6cd96 100644
--- a/doc/classes/ClippedCamera.xml
+++ b/doc/classes/ClippedCamera.xml
@@ -29,6 +29,12 @@
<description>
</description>
</method>
+ <method name="get_clip_offset" qualifiers="const">
+ <return type="float">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_collision_mask_bit" qualifiers="const">
<return type="bool">
</return>
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index ab5d7a0a5d..30e80fa512 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -144,7 +144,7 @@
<return type="Color">
</return>
<description>
- Returns the inverted color [code](1 - r, 1 - g, 1 - b, 1 - a)[/code].
+ Returns the inverted color [code](1 - r, 1 - g, 1 - b, a)[/code].
[codeblock]
var c = Color(0.3, 0.4, 0.9)
var inverted_color = c.inverted() # a color of an RGBA(178, 153, 26, 255)
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index ec9d5bc3dc..fea706987c 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -714,7 +714,7 @@
<member name="rect_clip_content" type="bool" setter="set_clip_contents" getter="is_clipping_contents">
Enables whether rendering of children should be clipped to this control's rectangle. If [code]true[/code], parts of a child which would be visibly outside of this control's rectangle will not be rendered.
</member>
- <member name="rect_global_position" type="Vector2" setter="set_global_position" getter="get_global_position">
+ <member name="rect_global_position" type="Vector2" setter="_set_global_position" getter="get_global_position">
The node's global position, relative to the world (usually to the top-left corner of the window).
</member>
<member name="rect_min_size" type="Vector2" setter="set_custom_minimum_size" getter="get_custom_minimum_size">
@@ -723,7 +723,7 @@
<member name="rect_pivot_offset" type="Vector2" setter="set_pivot_offset" getter="get_pivot_offset">
By default, the node's pivot is its top-left corner. When you change its [member rect_scale], it will scale around this pivot. Set this property to [member rect_size] / 2 to center the pivot in the node's rectangle.
</member>
- <member name="rect_position" type="Vector2" setter="set_position" getter="get_position">
+ <member name="rect_position" type="Vector2" setter="_set_position" getter="get_position">
The node's position, relative to its parent. It corresponds to the rectangle's top-left corner. The property is not affected by [member rect_pivot_offset].
</member>
<member name="rect_rotation" type="float" setter="set_rotation_degrees" getter="get_rotation_degrees">
@@ -732,7 +732,7 @@
<member name="rect_scale" type="Vector2" setter="set_scale" getter="get_scale">
The node's scale, relative to its [member rect_size]. Change this property to scale the node around its [member rect_pivot_offset].
</member>
- <member name="rect_size" type="Vector2" setter="set_size" getter="get_size">
+ <member name="rect_size" type="Vector2" setter="_set_size" getter="get_size">
The size of the node's bounding rectangle, in pixels. [Container] nodes update this property automatically.
</member>
<member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags">
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 97ad4f6829..3d91bff0aa 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -525,6 +525,10 @@
</constant>
<constant name="CONTAINER_PROPERTY_EDITOR_BOTTOM" value="9" enum="CustomControlContainer">
</constant>
+ <constant name="CONTAINER_PROJECT_SETTING_TAB_LEFT" value="10" enum="CustomControlContainer">
+ </constant>
+ <constant name="CONTAINER_PROJECT_SETTING_TAB_RIGHT" value="11" enum="CustomControlContainer">
+ </constant>
<constant name="DOCK_SLOT_LEFT_UL" value="0" enum="DockSlot">
</constant>
<constant name="DOCK_SLOT_LEFT_BL" value="1" enum="DockSlot">
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index 5f8dc552d7..eb94447529 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -57,6 +57,9 @@
<member name="auto_exposure_speed" type="float" setter="set_tonemap_auto_exposure_speed" getter="get_tonemap_auto_exposure_speed">
Speed of the auto exposure effect. Affects the time needed for the camera to perform auto exposure.
</member>
+ <member name="background_camera_feed_id" type="int" setter="set_camera_feed_id" getter="get_camera_feed_id">
+ The id of the camera feed to show in the background.
+ </member>
<member name="background_canvas_max_layer" type="int" setter="set_canvas_max_layer" getter="get_canvas_max_layer">
Maximum layer id (if using Layer background mode).
</member>
@@ -217,7 +220,7 @@
</member>
<member name="ssao_bias" type="float" setter="set_ssao_bias" getter="get_ssao_bias">
</member>
- <member name="ssao_blur" type="int" setter="set_ssao_blur" getter="is_ssao_blur_enabled" enum="Environment.SSAOBlur">
+ <member name="ssao_blur" type="int" setter="set_ssao_blur" getter="get_ssao_blur" enum="Environment.SSAOBlur">
</member>
<member name="ssao_color" type="Color" setter="set_ssao_color" getter="get_ssao_color">
</member>
@@ -266,7 +269,10 @@
<constant name="BG_CANVAS" value="4" enum="BGMode">
Display a [CanvasLayer] in the background.
</constant>
- <constant name="BG_MAX" value="6" enum="BGMode">
+ <constant name="BG_CAMERA_FEED" value="6" enum="BGMode">
+ Display a camera feed in the background.
+ </constant>
+ <constant name="BG_MAX" value="7" enum="BGMode">
Helper constant keeping track of the enum's size, has no direct usage in API calls.
</constant>
<constant name="GLOW_BLEND_MODE_ADDITIVE" value="0" enum="GlowBlendMode">
diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml
index 953f364bdb..908c017ac9 100644
--- a/doc/classes/FileDialog.xml
+++ b/doc/classes/FileDialog.xml
@@ -138,5 +138,7 @@
</theme_item>
<theme_item name="reload" type="Texture">
</theme_item>
+ <theme_item name="toggle_hidden" type="Texture">
+ </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 8dfabdd884..75434b031e 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -361,7 +361,7 @@
<return type="void">
</return>
<description>
- Locks the data for writing access.
+ Locks the data for reading and writing access. Sends an error to the console if the image is not locked when reading or writing a pixel.
</description>
</method>
<method name="normalmap_to_xy">
diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml
index 25e9073ec7..9df7bbca74 100644
--- a/doc/classes/InputEventAction.xml
+++ b/doc/classes/InputEventAction.xml
@@ -18,6 +18,9 @@
<member name="pressed" type="bool" setter="set_pressed" getter="is_pressed">
If [code]true[/code], the action's state is pressed. If [code]false[/code], the action's state is released.
</member>
+ <member name="strength" type="float" setter="set_strength" getter="get_strength">
+ The action's strength between 0 and 1. This value is consired as equal to 0 if pressed is [code]false[/code]. The event strength allows faking analog joypad motion events, by precising how strongly is the joypad axis bent or pressed.
+ </member>
</members>
<constants>
</constants>
diff --git a/doc/classes/InputEventKey.xml b/doc/classes/InputEventKey.xml
index 86a1362230..53b1f74bd4 100644
--- a/doc/classes/InputEventKey.xml
+++ b/doc/classes/InputEventKey.xml
@@ -29,7 +29,7 @@
Key scancode, one of the [enum KeyList] constants.
</member>
<member name="unicode" type="int" setter="set_unicode" getter="get_unicode">
- Key unicode identifier when relevant.
+ Key unicode identifier when relevant. Unicode identifiers for the composite characters and complex scripts may not be available unless IME input mode is active. See [method OS.set_ime_active] for more information.
</member>
</members>
<constants>
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index bb180b591d..184987d2dd 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -166,6 +166,7 @@
</constant>
<constant name="MENU_PASTE" value="2" enum="MenuItems">
Pastes the clipboard text over the selected text (or at the cursor's position).
+ Non-printable escape characters are automatically stripped from the OS clipboard via [method String.strip_escapes].
</constant>
<constant name="MENU_CLEAR" value="3" enum="MenuItems">
Erases the whole [LineEdit] text.
diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml
index b8bc5cbd11..048e7074f1 100644
--- a/doc/classes/Mesh.xml
+++ b/doc/classes/Mesh.xml
@@ -80,6 +80,17 @@
Returns a [Material] in a given surface. Surface is rendered using this material.
</description>
</method>
+ <method name="surface_set_material">
+ <return type="void">
+ </return>
+ <argument index="0" name="surf_idx" type="int">
+ </argument>
+ <argument index="1" name="material" type="Material">
+ </argument>
+ <description>
+ Set a [Material] for a given surface. Surface will be rendered using this material.
+ </description>
+ </method>
</methods>
<members>
<member name="lightmap_size_hint" type="Vector2" setter="set_lightmap_size_hint" getter="get_lightmap_size_hint">
diff --git a/doc/classes/MeshInstance.xml b/doc/classes/MeshInstance.xml
index c5c15e0ddc..f5868f51cb 100644
--- a/doc/classes/MeshInstance.xml
+++ b/doc/classes/MeshInstance.xml
@@ -43,6 +43,7 @@
<return type="int">
</return>
<description>
+ Returns the number of surface materials.
</description>
</method>
<method name="set_surface_material">
diff --git a/doc/classes/MeshInstance2D.xml b/doc/classes/MeshInstance2D.xml
index d18ba96a95..4b38b9aa96 100644
--- a/doc/classes/MeshInstance2D.xml
+++ b/doc/classes/MeshInstance2D.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="MeshInstance2D" inherits="Node2D" category="Core" version="3.2">
<brief_description>
+ Node used for displaying a [Mesh] in 2D.
</brief_description>
<description>
+ Node used for displaying a [Mesh] in 2D. Can be constructed from an existing [Sprite] use tool in Toolbar. Select "Sprite" then "Convert to Mesh2D", select settings in popup and press "Create Mesh2D".
</description>
<tutorials>
<link>http://docs.godotengine.org/en/latest/tutorials/2d/2d_meshes.html</link>
@@ -11,12 +13,21 @@
</methods>
<members>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
+ The [Mesh] that will be drawn by the [MeshInstance2D].
</member>
<member name="normal_map" type="Texture" setter="set_normal_map" getter="get_normal_map">
+ The normal map that will be used if using the default [CanvasItemMaterial].
</member>
<member name="texture" type="Texture" setter="set_texture" getter="get_texture">
+ The [Texture] that will be used if using the default [CanvasItemMaterial]. Can be accessed as [code]TEXTURE[/code] in CanvasItem shader.
</member>
</members>
+ <signals>
+ <signal name="texture_changed">
+ <description>
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/MultiMeshInstance2D.xml b/doc/classes/MultiMeshInstance2D.xml
new file mode 100644
index 0000000000..8509986c3c
--- /dev/null
+++ b/doc/classes/MultiMeshInstance2D.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiMeshInstance2D" inherits="Node2D" category="Core" version="3.2">
+ <brief_description>
+ Node that instances a [MultiMesh] in 2D.
+ </brief_description>
+ <description>
+ [MultiMeshInstance2D] is a specialized node to instance a [MultiMesh] resource in 2D.
+ Usage is the same as [MultiMeshInstance].
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="multimesh" type="MultiMesh" setter="set_multimesh" getter="get_multimesh">
+ The [MultiMesh] that will be drawn by the [MultiMeshInstance2D].
+ </member>
+ <member name="normal_map" type="Texture" setter="set_normal_map" getter="get_normal_map">
+ The normal map that will be used if using the default [CanvasItemMaterial].
+ </member>
+ <member name="texture" type="Texture" setter="set_texture" getter="get_texture">
+ The [Texture] that will be used if using the default [CanvasItemMaterial]. Can be accessed as [code]TEXTURE[/code] in CanvasItem shader.
+ </member>
+ </members>
+ <signals>
+ <signal name="texture_changed">
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/Navigation.xml b/doc/classes/Navigation.xml
index 3690c1d682..f0ae3fef99 100644
--- a/doc/classes/Navigation.xml
+++ b/doc/classes/Navigation.xml
@@ -59,7 +59,7 @@
<argument index="2" name="optimize" type="bool" default="true">
</argument>
<description>
- Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the agent properties associated with each [NavigationMesh] (raidus, height, etc.) are considered in the path calculation, otherwise they are ignored.
+ Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the agent properties associated with each [NavigationMesh] (radius, height, etc.) are considered in the path calculation, otherwise they are ignored.
</description>
</method>
<method name="navmesh_add">
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index dd0fcd63e7..e994c24582 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -221,14 +221,16 @@
<return type="Vector2">
</return>
<description>
- Returns IME selection range.
+ Returns IME cursor position (currently edited portion of the string) relative to the characters in the composition string.
+ [code]NOTIFICATION_OS_IME_UPDATE[/code] is sent to the application to notify it of changes to the IME cursor position.
</description>
</method>
<method name="get_ime_text" qualifiers="const">
<return type="String">
</return>
<description>
- Returns IME intermediate text.
+ Returns IME intermediate composition string.
+ [code]NOTIFICATION_OS_IME_UPDATE[/code] is sent to the application to notify it of changes to the IME composition string.
</description>
</method>
<method name="get_latin_keyboard_variant" qualifiers="const">
@@ -506,7 +508,8 @@
<argument index="0" name="tag_name" type="String">
</argument>
<description>
- Returns [code]true[/code] if the feature for the given feature tag is supported in the currently running instance, depending on platform, build etc. Can be used to check whether you're currently running a debug build, on a certain platform or arch, etc. See feature tags documentation.
+ Returns [code]true[/code] if the feature for the given feature tag is supported in the currently running instance, depending on platform, build etc. Can be used to check whether you're currently running a debug build, on a certain platform or arch, etc. Refer to the [url=https://docs.godotengine.org/en/latest/getting_started/workflow/export/feature_tags.html]Feature Tags[/url] documentation for more details.
+ Note that tag names are case-sensitive.
</description>
</method>
<method name="has_touchscreen_ui_hint" qualifiers="const">
@@ -710,6 +713,9 @@
</argument>
<description>
Sets whether IME input mode should be enabled.
+ If active IME handles key events before the application and creates an composition string and suggestion list.
+ Application can retrieve the composition status by using [method get_ime_selection] and [method get_ime_text] functions.
+ Completed composition string is committed when input is finished.
</description>
</method>
<method name="set_ime_position">
@@ -813,6 +819,7 @@
</member>
<member name="window_borderless" type="bool" setter="set_borderless_window" getter="get_borderless_window">
If [code]true[/code], removes the window frame.
+ Note: Setting [code]window_borderless[/code] to [code]false[/code] disables per-pixel transparency.
</member>
<member name="window_fullscreen" type="bool" setter="set_window_fullscreen" getter="is_window_fullscreen">
If [code]true[/code], the window is fullscreen.
@@ -824,6 +831,9 @@
If [code]true[/code], the window is minimized.
</member>
<member name="window_per_pixel_transparency_enabled" type="bool" setter="set_window_per_pixel_transparency_enabled" getter="get_window_per_pixel_transparency_enabled">
+ If [code]true[/code], the window background is transparent and window frame is removed.
+ Use [code]get_tree().get_root().set_transparent_background(true)[/code] to disable main viewport background rendering.
+ Note: This property has no effect if "Project &gt; Project Settings &gt; Display &gt; Window &gt; Per-pixel transparency &gt; Allowed" setting is disabled.
</member>
<member name="window_position" type="Vector2" setter="set_window_position" getter="get_window_position">
The window position relative to the screen, the origin is the top left corner, +Y axis goes to the bottom and +X axis goes to the right.
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index dbbd974b04..c9910360ed 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -4,12 +4,12 @@
Base class for all non built-in types.
</brief_description>
<description>
- Base class for all non built-in types. Everything which is not a built-in type starts the inheritance chain from this class.
- Objects can be constructed from scripting languages, using [code]Object.new()[/code] in GDScript, [code]new Object[/code] in C#, or the "Construct Object" node in VisualScript.
- Objects do not manage memory, if inheriting from one the object will most likely have to be deleted manually (call the [method free] function from the script or delete from C++).
- Some derivatives add memory management, such as [Reference] (which keeps a reference count and deletes itself automatically when no longer referenced) and [Node], which deletes the children tree when deleted.
+ Every class which is not a built-in type inherits from this class.
+ You can construct Objects from scripting languages, using [code]Object.new()[/code] in GDScript, [code]new Object[/code] in C#, or the "Construct Object" node in VisualScript.
+ Objects do not manage memory. If a class inherits from Object, you will have to delete instances of it manually. To do so, call the [method free] method from your script or delete the instance from C++.
+ Some classes that extend Object add memory management. This is the case of [Reference], which counts references and deletes itself automatically when no longer referenced. [Node], another fundamental type, deletes all its children when freed from memory.
Objects export properties, which are mainly useful for storage and editing, but not really so much in programming. Properties are exported in [method _get_property_list] and handled in [method _get] and [method _set]. However, scripting languages and C++ have simpler means to export them.
- Objects also receive notifications ([method _notification]). Notifications are a simple way to notify the object about simple events, so they can all be handled together.
+ Objects also receive notifications. Notifications are a simple way to notify the object about simple events, so they can all be handled together. See [method _notification].
</description>
<tutorials>
</tutorials>
@@ -126,7 +126,9 @@
<argument index="4" name="flags" type="int" default="0">
</argument>
<description>
- Connects a [code]signal[/code] to a [code]method[/code] on a [code]target[/code] object. Pass optional [code]binds[/code] to the call. Use [code]flags[/code] to set deferred or one shot connections. See [code]CONNECT_*[/code] constants. A [code]signal[/code] can only be connected once to a [code]method[/code]. It will throw an error if already connected. To avoid this, first use [method is_connected] to check for existing connections.
+ Connects a [code]signal[/code] to a [code]method[/code] on a [code]target[/code] object. Pass optional [code]binds[/code] to the call. Use [code]flags[/code] to set deferred or one shot connections. See [code]CONNECT_*[/code] constants.
+ A [code]signal[/code] can only be connected once to a [code]method[/code]. It will throw an error if already connected. To avoid this, first, use [method is_connected] to check for existing connections.
+ If the [code]target[/code] is destroyed in the game's lifecycle, the connection will be lost.
</description>
</method>
<method name="disconnect">
@@ -140,6 +142,7 @@
</argument>
<description>
Disconnects a [code]signal[/code] from a [code]method[/code] on the given [code]target[/code].
+ If you try to disconnect a connection that does not exist, the method will throw an error. Use [method is_connected] to ensure that the connection exists.
</description>
</method>
<method name="emit_signal" qualifiers="vararg">
diff --git a/doc/classes/Particles.xml b/doc/classes/Particles.xml
index 0023fce97d..a29b621406 100644
--- a/doc/classes/Particles.xml
+++ b/doc/classes/Particles.xml
@@ -15,13 +15,14 @@
<return type="AABB">
</return>
<description>
+ Returns the bounding box that contains all the particles that are active in the current frame.
</description>
</method>
<method name="restart">
<return type="void">
</return>
<description>
- Restarts the particle emmission, clearing existing particles.
+ Restarts the particle emission, clearing existing particles.
</description>
</method>
</methods>
@@ -54,8 +55,10 @@
Time ratio between each emission. If [code]0[/code] particles are emitted continuously. If [code]1[/code] all particles are emitted simultaneously. Default value: [code]0[/code].
</member>
<member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps">
+ The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself.
</member>
<member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta">
+ If [code]true[/code], results in fractional delta calculation which has a smoother particles display effect. Default value: [code]true[/code].
</member>
<member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime">
Amount of time each particle will exist. Default value: [code]1[/code].
diff --git a/doc/classes/Particles2D.xml b/doc/classes/Particles2D.xml
index de4877b639..78114e985d 100644
--- a/doc/classes/Particles2D.xml
+++ b/doc/classes/Particles2D.xml
@@ -40,7 +40,7 @@
How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins. Default value: [code]0[/code].
</member>
<member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps">
- The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the particle system itself.
+ The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself.
</member>
<member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta">
If [code]true[/code], results in fractional delta calculation which has a smoother particles display effect. Default value: [code]true[/code]
@@ -52,7 +52,7 @@
If [code]true[/code], particles use the parent node's coordinate space. If [code]false[/code], they use global coordinates. Default value: [code]true[/code].
</member>
<member name="normal_map" type="Texture" setter="set_normal_map" getter="get_normal_map">
- Normal map to be used for the [code]texture[/code] property.
+ Normal map to be used for the [member texture] property.
</member>
<member name="one_shot" type="bool" setter="set_one_shot" getter="get_one_shot">
If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end. Default value: [code]false[/code].
diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml
index 6ae5afeac5..cb06593bc2 100644
--- a/doc/classes/ParticlesMaterial.xml
+++ b/doc/classes/ParticlesMaterial.xml
@@ -100,7 +100,7 @@
Amount of [member spread] in Y/Z plane. A value of [code]1[/code] restricts particles to X/Z plane. Default [code]0[/code].
</member>
<member name="gravity" type="Vector3" setter="set_gravity" getter="get_gravity">
- Gravity applied to every particle. Default value: [code](0, 98, 0)[/code].
+ Gravity applied to every particle. Default value: [code](0, -9.8, 0)[/code].
</member>
<member name="hue_variation" type="float" setter="set_param" getter="get_param">
Initial hue variation applied to each particle.
@@ -112,13 +112,13 @@
Hue variation randomness ratio. Default value: [code]0[/code].
</member>
<member name="initial_velocity" type="float" setter="set_param" getter="get_param">
- Initial velocity magnitude for each particle. Direction comes from [member spread].
+ Initial velocity magnitude for each particle. Direction comes from [member spread] and the node's orientation.
</member>
<member name="initial_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
Initial velocity randomness ratio. Default value: [code]0[/code].
</member>
<member name="linear_accel" type="float" setter="set_param" getter="get_param">
- Linear acceleration applied to each particle. Acceleration increases velocity magnitude each frame without affecting direction.
+ Linear acceleration applied to each particle in the direction of motion.
</member>
<member name="linear_accel_curve" type="Texture" setter="set_param_texture" getter="get_param_texture">
Each particle's linear acceleration will vary along this [CurveTexture].
@@ -184,7 +184,7 @@
Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set angular velocity properties.
</constant>
<constant name="PARAM_ORBIT_VELOCITY" value="2" enum="Parameter">
- Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set orbital_velocity properties.
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set orbital velocity properties.
</constant>
<constant name="PARAM_LINEAR_ACCEL" value="3" enum="Parameter">
Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set linear acceleration properties.
@@ -205,7 +205,7 @@
Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set scale properties.
</constant>
<constant name="PARAM_HUE_VARIATION" value="9" enum="Parameter">
- Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set hue_variation properties.
+ Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set hue variation properties.
</constant>
<constant name="PARAM_ANIM_SPEED" value="10" enum="Parameter">
Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set animation speed properties.
@@ -214,14 +214,19 @@
Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set animation offset properties.
</constant>
<constant name="PARAM_MAX" value="12" enum="Parameter">
+ Represents the size of the [enum Parameter] enum.
</constant>
<constant name="FLAG_ALIGN_Y_TO_VELOCITY" value="0" enum="Flags">
Use with [method set_flag] to set [member flag_align_y].
</constant>
<constant name="FLAG_ROTATE_Y" value="1" enum="Flags">
- Use with [method set_flag] to set [member flag_rotate_y]
+ Use with [method set_flag] to set [member flag_rotate_y].
+ </constant>
+ <constant name="FLAG_DISABLE_Z" value="2" enum="Flags">
+ Use with [method set_flag] to set [member flag_disable_z].
</constant>
<constant name="FLAG_MAX" value="3" enum="Flags">
+ Represents the size of the [enum Flags] enum.
</constant>
<constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape">
All particles will be emitted from a single point.
diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml
index 7098f5f071..8f434e5499 100644
--- a/doc/classes/Polygon2D.xml
+++ b/doc/classes/Polygon2D.xml
@@ -109,7 +109,7 @@
<member name="texture_offset" type="Vector2" setter="set_texture_offset" getter="get_texture_offset">
Amount to offset the polygon's [code]texture[/code]. If [code](0, 0)[/code] the texture's origin (its top-left corner) will be placed at the polygon's [code]position[/code].
</member>
- <member name="texture_rotation" type="float" setter="set_texture_rotation_degrees" getter="get_texture_rotation_degrees">
+ <member name="texture_rotation" type="float" setter="set_texture_rotation" getter="get_texture_rotation">
The texture's rotation in radians.
</member>
<member name="texture_rotation_degrees" type="float" setter="set_texture_rotation_degrees" getter="get_texture_rotation_degrees">
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index d4f4834a66..d6249f73aa 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -515,6 +515,9 @@
</method>
</methods>
<members>
+ <member name="allow_search" type="bool" setter="set_allow_search" getter="get_allow_search">
+ If [code]true[/code], allows to navigate [PopupMenu] with letter keys. Default value: [code]false[/code].
+ </member>
<member name="hide_on_checkable_item_selection" type="bool" setter="set_hide_on_checkable_item_selection" getter="is_hide_on_checkable_item_selection">
</member>
<member name="hide_on_item_selection" type="bool" setter="set_hide_on_item_selection" getter="is_hide_on_item_selection">
diff --git a/doc/classes/Position2D.xml b/doc/classes/Position2D.xml
index 034e35d9d7..916bd99131 100644
--- a/doc/classes/Position2D.xml
+++ b/doc/classes/Position2D.xml
@@ -4,7 +4,7 @@
Generic 2D Position hint for editing.
</brief_description>
<description>
- Generic 2D Position hint for editing. It's just like a plain [Node2D] but displays as a cross in the 2D-Editor at all times.
+ Generic 2D Position hint for editing. It's just like a plain [Node2D] but displays as a cross in the 2D-Editor at all times. You can set visual size of the cross by changing Gizmo Extents in the inspector.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PrimitiveMesh.xml b/doc/classes/PrimitiveMesh.xml
index c9c3643edb..2214357308 100644
--- a/doc/classes/PrimitiveMesh.xml
+++ b/doc/classes/PrimitiveMesh.xml
@@ -4,7 +4,7 @@
Base class for all primitive meshes. Handles applying a [Material] to a primitive mesh.
</brief_description>
<description>
- Base class for all primitive meshes. Handles applying a [Material] to a primitive mesh.
+ Base class for all primitive meshes. Handles applying a [Material] to a primitive mesh. Examples include [CapsuleMesh], [CubeMesh], [CylinderMesh], [PlaneMesh], [PrismMesh], [QuadMesh], and [SphereMesh].
</description>
<tutorials>
</tutorials>
@@ -13,13 +13,16 @@
<return type="Array">
</return>
<description>
+ Returns mesh arrays used to constitute surface of [Mesh]. Mesh array can be used with [ArrayMesh] to create new surface.
</description>
</method>
</methods>
<members>
<member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb">
+ Overrides the [AABB] with one defined by user for use with frustum culling. Especially useful to avoid unnexpected culling when using a shader to offset vertices.
</member>
<member name="flip_faces" type="bool" setter="set_flip_faces" getter="get_flip_faces">
+ If set, the order of the vertices in each triangle are reversed resulting in the backside of the mesh being drawn. Result is the same as using *CULL_BACK* in [SpatialMaterial]. Default is false.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The current [Material] of the primitive mesh.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index ff8b702859..196fcbfaaa 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -374,11 +374,10 @@
Default orientation on mobile devices.
</member>
<member name="display/window/per_pixel_transparency/allowed" type="bool" setter="" getter="">
- If [code]true[/code], allows per-pixel transparency in a desktop window. This affects performance if not needed, so leave it on [code]false[/code] unless you need it.
+ If [code]true[/code], allows per-pixel transparency in a desktop window. This affects performance, so leave it on [code]false[/code] unless you need it.
</member>
<member name="display/window/per_pixel_transparency/enabled" type="bool" setter="" getter="">
- </member>
- <member name="display/window/per_pixel_transparency/splash" type="bool" setter="" getter="">
+ Set the window background to transparent when it starts.
</member>
<member name="display/window/size/always_on_top" type="bool" setter="" getter="">
Force the window to be always on top.
@@ -410,6 +409,8 @@
<member name="editor/active" type="bool" setter="" getter="">
Internal editor setting, don't touch.
</member>
+ <member name="editor/search_in_file_extensions" type="PoolStringArray" setter="" getter="">
+ </member>
<member name="gui/common/default_scroll_deadzone" type="int" setter="" getter="">
</member>
<member name="gui/common/swap_ok_cancel" type="bool" setter="" getter="">
@@ -721,7 +722,7 @@
Shaders have a time variable that constantly increases. At some point, it needs to be rolled back to zero to avoid precision errors on shader animations. This setting specifies when (in seconds).
</member>
<member name="rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround" type="bool" setter="" getter="">
- Some NVIDIA GPU drivers have a bug which produces flickering issues for the [code]draw_rect[/code] method, especially as used in [TileMap]. Refer to [url=https://github.com/godotengine/godot/issues/9913][/url] for details.
+ Some NVIDIA GPU drivers have a bug which produces flickering issues for the [code]draw_rect[/code] method, especially as used in [TileMap]. Refer to [url=https://github.com/godotengine/godot/issues/9913]GitHub issue 9913[/url] for details.
If [code]true[/code], this option enables a "safe" code path for such NVIDIA GPUs at the cost of performance. This option only impacts the GLES2 rendering backend (so the bug stays if you use GLES3), and only desktop platforms.
</member>
<member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter="">
diff --git a/doc/classes/QuadMesh.xml b/doc/classes/QuadMesh.xml
index 779ce11180..cf7e56f895 100644
--- a/doc/classes/QuadMesh.xml
+++ b/doc/classes/QuadMesh.xml
@@ -12,6 +12,7 @@
</methods>
<members>
<member name="size" type="Vector2" setter="set_size" getter="get_size">
+ Size in the X and Y axes. Default is [code]Vector2(1, 1)[/code].
</member>
</members>
<constants>
diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml
index 8b6fb549aa..211bda1a09 100644
--- a/doc/classes/Resource.xml
+++ b/doc/classes/Resource.xml
@@ -7,7 +7,7 @@
Resource is the base class for all resource types, serving primarily as data containers. They are reference counted and freed when no longer in use. They are also loaded only once from disk, and further attempts to load the resource will return the same reference (all this in contrast to a [Node], which is not reference counted and can be instanced from disk as many times as desired). Resources can be saved externally on disk or bundled into another object, such as a [Node] or another resource.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/stable/getting_started/step_by_step/resources.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/resources.html</link>
</tutorials>
<methods>
<method name="_setup_local_to_scene" qualifiers="virtual">
diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml
index 9ea6bb0fe1..8dadca49e1 100644
--- a/doc/classes/RigidBody.xml
+++ b/doc/classes/RigidBody.xml
@@ -119,10 +119,10 @@
Lock the body's movement in the x-axis.
</member>
<member name="axis_lock_linear_y" type="bool" setter="set_axis_lock" getter="get_axis_lock">
- Lock the body's movement in the x-axis.
+ Lock the body's movement in the y-axis.
</member>
<member name="axis_lock_linear_z" type="bool" setter="set_axis_lock" getter="get_axis_lock">
- Lock the body's movement in the x-axis.
+ Lock the body's movement in the z-axis.
</member>
<member name="bounce" type="float" setter="set_bounce" getter="get_bounce">
RigidBody's bounciness.
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index e06f0738b8..af7e5a395a 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -7,7 +7,7 @@
This is the built-in string class (and the one used by GDScript). It supports Unicode and provides all necessary means for string handling. Strings are reference counted and use a copy-on-write approach, so passing them around is cheap in resources.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/gdscript_format_string.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/gdscript_format_string.html</link>
</tutorials>
<methods>
<method name="String">
@@ -662,16 +662,17 @@
<method name="rsplit">
<return type="PoolStringArray">
</return>
- <argument index="0" name="divisor" type="String">
+ <argument index="0" name="delimiter" type="String">
</argument>
<argument index="1" name="allow_empty" type="bool" default="True">
</argument>
<argument index="2" name="maxsplit" type="int" default="0">
</argument>
<description>
- Splits the string by a [code]divisor[/code] string and returns an array of the substrings, starting from right.
- [b]Example:[/b] [code]"One,Two,Three"[/code] will return [code]["One","Two","Three"][/code] if split by [code]","[/code].
- If [code]maxsplit[/code] is specified, then it is number of splits to do, default is 0 which splits all the items.
+ Splits the string by a [code]delimiter[/code] string and returns an array of the substrings, starting from right.
+ The splits in the returned array are sorted in the same order as the original string, from left to right.
+ If [code]maxsplit[/code] is specified, it defines the number of splits to do from the right up to [code]maxsplit[/code]. The default value of 0 means that all items are split, thus giving the same result as [method split].
+ [b]Example:[/b] [code]"One,Two,Three,Four"[/code] will return [code]["Three","Four"][/code] if split by [code]","[/code] with [code]maxsplit[/code] of 2.
</description>
</method>
<method name="rstrip">
@@ -709,27 +710,27 @@
<method name="split">
<return type="PoolStringArray">
</return>
- <argument index="0" name="divisor" type="String">
+ <argument index="0" name="delimiter" type="String">
</argument>
<argument index="1" name="allow_empty" type="bool" default="True">
</argument>
<argument index="2" name="maxsplit" type="int" default="0">
</argument>
<description>
- Splits the string by a divisor string and returns an array of the substrings.
- [b]Example:[/b] [code]"One,Two,Three"[/code] will return [code]["One","Two","Three"][/code] if split by [code]","[/code].
- If [code]maxsplit[/code] is given, at most maxsplit number of splits occur, and the remainder of the string is returned as the final element of the list (thus, the list will have at most maxsplit+1 elements)
+ Splits the string by a [code]delimiter[/code] string and returns an array of the substrings.
+ If [code]maxsplit[/code] is specified, it defines the number of splits to do from the left up to [code]maxsplit[/code]. The default value of 0 means that all items are split.
+ [b]Example:[/b] [code]"One,Two,Three"[/code] will return [code]["One","Two"][/code] if split by [code]","[/code] with [code]maxsplit[/code] of 2.
</description>
</method>
<method name="split_floats">
<return type="PoolRealArray">
</return>
- <argument index="0" name="divisor" type="String">
+ <argument index="0" name="delimiter" type="String">
</argument>
<argument index="1" name="allow_empty" type="bool" default="True">
</argument>
<description>
- Splits the string in floats by using a divisor string and returns an array of the substrings.
+ Splits the string in floats by using a delimiter string and returns an array of the substrings.
[b]Example:[/b] [code]"1,2.5,3"[/code] will return [code][1,2.5,3][/code] if split by [code]","[/code].
</description>
</method>
@@ -741,7 +742,14 @@
<argument index="1" name="right" type="bool" default="True">
</argument>
<description>
- Returns a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively.
+ Returns a copy of the string stripped of any non-printable character (including tabulations, spaces and line breaks) at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively.
+ </description>
+ </method>
+ <method name="strip_escapes">
+ <return type="String">
+ </return>
+ <description>
+ Returns a copy of the string stripped of any escape character. These include all non-printable control characters of the first page of the ASCII table (&lt; 32), such as tabulation ([code]\t[/code] in C) and newline ([code]\n[/code] and [code]\r[/code]) characters, but not spaces.
</description>
</method>
<method name="substr">
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index eb70d7a1c3..8ca553ccb8 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -388,10 +388,10 @@
<member name="draw_tabs" type="bool" setter="set_draw_tabs" getter="is_drawing_tabs">
If [code]true[/code], the "tab" character will have a visible representation.
</member>
- <member name="fold_gutter" type="bool" setter="set_fold_gutter_enabled" getter="is_fold_gutter_enabled">
+ <member name="fold_gutter" type="bool" setter="set_draw_fold_gutter" getter="is_drawing_fold_gutter">
If [code]true[/code], the fold gutter is visible. This enables folding groups of indented lines.
</member>
- <member name="hiding_enabled" type="int" setter="set_hiding_enabled" getter="is_hiding_enabled">
+ <member name="hiding_enabled" type="bool" setter="set_hiding_enabled" getter="is_hiding_enabled">
</member>
<member name="highlight_all_occurrences" type="bool" setter="set_highlight_all_occurrences" getter="is_highlight_all_occurrences_enabled">
</member>
diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml
index c14e098b12..8fdef5540b 100644
--- a/doc/classes/Theme.xml
+++ b/doc/classes/Theme.xml
@@ -8,7 +8,7 @@
Theme resources can be alternatively loaded by writing them in a .theme file, see docs for more info.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/stable/tutorials/gui/gui_skinning.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/gui/gui_skinning.html</link>
</tutorials>
<methods>
<method name="clear">
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 0bf602dcf7..f29a7990ed 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -10,6 +10,18 @@
<tutorials>
</tutorials>
<methods>
+ <method name="_forward_atlas_subtile_selection" qualifiers="virtual">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="atlastile_id" type="int">
+ </argument>
+ <argument index="1" name="tilemap" type="Object">
+ </argument>
+ <argument index="2" name="tile_location" type="Vector2">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="_forward_subtile_selection" qualifiers="virtual">
<return type="Vector2">
</return>
diff --git a/doc/classes/Translation.xml b/doc/classes/Translation.xml
index 5d51f7ee09..eea4f8ed03 100644
--- a/doc/classes/Translation.xml
+++ b/doc/classes/Translation.xml
@@ -7,8 +7,8 @@
Translations are resources that can be loaded/unloaded on demand. They map a string to another string.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html</link>
- <link>https://docs.godotengine.org/en/stable/tutorials/i18n/locales.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/i18n/internationalizing_games.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/i18n/locales.html</link>
</tutorials>
<methods>
<method name="add_message">
diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml
index 05774635b8..400a6e31d5 100644
--- a/doc/classes/TranslationServer.xml
+++ b/doc/classes/TranslationServer.xml
@@ -7,8 +7,8 @@
Server that manages all translations. Translations can be set to it and removed from it.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html</link>
- <link>https://docs.godotengine.org/en/stable/tutorials/i18n/locales.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/i18n/internationalizing_games.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/i18n/locales.html</link>
</tutorials>
<methods>
<method name="add_translation">
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 9524e048bb..f7887a87c8 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -163,8 +163,11 @@
<method name="get_next_visible">
<return type="TreeItem">
</return>
+ <argument index="0" name="wrap" type="bool" default="false">
+ </argument>
<description>
Returns the next visible TreeItem in the tree.
+ If [code]wrap[/code] is enabled, the method will wrap around to the first visible element in the tree when called on the last visible element, otherwise it returns [code]null[/code].
</description>
</method>
<method name="get_parent">
@@ -184,8 +187,11 @@
<method name="get_prev_visible">
<return type="TreeItem">
</return>
+ <argument index="0" name="wrap" type="bool" default="false">
+ </argument>
<description>
Returns the previous visible TreeItem in the tree.
+ If [code]wrap[/code] is enabled, the method will wrap around to the last visible element in the tree when called on the first visible element, otherwise it returns [code]null[/code].
</description>
</method>
<method name="get_range" qualifiers="const">
diff --git a/doc/classes/TriangleMesh.xml b/doc/classes/TriangleMesh.xml
index 9600a1d196..2125aa5e17 100644
--- a/doc/classes/TriangleMesh.xml
+++ b/doc/classes/TriangleMesh.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TriangleMesh" inherits="Reference" category="Core" version="3.2">
<brief_description>
+ Internal mesh type.
</brief_description>
<description>
+ Mesh type used internally for collision calculations.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index 75221ccc82..243dbceced 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -185,6 +185,17 @@
Returns the result of the linear interpolation between this vector and [code]b[/code] by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation.
</description>
</method>
+ <method name="move_toward">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="to" type="Vector2">
+ </argument>
+ <argument index="1" name="delta" type="float">
+ </argument>
+ <description>
+ Moves the vector toward [code]to[/code] by the fixed [code]delta[/code] amount.
+ </description>
+ </method>
<method name="normalized">
<return type="Vector2">
</return>
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index bcd745945e..99bf3d2610 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -175,6 +175,17 @@
Returns the axis of the vector's smallest value. See [code]AXIS_*[/code] constants.
</description>
</method>
+ <method name="move_toward">
+ <return type="Vector3">
+ </return>
+ <argument index="0" name="to" type="Vector3">
+ </argument>
+ <argument index="1" name="delta" type="float">
+ </argument>
+ <description>
+ Moves the vector toward [code]to[/code] by the fixed [code]delta[/code] amount.
+ </description>
+ </method>
<method name="normalized">
<return type="Vector3">
</return>
diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml
index c3b668c170..0bc0e351e4 100644
--- a/doc/classes/VehicleWheel.xml
+++ b/doc/classes/VehicleWheel.xml
@@ -19,7 +19,7 @@
<return type="float">
</return>
<description>
- Returns a value between 0.0 and 1.0 that indicates whether this wheel is skidding. 0.0 is not skidding, 1.0 means the wheel has lost grip.
+ Returns a value between 0.0 and 1.0 that indicates whether this wheel is skidding. 0.0 is skidding (the wheel has lost grip, e.g. icy terrain), 1.0 means not skidding (the wheel has full grip, e.g. dry asphalt road).
</description>
</method>
<method name="is_in_contact" qualifiers="const">
diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml
index 3997798cca..d4ca1194d2 100644
--- a/doc/classes/VisualServer.xml
+++ b/doc/classes/VisualServer.xml
@@ -3359,6 +3359,17 @@
<description>
</description>
</method>
+ <method name="texture_bind">
+ <return type="void">
+ </return>
+ <argument index="0" name="texture" type="RID">
+ </argument>
+ <argument index="1" name="number" type="int">
+ </argument>
+ <description>
+ Binds the texture to a texture slot.
+ </description>
+ </method>
<method name="texture_create">
<return type="RID">
</return>
@@ -4427,7 +4438,7 @@
</constant>
<constant name="ENV_BG_KEEP" value="5" enum="EnvironmentBG">
</constant>
- <constant name="ENV_BG_MAX" value="6" enum="EnvironmentBG">
+ <constant name="ENV_BG_MAX" value="7" enum="EnvironmentBG">
</constant>
<constant name="ENV_DOF_BLUR_QUALITY_LOW" value="0" enum="EnvironmentDOFBlurQuality">
</constant>
diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py
index c3e15b2f9a..454c71d3c7 100755
--- a/doc/tools/makerst.py
+++ b/doc/tools/makerst.py
@@ -478,24 +478,7 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
f.write(make_heading('Tutorials', '-'))
for t in class_def.tutorials:
link = t.strip()
- match = GODOT_DOCS_PATTERN.search(link)
- if match:
- groups = match.groups()
- if match.lastindex == 2:
- # Doc reference with fragment identifier: emit direct link to section with reference to page, for example:
- # `#calling-javascript-from-script in Exporting For Web`
- f.write("- `" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n\n")
- # Commented out alternative: Instead just emit:
- # `Subsection in Exporting For Web`
- # f.write("- `Subsection <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n\n")
- elif match.lastindex == 1:
- # Doc reference, for example:
- # `Math`
- f.write("- :doc:`../" + groups[0] + "`\n\n")
- else:
- # External link, for example:
- # `http://enet.bespin.org/usergroup0.html`
- f.write("- `" + link + " <" + link + ">`_\n\n")
+ f.write("- " + make_url(link) + "\n\n")
# Property descriptions
if len(class_def.properties) > 0:
@@ -802,15 +785,16 @@ def rstize_text(text, state): # type: (str, State) -> str
tag_text = "" # '![](' + cmd[6:] + ')'
elif cmd.find('url=') == 0:
url_link = cmd[4:]
- tag_text = ':ref:`'
+ tag_text = '`'
tag_depth += 1
- url_has_name = False
inside_url = True
+ url_has_name = False
elif cmd == '/url':
- tag_text = ('' if url_has_name else url_link) + '<' + url_link + ">`"
+ tag_text = ('' if url_has_name else url_link) + " <" + url_link + ">`_"
tag_depth -= 1
escape_post = True
inside_url = False
+ url_has_name = False
elif cmd == 'center':
tag_depth += 1
tag_text = ''
@@ -996,5 +980,26 @@ def make_heading(title, underline): # type: (str, str) -> str
return title + '\n' + (underline * len(title)) + "\n\n"
+def make_url(link): # type: (str) -> str
+ match = GODOT_DOCS_PATTERN.search(link)
+ if match:
+ groups = match.groups()
+ if match.lastindex == 2:
+ # Doc reference with fragment identifier: emit direct link to section with reference to page, for example:
+ # `#calling-javascript-from-script in Exporting For Web`
+ return "`" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`"
+ # Commented out alternative: Instead just emit:
+ # `Subsection in Exporting For Web`
+ # return "`Subsection <../" + groups[0] + ".html" + groups[1] + ">`__ in :doc:`../" + groups[0] + "`"
+ elif match.lastindex == 1:
+ # Doc reference, for example:
+ # `Math`
+ return ":doc:`../" + groups[0] + "`"
+ else:
+ # External link, for example:
+ # `http://enet.bespin.org/usergroup0.html`
+ return "`" + link + " <" + link + ">`_"
+
+
if __name__ == '__main__':
main()
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
index 253936ca34..6f91fc4949 100644
--- a/drivers/dummy/rasterizer_dummy.h
+++ b/drivers/dummy/rasterizer_dummy.h
@@ -61,6 +61,7 @@ public:
void environment_set_bg_energy(RID p_env, float p_energy) {}
void environment_set_canvas_max_layer(RID p_env, int p_max_layer) {}
void environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy = 1.0, float p_sky_contribution = 0.0) {}
+ void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id){};
void environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_far_amount, VS::EnvironmentDOFBlurQuality p_quality) {}
void environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_far_amount, VS::EnvironmentDOFBlurQuality p_quality) {}
@@ -216,6 +217,7 @@ public:
uint32_t texture_get_height(RID p_texture) const { return 0; }
uint32_t texture_get_depth(RID p_texture) const { return 0; }
void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth_3d) {}
+ void texture_bind(RID p_texture, uint32_t p_texture_no) {}
void texture_set_path(RID p_texture, const String &p_path) {
DummyTexture *t = texture_owner.getornull(p_texture);
diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp
index ee722e9d6c..b82186162d 100644
--- a/drivers/gles2/rasterizer_canvas_gles2.cpp
+++ b/drivers/gles2/rasterizer_canvas_gles2.cpp
@@ -1081,6 +1081,8 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
} else {
glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 3, buffer + color_ofs);
}
+ } else {
+ glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 3, 1.0, 1.0, 1.0, 1.0);
}
if (multi_mesh->custom_data_floats) {
diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp
index 50cb39b13f..a07c6832dc 100644
--- a/drivers/gles2/rasterizer_scene_gles2.cpp
+++ b/drivers/gles2/rasterizer_scene_gles2.cpp
@@ -36,6 +36,7 @@
#include "core/project_settings.h"
#include "core/vmap.h"
#include "rasterizer_canvas_gles2.h"
+#include "servers/camera/camera_feed.h"
#include "servers/visual/visual_server_raster.h"
#ifndef GLES_OVER_GL
@@ -769,6 +770,13 @@ void RasterizerSceneGLES2::environment_set_ambient_light(RID p_env, const Color
env->ambient_sky_contribution = p_sky_contribution;
}
+void RasterizerSceneGLES2::environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) {
+ Environment *env = environment_owner.getornull(p_env);
+ ERR_FAIL_COND(!env);
+
+ env->camera_feed_id = p_camera_feed_id;
+}
+
void RasterizerSceneGLES2::environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
@@ -2261,7 +2269,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
bool rebind_reflection = false;
bool rebind_lightmap = false;
- if (!p_shadow) {
+ if (!p_shadow && material->shader) {
bool unshaded = material->shader->spatial.unshaded;
@@ -2281,7 +2289,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
bool depth_prepass = false;
- if (!p_alpha_pass && material->shader && material->shader->spatial.depth_draw_mode == RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) {
+ if (!p_alpha_pass && material->shader->spatial.depth_draw_mode == RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) {
depth_prepass = true;
}
@@ -2843,6 +2851,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
// clear color
Color clear_color(0, 0, 0, 1);
+ Ref<CameraFeed> feed;
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
clear_color = Color(0, 0, 0, 0);
@@ -2855,6 +2864,9 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
} else if (env->bg_mode == VS::ENV_BG_CANVAS || env->bg_mode == VS::ENV_BG_COLOR || env->bg_mode == VS::ENV_BG_COLOR_SKY) {
clear_color = env->bg_color;
storage->frame.clear_request = false;
+ } else if (env->bg_mode == VS::ENV_BG_CAMERA_FEED) {
+ feed = CameraServer::get_singleton()->get_feed_by_id(env->camera_feed_id);
+ storage->frame.clear_request = false;
} else {
storage->frame.clear_request = false;
}
@@ -2891,7 +2903,66 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
env_radiance_tex = sky->radiance;
}
} break;
+ case VS::ENV_BG_CAMERA_FEED: {
+ if (feed.is_valid() && (feed->get_base_width() > 0) && (feed->get_base_height() > 0)) {
+ // copy our camera feed to our background
+
+ glDisable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_NO_ALPHA, true);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_DISPLAY_TRANSFORM, true);
+
+ if (feed->get_datatype() == CameraFeed::FEED_RGB) {
+ RID camera_RGBA = feed->get_texture(CameraServer::FEED_RGBA_IMAGE);
+
+ VS::get_singleton()->texture_bind(camera_RGBA, 0);
+
+ } else if (feed->get_datatype() == CameraFeed::FEED_YCbCr) {
+ RID camera_YCbCr = feed->get_texture(CameraServer::FEED_YCbCr_IMAGE);
+
+ VS::get_singleton()->texture_bind(camera_YCbCr, 0);
+
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::YCBCR_TO_RGB, true);
+
+ } else if (feed->get_datatype() == CameraFeed::FEED_YCbCr_Sep) {
+ RID camera_Y = feed->get_texture(CameraServer::FEED_Y_IMAGE);
+ RID camera_CbCr = feed->get_texture(CameraServer::FEED_CbCr_IMAGE);
+
+ VS::get_singleton()->texture_bind(camera_Y, 0);
+ VS::get_singleton()->texture_bind(camera_CbCr, 1);
+
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::SEP_CBCR_TEXTURE, true);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::YCBCR_TO_RGB, true);
+ };
+
+ storage->shaders.copy.bind();
+ storage->shaders.copy.set_uniform(CopyShaderGLES2::DISPLAY_TRANSFORM, feed->get_transform());
+
+ storage->bind_quad_array();
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableVertexAttribArray(VS::ARRAY_VERTEX);
+ glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // turn off everything used
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::SEP_CBCR_TEXTURE, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::YCBCR_TO_RGB, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_NO_ALPHA, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_DISPLAY_TRANSFORM, false);
+ //restore
+ glEnable(GL_BLEND);
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ } else {
+ // don't have a feed, just show greenscreen :)
+ clear_color = Color(0.0, 1.0, 0.0, 1.0);
+ }
+ } break;
default: {
// FIXME: implement other background modes
} break;
@@ -2919,7 +2990,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
if (storage->frame.current_rt && state.used_screen_texture) {
//copy screen texture
- if (storage->frame.current_rt && storage->frame.current_rt->multisample_active) {
+ if (storage->frame.current_rt->multisample_active) {
// Resolve framebuffer to front buffer before copying
#ifdef GLES_OVER_GL
@@ -2931,14 +3002,16 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
#elif IPHONE_ENABLED
+
glBindFramebuffer(GL_READ_FRAMEBUFFER, storage->frame.current_rt->multisample_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, storage->frame.current_rt->fbo);
glResolveMultisampleFramebufferAPPLE();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-#else
- // In GLES2 Blit is not available, so just copy color texture manually
+#elif ANDROID_ENABLED
+
+ // In GLES2 AndroidBlit is not available, so just copy color texture manually
_copy_texture_to_front_buffer(storage->frame.current_rt->multisample_color);
#endif
}
@@ -2972,8 +3045,17 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-#else
- // In GLES2 Blit is not available, so just copy color texture manually
+#elif IPHONE_ENABLED
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, storage->frame.current_rt->multisample_fbo);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, storage->frame.current_rt->fbo);
+ glResolveMultisampleFramebufferAPPLE();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+#elif ANDROID_ENABLED
+
+ // In GLES2 Android Blit is not available, so just copy color texture manually
_copy_texture_to_front_buffer(storage->frame.current_rt->multisample_color);
#endif
}
diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h
index d5a691d0b9..c95385eb24 100644
--- a/drivers/gles2/rasterizer_scene_gles2.h
+++ b/drivers/gles2/rasterizer_scene_gles2.h
@@ -354,6 +354,8 @@ public:
float bg_energy;
float sky_ambient;
+ int camera_feed_id;
+
Color ambient_color;
float ambient_energy;
float ambient_sky_contribution;
@@ -381,6 +383,7 @@ public:
sky_custom_fov(0.0),
bg_energy(1.0),
sky_ambient(0),
+ camera_feed_id(0),
ambient_energy(1.0),
ambient_sky_contribution(0.0),
canvas_max_layer(0),
@@ -413,6 +416,7 @@ public:
virtual void environment_set_bg_energy(RID p_env, float p_energy);
virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer);
virtual void environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy = 1.0, float p_sky_contribution = 0.0);
+ virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id);
virtual void environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality);
virtual void environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality);
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index c610f31bc1..70f1173a7d 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -91,7 +91,7 @@ GLuint RasterizerStorageGLES2::system_fbo = 0;
//void *glRenderbufferStorageMultisampleAPPLE;
//void *glResolveMultisampleFramebufferAPPLE;
#define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleAPPLE
-#else
+#elif ANDROID_ENABLED
#include <GLES2/gl2ext.h>
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT;
@@ -968,6 +968,15 @@ uint32_t RasterizerStorageGLES2::texture_get_texid(RID p_texture) const {
return texture->tex_id;
}
+void RasterizerStorageGLES2::texture_bind(RID p_texture, uint32_t p_texture_no) {
+ Texture *texture = texture_owner.getornull(p_texture);
+
+ ERR_FAIL_COND(!texture);
+
+ glActiveTexture(GL_TEXTURE0 + p_texture_no);
+ glBindTexture(texture->target, texture->tex_id);
+}
+
uint32_t RasterizerStorageGLES2::texture_get_width(RID p_texture) const {
Texture *texture = texture_owner.getornull(p_texture);
@@ -4688,6 +4697,7 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) {
/* BACK FBO */
/* For MSAA */
+#ifndef JAVASCRIPT_ENABLED
if (rt->msaa != VS::VIEWPORT_MSAA_DISABLED && config.multisample_supported) {
rt->multisample_active = true;
@@ -4719,7 +4729,7 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) {
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, color_internal_format, rt->width, rt->height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rt->multisample_color);
-#else
+#elif ANDROID_ENABLED
// Render to a texture in android
glGenTextures(1, &rt->multisample_color);
glBindTexture(GL_TEXTURE_2D, rt->multisample_color);
@@ -4743,7 +4753,9 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) {
glBindRenderbuffer(GL_RENDERBUFFER, 0);
- } else {
+ } else
+#endif
+ {
rt->multisample_active = false;
}
@@ -5602,11 +5614,11 @@ void RasterizerStorageGLES2::initialize() {
//Manually load extensions for android and ios
#ifdef IPHONE_ENABLED
-
+ // appears that IPhone doesn't need to dlopen TODO: test this rigorously before removing
//void *gles2_lib = dlopen(NULL, RTLD_LAZY);
//glRenderbufferStorageMultisampleAPPLE = dlsym(gles2_lib, "glRenderbufferStorageMultisampleAPPLE");
//glResolveMultisampleFramebufferAPPLE = dlsym(gles2_lib, "glResolveMultisampleFramebufferAPPLE");
-#else
+#elif ANDROID_ENABLED
void *gles2_lib = dlopen("libGLESv2.so", RTLD_LAZY);
glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)dlsym(gles2_lib, "glRenderbufferStorageMultisampleEXT");
diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h
index af57aa3d9b..d139697b86 100644
--- a/drivers/gles2/rasterizer_storage_gles2.h
+++ b/drivers/gles2/rasterizer_storage_gles2.h
@@ -350,6 +350,7 @@ public:
virtual uint32_t texture_get_height(RID p_texture) const;
virtual uint32_t texture_get_depth(RID p_texture) const;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth);
+ virtual void texture_bind(RID p_texture, uint32_t p_texture_no);
virtual void texture_set_path(RID p_texture, const String &p_path);
virtual String texture_get_path(RID p_texture) const;
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp
index 9778d39a21..b48b93944c 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -361,6 +361,21 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
fragment_global += final_code;
}
+ // constants
+
+ for (Map<StringName, SL::ShaderNode::Constant>::Element *E = snode->constants.front(); E; E = E->next()) {
+ String gcode;
+ gcode += "const ";
+ gcode += _prestr(E->get().precision);
+ gcode += _typestr(E->get().type);
+ gcode += " " + _mkid(E->key());
+ gcode += "=";
+ gcode += _dump_node_code(E->get().initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ gcode += ";\n";
+ vertex_global += gcode;
+ fragment_global += gcode;
+ }
+
// functions
Map<StringName, String> function_code;
diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h
index ebea40e10e..2456a83d35 100644
--- a/drivers/gles2/shader_gles2.h
+++ b/drivers/gles2/shader_gles2.h
@@ -1,4 +1,4 @@
-/*************************************************************************/
+/*************************************************************************/
/* shader_gles2.h */
/*************************************************************************/
/* This file is part of: */
diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl
index 7dce784f52..0818942b0a 100644
--- a/drivers/gles2/shaders/canvas.glsl
+++ b/drivers/gles2/shaders/canvas.glsl
@@ -112,7 +112,12 @@ void main() {
#ifdef USE_INSTANCING
mat4 extra_matrix_instance = extra_matrix * transpose(mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0)));
color *= instance_color;
+
+#ifdef USE_INSTANCE_CUSTOM
vec4 instance_custom = instance_custom_data;
+#else
+ vec4 instance_custom = vec4(0.0);
+#endif
#else
mat4 extra_matrix_instance = extra_matrix;
diff --git a/drivers/gles2/shaders/copy.glsl b/drivers/gles2/shaders/copy.glsl
index 931b3f3708..195db7c45f 100644
--- a/drivers/gles2/shaders/copy.glsl
+++ b/drivers/gles2/shaders/copy.glsl
@@ -28,8 +28,15 @@ varying vec2 uv_interp;
#endif
varying vec2 uv2_interp;
+// These definitions are here because the shader-wrapper builder does
+// not understand `#elif defined()`
+#ifdef USE_DISPLAY_TRANSFORM
+#endif
+
#ifdef USE_COPY_SECTION
uniform highp vec4 copy_section;
+#elif defined(USE_DISPLAY_TRANSFORM)
+uniform highp mat4 display_transform;
#endif
void main() {
@@ -48,6 +55,8 @@ void main() {
#ifdef USE_COPY_SECTION
uv_interp = copy_section.xy + uv_interp * copy_section.zw;
gl_Position.xy = (copy_section.xy + (gl_Position.xy * 0.5 + 0.5) * copy_section.zw) * 2.0 - 1.0;
+#elif defined(USE_DISPLAY_TRANSFORM)
+ uv_interp = (display_transform * vec4(uv_in, 1.0, 1.0)).xy;
#endif
}
@@ -88,6 +97,10 @@ uniform samplerCube source_cube; // texunit:0
uniform sampler2D source; // texunit:0
#endif
+#ifdef SEP_CBCR_TEXTURE
+uniform sampler2D CbCr; //texunit:1
+#endif
+
varying vec2 uv2_interp;
#ifdef USE_MULTIPLIER
@@ -145,10 +158,26 @@ void main() {
#elif defined(USE_CUBEMAP)
vec4 color = textureCube(source_cube, normalize(cube_interp));
+#elif defined(SEP_CBCR_TEXTURE)
+ vec4 color;
+ color.r = texture2D(source, uv_interp).r;
+ color.gb = texture2D(CbCr, uv_interp).rg - vec2(0.5, 0.5);
+ color.a = 1.0;
#else
vec4 color = texture2D(source, uv_interp);
#endif
+#ifdef YCBCR_TO_RGB
+ // YCbCr -> RGB conversion
+
+ // Using BT.601, which is the standard for SDTV is provided as a reference
+ color.rgb = mat3(
+ vec3(1.00000, 1.00000, 1.00000),
+ vec3(0.00000, -0.34413, 1.77200),
+ vec3(1.40200, -0.71414, 0.00000)) *
+ color.rgb;
+#endif
+
#ifdef USE_NO_ALPHA
color.a = 1.0;
#endif
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 4552fddfe8..7acb8a22bc 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -34,6 +34,7 @@
#include "core/os/os.h"
#include "core/project_settings.h"
#include "rasterizer_canvas_gles3.h"
+#include "servers/camera/camera_feed.h"
#include "servers/visual/visual_server_raster.h"
#ifndef GLES_OVER_GL
@@ -830,6 +831,12 @@ void RasterizerSceneGLES3::environment_set_ambient_light(RID p_env, const Color
env->ambient_energy = p_energy;
env->ambient_sky_contribution = p_sky_contribution;
}
+void RasterizerSceneGLES3::environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) {
+ Environment *env = environment_owner.getornull(p_env);
+ ERR_FAIL_COND(!env);
+
+ env->camera_feed_id = p_camera_feed_id;
+}
void RasterizerSceneGLES3::environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) {
@@ -3304,7 +3311,7 @@ void RasterizerSceneGLES3::_prepare_depth_texture() {
void RasterizerSceneGLES3::_bind_depth_texture() {
if (!state.bound_depth_texture) {
- ERR_FAIL_COND(!state.prepared_depth_texture)
+ ERR_FAIL_COND(!state.prepared_depth_texture);
//bind depth for read
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 8);
glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->depth);
@@ -4342,6 +4349,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
Color clear_color(0, 0, 0, 0);
RasterizerStorageGLES3::Sky *sky = NULL;
+ Ref<CameraFeed> feed;
GLuint env_radiance_tex = 0;
if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
@@ -4376,6 +4384,9 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
clear_color = env->bg_color.to_linear();
storage->frame.clear_request = false;
+ } else if (env->bg_mode == VS::ENV_BG_CAMERA_FEED) {
+ feed = CameraServer::get_singleton()->get_feed_by_id(env->camera_feed_id);
+ storage->frame.clear_request = false;
} else {
storage->frame.clear_request = false;
}
@@ -4426,6 +4437,63 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
break;
+ case VS::ENV_BG_CAMERA_FEED:
+ if (feed.is_valid() && (feed->get_base_width() > 0) && (feed->get_base_height() > 0)) {
+ // copy our camera feed to our background
+
+ glDisable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_DISPLAY_TRANSFORM, true);
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::DISABLE_ALPHA, true);
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::SRGB_TO_LINEAR, true);
+
+ if (feed->get_datatype() == CameraFeed::FEED_RGB) {
+ RID camera_RGBA = feed->get_texture(CameraServer::FEED_RGBA_IMAGE);
+
+ VS::get_singleton()->texture_bind(camera_RGBA, 0);
+ } else if (feed->get_datatype() == CameraFeed::FEED_YCbCr) {
+ RID camera_YCbCr = feed->get_texture(CameraServer::FEED_YCbCr_IMAGE);
+
+ VS::get_singleton()->texture_bind(camera_YCbCr, 0);
+
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::YCBCR_TO_SRGB, true);
+
+ } else if (feed->get_datatype() == CameraFeed::FEED_YCbCr_Sep) {
+ RID camera_Y = feed->get_texture(CameraServer::FEED_Y_IMAGE);
+ RID camera_CbCr = feed->get_texture(CameraServer::FEED_CbCr_IMAGE);
+
+ VS::get_singleton()->texture_bind(camera_Y, 0);
+ VS::get_singleton()->texture_bind(camera_CbCr, 1);
+
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::SEP_CBCR_TEXTURE, true);
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::YCBCR_TO_SRGB, true);
+ };
+
+ storage->shaders.copy.bind();
+ storage->shaders.copy.set_uniform(CopyShaderGLES3::DISPLAY_TRANSFORM, feed->get_transform());
+
+ _copy_screen(true, true);
+
+ //turn off everything used
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_DISPLAY_TRANSFORM, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::DISABLE_ALPHA, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::SRGB_TO_LINEAR, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::SEP_CBCR_TEXTURE, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::YCBCR_TO_SRGB, false);
+
+ //restore
+ glEnable(GL_BLEND);
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ } else {
+ // don't have a feed, just show greenscreen :)
+ clear_color = Color(0.0, 1.0, 0.0, 1.0);
+ }
+ break;
default: {
}
}
@@ -4552,8 +4620,8 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
}
_post_process(env, p_cam_projection);
-
- if (false && shadow_atlas) {
+ // Needed only for debugging
+ /* if (shadow_atlas && storage->frame.current_rt) {
//_copy_texture_to_front_buffer(shadow_atlas->depth);
storage->canvas->canvas_begin();
@@ -4563,7 +4631,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 2, storage->frame.current_rt->height / 2), Rect2(0, 0, 1, 1));
}
- if (false && storage->frame.current_rt) {
+ if (storage->frame.current_rt) {
//_copy_texture_to_front_buffer(shadow_atlas->depth);
storage->canvas->canvas_begin();
@@ -4573,7 +4641,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 16, storage->frame.current_rt->height / 16), Rect2(0, 0, 1, 1));
}
- if (false && reflection_atlas && storage->frame.current_rt) {
+ if (reflection_atlas && storage->frame.current_rt) {
//_copy_texture_to_front_buffer(shadow_atlas->depth);
storage->canvas->canvas_begin();
@@ -4582,7 +4650,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 2, storage->frame.current_rt->height / 2), Rect2(0, 0, 1, 1));
}
- if (false && directional_shadow.fbo) {
+ if (directional_shadow.fbo) {
//_copy_texture_to_front_buffer(shadow_atlas->depth);
storage->canvas->canvas_begin();
@@ -4592,7 +4660,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 2, storage->frame.current_rt->height / 2), Rect2(0, 0, 1, 1));
}
- if (false && env_radiance_tex) {
+ if ( env_radiance_tex) {
//_copy_texture_to_front_buffer(shadow_atlas->depth);
storage->canvas->canvas_begin();
@@ -4603,8 +4671,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
storage->canvas->draw_generic_textured_rect(Rect2(0, 0, storage->frame.current_rt->width / 2, storage->frame.current_rt->height / 2), Rect2(0, 0, 1, 1));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- }
-
+ }*/
//disable all stuff
}
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 59e23e5ac9..910f90edc2 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -376,6 +376,8 @@ public:
float bg_energy;
float sky_ambient;
+ int camera_feed_id;
+
Color ambient_color;
float ambient_energy;
float ambient_sky_contribution;
@@ -461,6 +463,7 @@ public:
sky_custom_fov(0.0),
bg_energy(1.0),
sky_ambient(0),
+ camera_feed_id(0),
ambient_energy(1.0),
ambient_sky_contribution(0.0),
canvas_max_layer(0),
@@ -542,6 +545,7 @@ public:
virtual void environment_set_bg_energy(RID p_env, float p_energy);
virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer);
virtual void environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy = 1.0, float p_sky_contribution = 0.0);
+ virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id);
virtual void environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality);
virtual void environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality);
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 8311d0de84..f8a3283869 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -1437,6 +1437,15 @@ uint32_t RasterizerStorageGLES3::texture_get_texid(RID p_texture) const {
return texture->tex_id;
}
+void RasterizerStorageGLES3::texture_bind(RID p_texture, uint32_t p_texture_no) {
+
+ Texture *texture = texture_owner.getornull(p_texture);
+
+ ERR_FAIL_COND(!texture);
+
+ glActiveTexture(GL_TEXTURE0 + p_texture_no);
+ glBindTexture(texture->target, texture->tex_id);
+}
uint32_t RasterizerStorageGLES3::texture_get_width(RID p_texture) const {
Texture *texture = texture_owner.get(p_texture);
@@ -3501,7 +3510,7 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
if (p_vertex_count < (1 << 16)) {
//read 16 bit indices
const uint16_t *src_idx = (const uint16_t *)ir.ptr();
- for (int i = 0; i < index_count; i += 6) {
+ for (int i = 0; i + 5 < index_count; i += 6) {
wr[i + 0] = src_idx[i / 2];
wr[i + 1] = src_idx[i / 2 + 1];
@@ -3515,7 +3524,7 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
//read 16 bit indices
const uint32_t *src_idx = (const uint32_t *)ir.ptr();
- for (int i = 0; i < index_count; i += 6) {
+ for (int i = 0; i + 5 < index_count; i += 6) {
wr[i + 0] = src_idx[i / 2];
wr[i + 1] = src_idx[i / 2 + 1];
@@ -3531,7 +3540,7 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
index_count = p_vertex_count * 2;
wf_indices.resize(index_count);
PoolVector<uint32_t>::Write wr = wf_indices.write();
- for (int i = 0; i < index_count; i += 6) {
+ for (int i = 0; i + 5 < index_count; i += 6) {
wr[i + 0] = i / 2;
wr[i + 1] = i / 2 + 1;
@@ -3772,28 +3781,30 @@ PoolVector<uint8_t> RasterizerStorageGLES3::mesh_surface_get_index_array(RID p_m
Surface *surface = mesh->surfaces[p_surface];
- ERR_FAIL_COND_V(surface->index_array_len == 0, PoolVector<uint8_t>());
-
PoolVector<uint8_t> ret;
ret.resize(surface->index_array_byte_size);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id);
+
+ if (surface->index_array_byte_size > 0) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id);
#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__)
- {
- PoolVector<uint8_t>::Write w = ret.write();
- glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, w.ptr());
- }
+ {
+ PoolVector<uint8_t>::Write w = ret.write();
+ glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, w.ptr());
+ }
#else
- void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, GL_MAP_READ_BIT);
- ERR_FAIL_NULL_V(data, PoolVector<uint8_t>());
- {
- PoolVector<uint8_t>::Write w = ret.write();
- copymem(w.ptr(), data, surface->index_array_byte_size);
- }
- glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+ void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, GL_MAP_READ_BIT);
+ ERR_FAIL_NULL_V(data, PoolVector<uint8_t>());
+ {
+ PoolVector<uint8_t>::Write w = ret.write();
+ copymem(w.ptr(), data, surface->index_array_byte_size);
+ }
+ glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
#endif
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
return ret;
}
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 3d86558407..badb656e96 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -358,6 +358,7 @@ public:
virtual uint32_t texture_get_height(RID p_texture) const;
virtual uint32_t texture_get_depth(RID p_texture) const;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth);
+ virtual void texture_bind(RID p_texture, uint32_t p_texture_no);
virtual void texture_set_path(RID p_texture, const String &p_path);
virtual String texture_get_path(RID p_texture) const;
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index e8417900ea..b0f0a71d56 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -472,6 +472,19 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
r_gen_code.fragment_global += interp_mode + "in " + vcode;
}
+ for (Map<StringName, SL::ShaderNode::Constant>::Element *E = pnode->constants.front(); E; E = E->next()) {
+ String gcode;
+ gcode += "const ";
+ gcode += _prestr(E->get().precision);
+ gcode += _typestr(E->get().type);
+ gcode += " " + _mkid(E->key());
+ gcode += "=";
+ gcode += _dump_node_code(E->get().initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ gcode += ";\n";
+ r_gen_code.vertex_global += gcode;
+ r_gen_code.fragment_global += gcode;
+ }
+
Map<StringName, String> function_code;
//code for functions
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 0d1e7ee4a1..a46b31c92e 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -117,7 +117,12 @@ void main() {
#ifdef USE_INSTANCING
mat4 extra_matrix_instance = extra_matrix * transpose(mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0)));
color *= instance_color;
+
+#ifdef USE_INSTANCE_CUSTOM
vec4 instance_custom = instance_custom_data;
+#else
+ vec4 instance_custom = vec4(0.0);
+#endif
#else
mat4 extra_matrix_instance = extra_matrix;
diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl
index e1a0813efc..232b9ce7c0 100644
--- a/drivers/gles3/shaders/copy.glsl
+++ b/drivers/gles3/shaders/copy.glsl
@@ -18,10 +18,19 @@ out vec2 uv_interp;
out vec2 uv2_interp;
+// These definitions are here because the shader-wrapper builder does
+// not understand `#elif defined()`
+#ifdef USE_DISPLAY_TRANSFORM
+#endif
+
#ifdef USE_COPY_SECTION
uniform vec4 copy_section;
+#elif defined(USE_DISPLAY_TRANSFORM)
+
+uniform highp mat4 display_transform;
+
#endif
void main() {
@@ -44,6 +53,9 @@ void main() {
uv_interp = copy_section.xy + uv_interp * copy_section.zw;
gl_Position.xy = (copy_section.xy + (gl_Position.xy * 0.5 + 0.5) * copy_section.zw) * 2.0 - 1.0;
+#elif defined(USE_DISPLAY_TRANSFORM)
+
+ uv_interp = (display_transform * vec4(uv_in, 1.0, 1.0)).xy;
#endif
}
@@ -73,6 +85,8 @@ uniform highp vec4 asym_proj;
#endif
#ifdef USE_TEXTURE2DARRAY
#endif
+#ifdef YCBCR_TO_SRGB
+#endif
#ifdef USE_CUBEMAP
uniform samplerCube source_cube; //texunit:0
@@ -84,6 +98,10 @@ uniform sampler2DArray source_2d_array; //texunit:0
uniform sampler2D source; //texunit:0
#endif
+#ifdef SEP_CBCR_TEXTURE
+uniform sampler2D CbCr; //texunit:1
+#endif
+
/* clang-format on */
#if defined(USE_TEXTURE3D) || defined(USE_TEXTURE2DARRAY)
@@ -166,14 +184,30 @@ void main() {
vec4 color = textureLod(source_3d, vec3(uv_interp, layer), 0.0);
#elif defined(USE_TEXTURE2DARRAY)
vec4 color = textureLod(source_2d_array, vec3(uv_interp, layer), 0.0);
+#elif defined(SEP_CBCR_TEXTURE)
+ vec4 color;
+ color.r = textureLod(source, uv_interp, 0.0).r;
+ color.gb = textureLod(CbCr, uv_interp, 0.0).rg - vec2(0.5, 0.5);
+ color.a = 1.0;
#else
vec4 color = textureLod(source, uv_interp, 0.0);
#endif
#ifdef LINEAR_TO_SRGB
- //regular Linear -> SRGB conversion
+ // regular Linear -> SRGB conversion
vec3 a = vec3(0.055);
color.rgb = mix((vec3(1.0) + a) * pow(color.rgb, vec3(1.0 / 2.4)) - a, 12.92 * color.rgb, lessThan(color.rgb, vec3(0.0031308)));
+
+#elif defined(YCBCR_TO_SRGB)
+
+ // YCbCr -> SRGB conversion
+ // Using BT.709 which is the standard for HDTV
+ color.rgb = mat3(
+ vec3(1.00000, 1.00000, 1.00000),
+ vec3(0.00000, -0.18732, 1.85560),
+ vec3(1.57481, -0.46813, 0.00000)) *
+ color.rgb;
+
#endif
#ifdef SRGB_TO_LINEAR
diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp
index 37abccbe27..75d51c8503 100644
--- a/drivers/unix/net_socket_posix.cpp
+++ b/drivers/unix/net_socket_posix.cpp
@@ -306,7 +306,7 @@ Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) {
sockaddr_storage addr;
size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type);
- if (::bind(_sock, (struct sockaddr *)&addr, addr_size) == SOCK_EMPTY) {
+ if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
close();
ERR_FAIL_V(ERR_UNAVAILABLE);
}
@@ -317,7 +317,7 @@ Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) {
Error NetSocketPosix::listen(int p_max_pending) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
- if (::listen(_sock, p_max_pending) == SOCK_EMPTY) {
+ if (::listen(_sock, p_max_pending) != 0) {
close();
ERR_FAIL_V(FAILED);
@@ -334,7 +334,7 @@ Error NetSocketPosix::connect_to_host(IP_Address p_host, uint16_t p_port) {
struct sockaddr_storage addr;
size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type);
- if (::connect(_sock, (struct sockaddr *)&addr, addr_size) == SOCK_EMPTY) {
+ if (::connect(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
NetError err = _get_socket_error();
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index c97849ef07..fea38ee95d 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -167,13 +167,13 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
for (ULONG i = 0; i < count && !found; i++) {
- IMMDevice *device = NULL;
+ IMMDevice *tmp_device = NULL;
- hr = devices->Item(i, &device);
+ hr = devices->Item(i, &tmp_device);
ERR_BREAK(hr != S_OK);
IPropertyStore *props = NULL;
- hr = device->OpenPropertyStore(STGM_READ, &props);
+ hr = tmp_device->OpenPropertyStore(STGM_READ, &props);
ERR_BREAK(hr != S_OK);
PROPVARIANT propvar;
@@ -183,7 +183,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
ERR_BREAK(hr != S_OK);
if (p_device->device_name == String(propvar.pwszVal)) {
- hr = device->GetId(&strId);
+ hr = tmp_device->GetId(&strId);
ERR_BREAK(hr != S_OK);
found = true;
@@ -191,7 +191,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
PropVariantClear(&propvar);
props->Release();
- device->Release();
+ tmp_device->Release();
}
if (found) {
@@ -289,7 +289,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
}
DWORD streamflags = 0;
- if (mix_rate != pwfex->nSamplesPerSec) {
+ if ((DWORD)mix_rate != pwfex->nSamplesPerSec) {
streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
pwfex->nSamplesPerSec = mix_rate;
pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8);
@@ -571,7 +571,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
if (ad->audio_output.active) {
ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
} else {
- for (unsigned int i = 0; i < ad->samples_in.size(); i++) {
+ for (int i = 0; i < ad->samples_in.size(); i++) {
ad->samples_in.write[i] = 0;
}
}
@@ -699,7 +699,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
ERR_BREAK(hr != S_OK);
// fixme: Only works for floating point atm
- for (int j = 0; j < num_frames_available; j++) {
+ for (UINT32 j = 0; j < num_frames_available; j++) {
int32_t l, r;
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index c13300d09f..646e744248 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -93,7 +93,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
// a file using the wrong case (which *works* on Windows, but won't on other
// platforms).
if (p_mode_flags == READ) {
- WIN32_FIND_DATAW d = { 0 };
+ WIN32_FIND_DATAW d;
HANDLE f = FindFirstFileW(path.c_str(), &d);
if (f) {
String fname = d.cFileName;
@@ -302,7 +302,7 @@ void FileAccessWindows::store_buffer(const uint8_t *p_src, int p_length) {
}
prev_op = WRITE;
}
- ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length);
+ ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != (size_t)p_length);
}
bool FileAccessWindows::file_exists(const String &p_name) {
@@ -340,13 +340,11 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
}
uint32_t FileAccessWindows::_get_unix_permissions(const String &p_file) {
- ERR_PRINT("Windows does not support unix permissions");
return 0;
}
Error FileAccessWindows::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
- ERR_PRINT("Windows does not support unix permissions");
- return FAILED;
+ return ERR_UNAVAILABLE;
}
FileAccessWindows::FileAccessWindows() :
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index e8490e8729..0385220baa 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1214,7 +1214,7 @@ void AnimationTrackEdit::_notification(int p_what) {
Color accent = get_color("accent_color", "Editor");
accent.a *= 0.7;
// Offside so the horizontal sides aren't cutoff.
- draw_rect(Rect2(Point2(1, 0), get_size() - Size2(1, 0)), accent, false);
+ draw_rect(Rect2(Point2(1 * EDSCALE, 0), get_size() - Size2(1 * EDSCALE, 0)), accent, false);
}
Ref<Font> font = get_font("font", "Label");
@@ -3633,6 +3633,9 @@ void AnimationTrackEditor::_animation_update() {
bezier_edit->update();
_update_step_spinbox();
+ emit_signal("animation_step_changed", animation->get_step());
+ emit_signal("animation_len_changed", animation->get_length());
+ EditorNode::get_singleton()->get_inspector()->refresh();
animation_changing_awaiting_update = false;
}
@@ -3685,8 +3688,7 @@ void AnimationTrackEditor::_update_step(double p_new_step) {
step->set_block_signals(true);
undo_redo->commit_action();
step->set_block_signals(false);
- emit_signal("animation_step_changed", p_new_step);
- animation->_change_notify("step");
+ emit_signal("animation_step_changed", step_value);
}
void AnimationTrackEditor::_update_length(double p_new_len) {
@@ -4973,6 +4975,7 @@ void AnimationTrackEditor::_bind_methods() {
ClassDB::bind_method("_update_scroll", &AnimationTrackEditor::_update_scroll);
ClassDB::bind_method("_update_tracks", &AnimationTrackEditor::_update_tracks);
ClassDB::bind_method("_update_step", &AnimationTrackEditor::_update_step);
+ ClassDB::bind_method("_update_length", &AnimationTrackEditor::_update_length);
ClassDB::bind_method("_dropped_track", &AnimationTrackEditor::_dropped_track);
ClassDB::bind_method("_add_track", &AnimationTrackEditor::_add_track);
ClassDB::bind_method("_new_track_node_selected", &AnimationTrackEditor::_new_track_node_selected);
@@ -5033,6 +5036,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
timeline->connect("name_limit_changed", this, "_name_limit_changed");
timeline->connect("track_added", this, "_add_track");
timeline->connect("value_changed", this, "_timeline_value_changed");
+ timeline->connect("length_changed", this, "_update_length");
scroll = memnew(ScrollContainer);
timeline_vbox->add_child(scroll);
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index baf417fed7..07dbc1fd81 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -48,7 +48,7 @@ int AnimationTrackEditBool::get_key_height() const {
Rect2 AnimationTrackEditBool::get_key_rect(int p_index, float p_pixels_sec) {
Ref<Texture> checked = get_icon("checked", "CheckBox");
- return Rect2(0, 0, checked->get_width(), get_size().height);
+ return Rect2(-checked->get_width() / 2, 0, checked->get_width(), get_size().height);
}
bool AnimationTrackEditBool::is_key_selectable_by_distance() const {
@@ -57,17 +57,18 @@ bool AnimationTrackEditBool::is_key_selectable_by_distance() const {
}
void AnimationTrackEditBool::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
- Ref<Texture> icon;
bool checked = get_animation()->track_get_key_value(get_track(), p_index);
+ Ref<Texture> icon = get_icon(checked ? "checked" : "unchecked", "CheckBox");
- if (checked)
- icon = get_icon("checked", "CheckBox");
- else
- icon = get_icon("unchecked", "CheckBox");
+ Vector2 ofs(p_x - icon->get_width() / 2, int(get_size().height - icon->get_height()) / 2);
- Vector2 ofs(p_x, int(get_size().height - icon->get_height()) / 2);
+ if (ofs.x + icon->get_width() / 2 < p_clip_left)
+ return;
+
+ if (ofs.x + icon->get_width() / 2 > p_clip_right)
+ return;
- draw_texture_clipped(icon, ofs);
+ draw_texture(icon, ofs);
if (p_selected) {
Color color = get_color("accent_color", "Editor");
@@ -86,7 +87,7 @@ Rect2 AnimationTrackEditColor::get_key_rect(int p_index, float p_pixels_sec) {
Ref<Font> font = get_font("font", "Label");
int fh = font->get_height() * 0.8;
- return Rect2(0, 0, fh, get_size().height);
+ return Rect2(-fh / 2, 0, fh, get_size().height);
}
bool AnimationTrackEditColor::is_key_selectable_by_distance() const {
@@ -96,20 +97,14 @@ bool AnimationTrackEditColor::is_key_selectable_by_distance() const {
void AnimationTrackEditColor::draw_key_link(int p_index, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right) {
- int x_from = p_x;
- int x_to = p_next_x;
-
Ref<Font> font = get_font("font", "Label");
int fh = (font->get_height() * 0.8);
- x_from += fh - 1;
- x_to += 1;
+ int x_from = p_x + fh / 2 - 1;
+ int x_to = p_next_x - fh / 2 + 1;
fh /= 3;
- if (x_from > p_clip_right)
- return;
-
- if (x_to < p_clip_left)
+ if (x_from > p_clip_right || x_to < p_clip_left)
return;
Color color = get_animation()->track_get_key_value(get_track(), p_index);
@@ -154,7 +149,7 @@ void AnimationTrackEditColor::draw_key(int p_index, float p_pixels_sec, int p_x,
Ref<Font> font = get_font("font", "Label");
int fh = font->get_height() * 0.8;
- Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));
+ Rect2 rect(Vector2(p_x - fh / 2, int(get_size().height - fh) / 2), Size2(fh, fh));
draw_rect_clipped(Rect2(rect.position, rect.size / 2), Color(0.4, 0.4, 0.4));
draw_rect_clipped(Rect2(rect.position + rect.size / 2, rect.size / 2), Color(0.4, 0.4, 0.4));
diff --git a/editor/audio_stream_preview.cpp b/editor/audio_stream_preview.cpp
index 85db8b77f9..b30b94ab26 100644
--- a/editor/audio_stream_preview.cpp
+++ b/editor/audio_stream_preview.cpp
@@ -129,7 +129,7 @@ void AudioStreamPreviewGenerator::_preview_thread(void *p_preview) {
float max = -1000;
float min = 1000;
int from = uint64_t(i) * to_read / to_write;
- int to = uint64_t(i + 1) * to_read / to_write;
+ int to = (uint64_t(i) + 1) * to_read / to_write;
to = MIN(to, to_read);
from = MIN(from, to_read - 1);
if (to == from) {
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 695560ca34..01773a0bcd 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -675,14 +675,14 @@ void CodeTextEditor::_line_col_changed() {
}
}
- StringBuilder *sb = memnew(StringBuilder);
- sb->append("(");
- sb->append(itos(text_editor->cursor_get_line() + 1).lpad(3));
- sb->append(",");
- sb->append(itos(positional_column + 1).lpad(3));
- sb->append(")");
+ StringBuilder sb;
+ sb.append("(");
+ sb.append(itos(text_editor->cursor_get_line() + 1).lpad(3));
+ sb.append(",");
+ sb.append(itos(positional_column + 1).lpad(3));
+ sb.append(")");
- line_and_col_txt->set_text(sb->as_string());
+ line_and_col_txt->set_text(sb.as_string());
}
void CodeTextEditor::_text_changed() {
@@ -804,6 +804,24 @@ void CodeTextEditor::trim_trailing_whitespace() {
}
}
+void CodeTextEditor::insert_final_newline() {
+ int final_line = text_editor->get_line_count() - 1;
+
+ String line = text_editor->get_line(final_line);
+
+ //length 0 means it's already an empty line,
+ //no need to add a newline
+ if (line.length() > 0 && !line.ends_with("\n")) {
+ text_editor->begin_complex_operation();
+
+ line += "\n";
+ text_editor->set_line(final_line, line);
+
+ text_editor->end_complex_operation();
+ text_editor->update();
+ }
+}
+
void CodeTextEditor::convert_indent_to_spaces() {
int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size");
String indent = "";
@@ -912,7 +930,7 @@ void CodeTextEditor::convert_case(CaseStyle p_case) {
for (int i = begin; i <= end; i++) {
int len = text_editor->get_line(i).length();
if (i == end)
- len -= len - end_col;
+ len = end_col;
if (i == begin)
len -= begin_col;
String new_line = text_editor->get_line(i).substr(i == begin ? begin_col : 0, len);
diff --git a/editor/code_editor.h b/editor/code_editor.h
index 5c6b54ae44..cf97f30b72 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -195,6 +195,7 @@ protected:
public:
void trim_trailing_whitespace();
+ void insert_final_newline();
void convert_indent_to_spaces();
void convert_indent_to_tabs();
diff --git a/editor/collada/collada.cpp b/editor/collada/collada.cpp
index e9040b9d3e..a0d319c81b 100644
--- a/editor/collada/collada.cpp
+++ b/editor/collada/collada.cpp
@@ -651,7 +651,7 @@ void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &
effect.emission.texture = uri;
} else if (what == "bump") {
if (parser.has_attribute("bumptype") && parser.get_attribute_value("bumptype") != "NORMALMAP") {
- WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported.")
+ WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported.");
}
effect.bump.texture = uri;
@@ -707,7 +707,7 @@ void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &
String uri = effect.params[surface];
if (parser.has_attribute("bumptype") && parser.get_attribute_value("bumptype") != "NORMALMAP") {
- WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported.")
+ WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported.");
}
effect.bump.texture = uri;
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index a9a96da7b1..6d3603f31b 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -126,7 +126,6 @@ void ConnectDialog::ok_pressed() {
}
}
emit_signal("connected");
- hide();
}
void ConnectDialog::_cancel_pressed() {
@@ -307,39 +306,42 @@ void ConnectDialog::init(Connection c, bool bEdit) {
bEditMode = bEdit;
}
-void ConnectDialog::popup_dialog(const String &p_for_signal, bool p_advanced) {
+void ConnectDialog::popup_dialog(const String &p_for_signal) {
- advanced->set_pressed(p_advanced);
from_signal->set_text(p_for_signal);
error_label->add_color_override("font_color", get_color("error_color", "Editor"));
- vbc_right->set_visible(p_advanced);
+ if (!advanced->is_pressed())
+ error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root()));
+
+ popup_centered();
+}
- if (p_advanced) {
+void ConnectDialog::_advanced_pressed() {
- popup_centered(Size2(900, 500) * EDSCALE);
- connect_to_label->set_text("Connect to Node:");
+ if (advanced->is_pressed()) {
+ set_custom_minimum_size(Size2(900, 500) * EDSCALE);
+ connect_to_label->set_text(TTR("Connect to Node:"));
tree->set_connect_to_script_mode(false);
+
+ vbc_right->show();
error_label->hide();
} else {
- popup_centered(Size2(700, 500) * EDSCALE);
- connect_to_label->set_text("Connect to Script:");
+ set_custom_minimum_size(Size2(600, 500) * EDSCALE);
+ set_size(Size2());
+ connect_to_label->set_text(TTR("Connect to Script:"));
tree->set_connect_to_script_mode(true);
- if (!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())) {
- error_label->show();
- } else {
- error_label->hide();
- }
+ vbc_right->hide();
+ error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root()));
}
-}
-
-void ConnectDialog::_advanced_pressed() {
- popup_dialog(from_signal->get_text(), advanced->is_pressed());
+ set_position((get_viewport_rect().size - get_custom_minimum_size()) / 2);
}
ConnectDialog::ConnectDialog() {
+ set_custom_minimum_size(Size2(600, 500) * EDSCALE);
+
VBoxContainer *vbc = memnew(VBoxContainer);
add_child(vbc);
@@ -356,11 +358,12 @@ ConnectDialog::ConnectDialog() {
vbc_left->add_margin_child(TTR("From Signal:"), from_signal);
tree = memnew(SceneTreeEditor(false));
+ tree->set_connecting_signal(true);
tree->get_scene_tree()->connect("item_activated", this, "_ok");
tree->connect("node_selected", this, "_tree_node_selected");
tree->set_connect_to_script_mode(true);
- Node *mc = vbc_left->add_margin_child(TTR("Connect To Script:"), tree, true);
+ Node *mc = vbc_left->add_margin_child(TTR("Connect to Script:"), tree, true);
connect_to_label = Object::cast_to<Label>(vbc_left->get_child(mc->get_index() - 1));
error_label = memnew(Label);
@@ -381,7 +384,7 @@ ConnectDialog::ConnectDialog() {
type_list->add_item("bool", Variant::BOOL);
type_list->add_item("int", Variant::INT);
type_list->add_item("real", Variant::REAL);
- type_list->add_item("string", Variant::STRING);
+ type_list->add_item("String", Variant::STRING);
type_list->add_item("Vector2", Variant::VECTOR2);
type_list->add_item("Rect2", Variant::RECT2);
type_list->add_item("Vector3", Variant::VECTOR3);
@@ -410,34 +413,32 @@ ConnectDialog::ConnectDialog() {
vbc_right->add_margin_child(TTR("Extra Call Arguments:"), bind_editor, true);
HBoxContainer *dstm_hb = memnew(HBoxContainer);
- vbc_left->add_margin_child("Method to Create:", dstm_hb);
+ vbc_left->add_margin_child("Receiver Method:", dstm_hb);
dst_method = memnew(LineEdit);
dst_method->set_h_size_flags(SIZE_EXPAND_FILL);
dstm_hb->add_child(dst_method);
- advanced = memnew(CheckBox);
+ advanced = memnew(CheckButton);
dstm_hb->add_child(advanced);
- advanced->set_text(TTR("Advanced..."));
+ advanced->set_text(TTR("Advanced"));
advanced->connect("pressed", this, "_advanced_pressed");
- /*
- dst_method_list = memnew( MenuButton );
- dst_method_list->set_text("List...");
- dst_method_list->set_anchor( MARGIN_RIGHT, ANCHOR_END );
- dst_method_list->set_anchor( MARGIN_LEFT, ANCHOR_END );
- dst_method_list->set_anchor( MARGIN_TOP, ANCHOR_END );
- dst_method_list->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
- dst_method_list->set_begin( Point2( 70,59) );
- dst_method_list->set_end( Point2( 15,39 ) );
- */
-
- deferred = memnew(CheckButton);
+ // Add spacing so the tree and inspector are the same size.
+ Control *spacing = memnew(Control);
+ spacing->set_custom_minimum_size(Size2(0, 4) * EDSCALE);
+ vbc_right->add_child(spacing);
+
+ deferred = memnew(CheckBox);
+ deferred->set_h_size_flags(0);
deferred->set_text(TTR("Deferred"));
+ deferred->set_tooltip(TTR("Defers the signal, storing it in a queue and only firing it at idle time."));
vbc_right->add_child(deferred);
- oneshot = memnew(CheckButton);
+ oneshot = memnew(CheckBox);
+ oneshot->set_h_size_flags(0);
oneshot->set_text(TTR("Oneshot"));
+ oneshot->set_tooltip(TTR("Disconnects the signal after its first emission."));
vbc_right->add_child(oneshot);
set_as_toplevel(true);
@@ -456,7 +457,7 @@ ConnectDialog::~ConnectDialog() {
memdelete(cdbinds);
}
-//ConnectionsDock ==========================
+//////////////////////////////////////////
struct _ConnectionsDockMethodInfoSort {
@@ -488,11 +489,29 @@ void ConnectionsDock::_make_or_edit_connection() {
bool oshot = connect_dialog->get_oneshot();
cToMake.flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0);
- //conditions to add function, must have a script and must have a method
- bool add_script_function = !target->get_script().is_null() && !ClassDB::has_method(target->get_class(), cToMake.method);
+ // Conditions to add function: must have a script and must not have the method already
+ // (in the class, the script itself, or inherited).
+ bool add_script_function = false;
+ Ref<Script> script = target->get_script();
+ if (!target->get_script().is_null() && !ClassDB::has_method(target->get_class(), cToMake.method)) {
+ // There is a chance that the method is inherited from another script.
+ bool found_inherited_function = false;
+ Ref<Script> inherited_script = script->get_base_script();
+ while (!inherited_script.is_null()) {
+ int line = inherited_script->get_language()->find_function(cToMake.method, inherited_script->get_source_code());
+ if (line != -1) {
+ found_inherited_function = true;
+ break;
+ }
+
+ inherited_script = inherited_script->get_base_script();
+ }
+
+ add_script_function = !found_inherited_function;
+ }
PoolStringArray script_function_args;
if (add_script_function) {
- // pick up args here before "it" is deleted by update_tree
+ // Pick up args here before "it" is deleted by update_tree.
script_function_args = it->get_metadata(0).operator Dictionary()["args"];
for (int i = 0; i < cToMake.binds.size(); i++) {
script_function_args.append("extra_arg_" + itos(i));
@@ -506,8 +525,7 @@ void ConnectionsDock::_make_or_edit_connection() {
_connect(cToMake);
}
- // IMPORTANT NOTE: _disconnect and _connect cause an update_tree,
- // which will delete the object "it" is pointing to
+ // IMPORTANT NOTE: _disconnect and _connect cause an update_tree, which will delete the object "it" is pointing to.
it = NULL;
if (add_script_function) {
@@ -547,7 +565,7 @@ Break single connection w/ undo-redo functionality.
void ConnectionsDock::_disconnect(TreeItem &item) {
Connection c = item.get_metadata(0);
- ERR_FAIL_COND(c.source != selectedNode); //shouldn't happen but...bugcheck
+ ERR_FAIL_COND(c.source != selectedNode); // Shouldn't happen but... Bugcheck.
undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), c.signal, c.method));
@@ -555,7 +573,7 @@ void ConnectionsDock::_disconnect(TreeItem &item) {
undo_redo->add_undo_method(selectedNode, "connect", c.signal, c.target, c.method, c.binds, c.flags);
undo_redo->add_do_method(this, "update_tree");
undo_redo->add_undo_method(this, "update_tree");
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); //to force redraw of scene tree
+ undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); // To force redraw of scene tree.
undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
@@ -594,7 +612,7 @@ void ConnectionsDock::_disconnect_all() {
void ConnectionsDock::_tree_item_selected() {
TreeItem *item = tree->get_selected();
- if (!item) { //Unlikely. Disable button just in case.
+ if (!item) { // Unlikely. Disable button just in case.
connect_button->set_text(TTR("Connect..."));
connect_button->set_disabled(true);
} else if (_is_item_signal(*item)) {
@@ -606,7 +624,7 @@ void ConnectionsDock::_tree_item_selected() {
}
}
-void ConnectionsDock::_tree_item_activated() { //"Activation" on double-click.
+void ConnectionsDock::_tree_item_activated() { // "Activation" on double-click.
TreeItem *item = tree->get_selected();
@@ -628,7 +646,6 @@ bool ConnectionsDock::_is_item_signal(TreeItem &item) {
/*
Open connection dialog with TreeItem data to CREATE a brand-new connection.
*/
-
void ConnectionsDock::_open_connection_dialog(TreeItem &item) {
String signal = item.get_metadata(0).operator Dictionary()["name"];
@@ -638,10 +655,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) {
CharType c = midname[i];
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
if (c == ' ') {
- //Replace spaces with underlines.
+ // Replace spaces with underlines.
c = '_';
} else {
- //Remove any other characters.
+ // Remove any other characters.
midname.remove(i);
i--;
continue;
@@ -662,9 +679,7 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) {
c.signal = StringName(signalname);
c.target = dst_node;
c.method = dst_method;
-
- //connect_dialog->set_title(TTR("Connect Signal: ") + signalname);
- connect_dialog->popup_dialog(signalname, false);
+ connect_dialog->popup_dialog(signalname);
connect_dialog->init(c);
connect_dialog->set_title(TTR("Connect a Signal to a Method"));
}
@@ -679,7 +694,7 @@ void ConnectionsDock::_open_connection_dialog(Connection cToEdit) {
if (src && dst) {
connect_dialog->set_title(TTR("Edit Connection:") + cToEdit.signal);
- connect_dialog->popup_centered_ratio();
+ connect_dialog->popup_centered();
connect_dialog->init(cToEdit, true);
}
}
@@ -828,7 +843,6 @@ void ConnectionsDock::update_tree() {
selectedNode->get_signal_list(&node_signals);
- //node_signals.sort_custom<_ConnectionsDockMethodInfoSort>();
bool did_script = false;
StringName base = selectedNode->get_class();
@@ -955,7 +969,7 @@ void ConnectionsDock::update_tree() {
}
}
- connect_button->set_text(TTR("Connect"));
+ connect_button->set_text(TTR("Connect..."));
connect_button->set_disabled(true);
}
@@ -975,7 +989,6 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) {
tree->set_allow_rmb_select(true);
connect_button = memnew(Button);
- connect_button->set_text(TTR("Connect"));
HBoxContainer *hb = memnew(HBoxContainer);
vbc->add_child(hb);
hb->add_spacer();
@@ -1005,15 +1018,6 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) {
slot_menu->add_item(TTR("Go To Method"), GO_TO_SCRIPT);
slot_menu->add_item(TTR("Disconnect"), DISCONNECT);
- /*
- node_only->set_anchor( MARGIN_TOP, ANCHOR_END );
- node_only->set_anchor( MARGIN_BOTTOM, ANCHOR_END );
- node_only->set_anchor( MARGIN_RIGHT, ANCHOR_END );
-
- node_only->set_begin( Point2( 20,51) );
- node_only->set_end( Point2( 10,44) );
- */
-
connect_dialog->connect("connected", this, "_make_or_edit_connection");
tree->connect("item_selected", this, "_tree_item_selected");
tree->connect("item_activated", this, "_tree_item_activated");
diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h
index 9b35d84426..195c9e1e7d 100644
--- a/editor/connections_dialog.h
+++ b/editor/connections_dialog.h
@@ -67,9 +67,9 @@ class ConnectDialog : public ConfirmationDialog {
AcceptDialog *error;
EditorInspector *bind_editor;
OptionButton *type_list;
- CheckButton *deferred;
- CheckButton *oneshot;
- CheckBox *advanced;
+ CheckBox *deferred;
+ CheckBox *oneshot;
+ CheckButton *advanced;
Label *error_label;
@@ -99,12 +99,12 @@ public:
void init(Connection c, bool bEdit = false);
- void popup_dialog(const String &p_for_signal, bool p_advanced);
+ void popup_dialog(const String &p_for_signal);
ConnectDialog();
~ConnectDialog();
};
-//========================================
+//////////////////////////////////////////
class ConnectionsDock : public VBoxContainer {
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 604a050fcd..e84602b29f 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -246,13 +246,14 @@ bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_cla
if (profile->is_class_disabled(class_name)) {
return true;
}
- class_name = ClassDB::get_parent_class(class_name);
+ class_name = ClassDB::get_parent_class_nocheck(class_name);
}
return false;
}
void CreateDialog::select_type(const String &p_type) {
+
TreeItem *to_select;
if (search_options_types.has(p_type)) {
to_select = search_options_types[p_type];
@@ -279,10 +280,6 @@ void CreateDialog::_update_search() {
favorite->set_disabled(true);
help_bit->set_text("");
- /*
- TreeItem *root = search_options->create_item();
- _parse_fs(EditorFileSystem::get_singleton()->get_filesystem());
-*/
search_options_types.clear();
@@ -733,6 +730,7 @@ CreateDialog::CreateDialog() {
fav_vb->add_margin_child(TTR("Favorites:"), favorites, true);
favorites->set_hide_root(true);
favorites->set_hide_folding(true);
+ favorites->set_allow_reselect(true);
favorites->connect("cell_selected", this, "_favorite_selected");
favorites->connect("item_activated", this, "_favorite_activated");
favorites->set_drag_forwarding(this);
@@ -747,6 +745,7 @@ CreateDialog::CreateDialog() {
rec_vb->add_margin_child(TTR("Recent:"), recent, true);
recent->set_hide_root(true);
recent->set_hide_folding(true);
+ recent->set_allow_reselect(true);
recent->connect("cell_selected", this, "_history_selected");
recent->connect("item_activated", this, "_history_activated");
recent->add_constant_override("draw_guides", 1);
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index bde73e9268..9a049f3ae3 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -35,9 +35,6 @@
#include "editor_node.h"
#include "scene/gui/margin_container.h"
-void DependencyEditor::_notification(int p_what) {
-}
-
void DependencyEditor::_searched(const String &p_path) {
Map<String, String> dep_rename;
@@ -63,7 +60,7 @@ void DependencyEditor::_load_pressed(Object *p_item, int p_cell, int p_button) {
for (List<String>::Element *E = ext.front(); E; E = E->next()) {
search->add_filter("*" + E->get());
}
- search->popup_centered_ratio();
+ search->popup_centered_ratio(0.65); // So it doesn't completely cover the dialog below it.
}
void DependencyEditor::_fix_and_find(EditorFileSystemDirectory *efsd, Map<String, Map<String, String> > &candidates) {
@@ -222,7 +219,7 @@ void DependencyEditor::edit(const String &p_path) {
set_title(TTR("Dependencies For:") + " " + p_path.get_file());
_update_list();
- popup_centered_ratio();
+ popup_centered_ratio(0.7); // So it doesn't completely cover the dialog below it.
if (EditorNode::get_singleton()->is_scene_open(p_path)) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Scene '%s' is currently being edited.\nChanges will only take effect when reloaded."), p_path.get_file()));
@@ -478,12 +475,13 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<
if (removed_deps.empty()) {
owners->hide();
text->set_text(TTR("Remove selected files from the project? (no undo)"));
- popup_centered_minsize(Size2(400, 100));
+ set_size(Size2());
+ popup_centered();
} else {
_build_removed_dependency_tree(removed_deps);
owners->show();
text->set_text(TTR("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (no undo)"));
- popup_centered_minsize(Size2(500, 350));
+ popup_centered(Size2(500, 350));
}
EditorFileSystem::get_singleton()->scan_changes();
}
@@ -579,6 +577,8 @@ void DependencyRemoveDialog::_bind_methods() {
DependencyRemoveDialog::DependencyRemoveDialog() {
+ get_ok()->set_text(TTR("Remove"));
+
VBoxContainer *vb = memnew(VBoxContainer);
add_child(vb);
@@ -589,7 +589,6 @@ DependencyRemoveDialog::DependencyRemoveDialog() {
owners->set_hide_root(true);
vb->add_child(owners);
owners->set_v_size_flags(SIZE_EXPAND_FILL);
- get_ok()->set_text(TTR("Remove"));
}
//////////////
@@ -617,7 +616,7 @@ void DependencyErrorDialog::show(Mode p_mode, const String &p_for_file, const Ve
ti->set_icon(0, icon);
}
- popup_centered_minsize(Size2(500, 220));
+ popup_centered();
}
void DependencyErrorDialog::ok_pressed() {
@@ -646,7 +645,8 @@ DependencyErrorDialog::DependencyErrorDialog() {
files->set_hide_root(true);
vb->add_margin_child(TTR("Load failed due to missing dependencies:"), files, true);
files->set_v_size_flags(SIZE_EXPAND_FILL);
- files->set_custom_minimum_size(Size2(1, 200));
+
+ set_custom_minimum_size(Size2(500, 220));
get_ok()->set_text(TTR("Open Anyway"));
get_cancel()->set_text(TTR("Close"));
@@ -670,7 +670,7 @@ void OrphanResourcesDialog::ok_pressed() {
return;
delete_confirm->set_text(vformat(TTR("Permanently delete %d item(s)? (No undo!)"), paths.size()));
- delete_confirm->popup_centered_minsize();
+ delete_confirm->popup_centered_clamped(delete_confirm->get_minimum_size());
}
bool OrphanResourcesDialog::_fill_owners(EditorFileSystemDirectory *efsd, HashMap<String, int> &refs, TreeItem *p_parent) {
@@ -725,7 +725,7 @@ bool OrphanResourcesDialog::_fill_owners(EditorFileSystemDirectory *efsd, HashMa
int ds = efsd->get_file_deps(i).size();
ti->set_text(1, itos(ds));
if (ds) {
- ti->add_button(1, get_icon("GuiVisibilityVisible", "EditorIcons"));
+ ti->add_button(1, get_icon("GuiVisibilityVisible", "EditorIcons"), -1, false, TTR("Show Dependencies"));
}
ti->set_metadata(0, path);
has_children = true;
@@ -794,6 +794,15 @@ void OrphanResourcesDialog::_bind_methods() {
OrphanResourcesDialog::OrphanResourcesDialog() {
+ set_title(TTR("Orphan Resource Explorer"));
+ delete_confirm = memnew(ConfirmationDialog);
+ get_ok()->set_text(TTR("Delete"));
+ add_child(delete_confirm);
+ dep_edit = memnew(DependencyEditor);
+ add_child(dep_edit);
+ delete_confirm->connect("confirmed", this, "_delete_confirm");
+ set_hide_on_ok(false);
+
VBoxContainer *vbc = memnew(VBoxContainer);
add_child(vbc);
@@ -807,14 +816,5 @@ OrphanResourcesDialog::OrphanResourcesDialog() {
files->set_column_title(1, TTR("Owns"));
files->set_hide_root(true);
vbc->add_margin_child(TTR("Resources Without Explicit Ownership:"), files, true);
- set_title(TTR("Orphan Resource Explorer"));
- delete_confirm = memnew(ConfirmationDialog);
- delete_confirm->set_text(TTR("Delete selected files?"));
- get_ok()->set_text(TTR("Delete"));
- add_child(delete_confirm);
- dep_edit = memnew(DependencyEditor);
- add_child(dep_edit);
files->connect("button_pressed", this, "_button_pressed");
- delete_confirm->connect("confirmed", this, "_delete_confirm");
- set_hide_on_ok(false);
}
diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h
index 23c3cc031c..22e28a4d26 100644
--- a/editor/dependency_editor.h
+++ b/editor/dependency_editor.h
@@ -63,7 +63,6 @@ class DependencyEditor : public AcceptDialog {
protected:
static void _bind_methods();
- void _notification(int p_what);
public:
void edit(const String &p_path);
diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp
index c2a492bc9a..7d2159d365 100644
--- a/editor/doc/doc_data.cpp
+++ b/editor/doc/doc_data.cpp
@@ -136,9 +136,6 @@ void DocData::merge_from(const DocData &p_data) {
const PropertyDoc &pf = cf.properties[j];
p.description = pf.description;
- p.setter = pf.setter;
- p.getter = pf.getter;
-
break;
}
}
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index e9dd6d7199..bea1980b09 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -131,7 +131,7 @@ EditorAbout::EditorAbout() {
String hash = String(VERSION_HASH);
if (hash.length() != 0)
- hash = "." + hash.left(7);
+ hash = "." + hash.left(9);
Label *about_text = memnew(Label);
about_text->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
diff --git a/editor/editor_atlas_packer.cpp b/editor/editor_atlas_packer.cpp
index 4e1d98399a..96ddc607df 100644
--- a/editor/editor_atlas_packer.cpp
+++ b/editor/editor_atlas_packer.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* editor_atlas_packer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 "editor_atlas_packer.h"
void EditorAtlasPacker::_plot_triangle(Ref<BitMap> p_bitmap, Vector2i *vertices) {
diff --git a/editor/editor_atlas_packer.h b/editor/editor_atlas_packer.h
index dd9caa340e..1627f74a28 100644
--- a/editor/editor_atlas_packer.h
+++ b/editor/editor_atlas_packer.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* editor_atlas_packer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 EDITOR_ATLAS_PACKER_H
#define EDITOR_ATLAS_PACKER_H
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 9cd7d781a4..57fac241b0 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -63,118 +63,132 @@ void EditorAudioBus::_update_visible_channels() {
void EditorAudioBus::_notification(int p_what) {
- if (p_what == NOTIFICATION_READY) {
-
- for (int i = 0; i < CHANNELS_MAX; i++) {
- channel[i].vu_l->set_under_texture(get_icon("BusVuEmpty", "EditorIcons"));
- channel[i].vu_l->set_progress_texture(get_icon("BusVuFull", "EditorIcons"));
- channel[i].vu_r->set_under_texture(get_icon("BusVuEmpty", "EditorIcons"));
- channel[i].vu_r->set_progress_texture(get_icon("BusVuFull", "EditorIcons"));
- channel[i].prev_active = true;
- }
-
- disabled_vu = get_icon("BusVuFrozen", "EditorIcons");
-
- Color solo_color = Color::html(EditorSettings::get_singleton()->is_dark_theme() ? "#ffe337" : "#ffeb70");
- Color mute_color = Color::html(EditorSettings::get_singleton()->is_dark_theme() ? "#ff2929" : "#ff7070");
- Color bypass_color = Color::html(EditorSettings::get_singleton()->is_dark_theme() ? "#22ccff" : "#70deff");
-
- solo->set_icon(get_icon("AudioBusSolo", "EditorIcons"));
- solo->add_color_override("icon_color_pressed", solo_color);
- mute->set_icon(get_icon("AudioBusMute", "EditorIcons"));
- mute->add_color_override("icon_color_pressed", mute_color);
- bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons"));
- bypass->add_color_override("icon_color_pressed", bypass_color);
-
- bus_options->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons"));
-
- update_bus();
- set_process(true);
- }
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+
+ for (int i = 0; i < CHANNELS_MAX; i++) {
+ channel[i].vu_l->set_under_texture(get_icon("BusVuEmpty", "EditorIcons"));
+ channel[i].vu_l->set_progress_texture(get_icon("BusVuFull", "EditorIcons"));
+ channel[i].vu_r->set_under_texture(get_icon("BusVuEmpty", "EditorIcons"));
+ channel[i].vu_r->set_progress_texture(get_icon("BusVuFull", "EditorIcons"));
+ channel[i].prev_active = true;
+ }
- if (p_what == NOTIFICATION_DRAW) {
+ disabled_vu = get_icon("BusVuFrozen", "EditorIcons");
- if (has_focus()) {
- draw_style_box(get_stylebox("focus", "Button"), Rect2(Vector2(), get_size()));
- } else if (is_master) {
- draw_style_box(get_stylebox("disabled", "Button"), Rect2(Vector2(), get_size()));
- }
- }
+ Color solo_color = Color::html(EditorSettings::get_singleton()->is_dark_theme() ? "#ffe337" : "#ffeb70");
+ Color mute_color = Color::html(EditorSettings::get_singleton()->is_dark_theme() ? "#ff2929" : "#ff7070");
+ Color bypass_color = Color::html(EditorSettings::get_singleton()->is_dark_theme() ? "#22ccff" : "#70deff");
- if (p_what == NOTIFICATION_PROCESS) {
+ solo->set_icon(get_icon("AudioBusSolo", "EditorIcons"));
+ solo->add_color_override("icon_color_pressed", solo_color);
+ mute->set_icon(get_icon("AudioBusMute", "EditorIcons"));
+ mute->add_color_override("icon_color_pressed", mute_color);
+ bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons"));
+ bypass->add_color_override("icon_color_pressed", bypass_color);
- if (cc != AudioServer::get_singleton()->get_bus_channels(get_index())) {
- cc = AudioServer::get_singleton()->get_bus_channels(get_index());
- _update_visible_channels();
- }
+ bus_options->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons"));
- for (int i = 0; i < cc; i++) {
- float real_peak[2] = { -100, -100 };
- bool activity_found = false;
+ update_bus();
+ set_process(true);
+ } break;
+ case NOTIFICATION_DRAW: {
- if (AudioServer::get_singleton()->is_bus_channel_active(get_index(), i)) {
- activity_found = true;
- real_peak[0] = MAX(real_peak[0], AudioServer::get_singleton()->get_bus_peak_volume_left_db(get_index(), i));
- real_peak[1] = MAX(real_peak[1], AudioServer::get_singleton()->get_bus_peak_volume_right_db(get_index(), i));
+ if (is_master) {
+ draw_style_box(get_stylebox("disabled", "Button"), Rect2(Vector2(), get_size()));
+ } else if (has_focus()) {
+ draw_style_box(get_stylebox("focus", "Button"), Rect2(Vector2(), get_size()));
+ } else {
+ draw_style_box(get_stylebox("panel", "TabContainer"), Rect2(Vector2(), get_size()));
}
- if (real_peak[0] > channel[i].peak_l) {
- channel[i].peak_l = real_peak[0];
- } else {
- channel[i].peak_l -= get_process_delta_time() * 60.0;
+ if (get_index() != 0 && hovering_drop) {
+ Color accent = get_color("accent_color", "Editor");
+ accent.a *= 0.7;
+ draw_rect(Rect2(Point2(), get_size()), accent, false);
}
+ } break;
+ case NOTIFICATION_PROCESS: {
- if (real_peak[1] > channel[i].peak_r) {
- channel[i].peak_r = real_peak[1];
- } else {
- channel[i].peak_r -= get_process_delta_time() * 60.0;
+ if (cc != AudioServer::get_singleton()->get_bus_channels(get_index())) {
+ cc = AudioServer::get_singleton()->get_bus_channels(get_index());
+ _update_visible_channels();
}
- channel[i].vu_l->set_value(channel[i].peak_l);
- channel[i].vu_r->set_value(channel[i].peak_r);
+ for (int i = 0; i < cc; i++) {
+ float real_peak[2] = { -100, -100 };
+ bool activity_found = false;
- if (activity_found != channel[i].prev_active) {
- if (activity_found) {
- channel[i].vu_l->set_over_texture(Ref<Texture>());
- channel[i].vu_r->set_over_texture(Ref<Texture>());
+ if (AudioServer::get_singleton()->is_bus_channel_active(get_index(), i)) {
+ activity_found = true;
+ real_peak[0] = MAX(real_peak[0], AudioServer::get_singleton()->get_bus_peak_volume_left_db(get_index(), i));
+ real_peak[1] = MAX(real_peak[1], AudioServer::get_singleton()->get_bus_peak_volume_right_db(get_index(), i));
+ }
+
+ if (real_peak[0] > channel[i].peak_l) {
+ channel[i].peak_l = real_peak[0];
} else {
- channel[i].vu_l->set_over_texture(disabled_vu);
- channel[i].vu_r->set_over_texture(disabled_vu);
+ channel[i].peak_l -= get_process_delta_time() * 60.0;
}
- channel[i].prev_active = activity_found;
- }
- }
- }
+ if (real_peak[1] > channel[i].peak_r) {
+ channel[i].peak_r = real_peak[1];
+ } else {
+ channel[i].peak_r -= get_process_delta_time() * 60.0;
+ }
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ channel[i].vu_l->set_value(channel[i].peak_l);
+ channel[i].vu_r->set_value(channel[i].peak_r);
- for (int i = 0; i < CHANNELS_MAX; i++) {
- channel[i].peak_l = -100;
- channel[i].peak_r = -100;
- channel[i].prev_active = true;
- }
+ if (activity_found != channel[i].prev_active) {
+ if (activity_found) {
+ channel[i].vu_l->set_over_texture(Ref<Texture>());
+ channel[i].vu_r->set_over_texture(Ref<Texture>());
+ } else {
+ channel[i].vu_l->set_over_texture(disabled_vu);
+ channel[i].vu_r->set_over_texture(disabled_vu);
+ }
- set_process(is_visible_in_tree());
- }
+ channel[i].prev_active = activity_found;
+ }
+ }
+ } break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
- if (p_what == NOTIFICATION_THEME_CHANGED) {
+ for (int i = 0; i < CHANNELS_MAX; i++) {
+ channel[i].peak_l = -100;
+ channel[i].peak_r = -100;
+ channel[i].prev_active = true;
+ }
- for (int i = 0; i < CHANNELS_MAX; i++) {
- channel[i].vu_l->set_under_texture(get_icon("BusVuEmpty", "EditorIcons"));
- channel[i].vu_l->set_progress_texture(get_icon("BusVuFull", "EditorIcons"));
- channel[i].vu_r->set_under_texture(get_icon("BusVuEmpty", "EditorIcons"));
- channel[i].vu_r->set_progress_texture(get_icon("BusVuFull", "EditorIcons"));
- channel[i].prev_active = true;
- }
+ set_process(is_visible_in_tree());
+ } break;
+ case NOTIFICATION_THEME_CHANGED: {
- disabled_vu = get_icon("BusVuFrozen", "EditorIcons");
+ for (int i = 0; i < CHANNELS_MAX; i++) {
+ channel[i].vu_l->set_under_texture(get_icon("BusVuEmpty", "EditorIcons"));
+ channel[i].vu_l->set_progress_texture(get_icon("BusVuFull", "EditorIcons"));
+ channel[i].vu_r->set_under_texture(get_icon("BusVuEmpty", "EditorIcons"));
+ channel[i].vu_r->set_progress_texture(get_icon("BusVuFull", "EditorIcons"));
+ channel[i].prev_active = true;
+ }
- solo->set_icon(get_icon("AudioBusSolo", "EditorIcons"));
- mute->set_icon(get_icon("AudioBusMute", "EditorIcons"));
- bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons"));
+ disabled_vu = get_icon("BusVuFrozen", "EditorIcons");
- bus_options->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons"));
+ solo->set_icon(get_icon("AudioBusSolo", "EditorIcons"));
+ mute->set_icon(get_icon("AudioBusMute", "EditorIcons"));
+ bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons"));
+
+ bus_options->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons"));
+ } break;
+ case NOTIFICATION_MOUSE_EXIT:
+ case NOTIFICATION_DRAG_END: {
+
+ if (hovering_drop) {
+ hovering_drop = false;
+ update();
+ }
+ } break;
}
}
@@ -553,6 +567,7 @@ Variant EditorAudioBus::get_drag_data(const Point2 &p_point) {
Control *c = memnew(Control);
Panel *p = memnew(Panel);
c->add_child(p);
+ p->set_modulate(Color(1, 1, 1, 0.7));
p->add_style_override("panel", get_stylebox("focus", "Button"));
p->set_size(get_size());
p->set_position(-p_point);
@@ -560,21 +575,29 @@ Variant EditorAudioBus::get_drag_data(const Point2 &p_point) {
Dictionary d;
d["type"] = "move_audio_bus";
d["index"] = get_index();
- emit_signal("drop_end_request");
+
+ if (get_index() < AudioServer::get_singleton()->get_bus_count() - 1) {
+ emit_signal("drop_end_request");
+ }
+
return d;
}
bool EditorAudioBus::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
- if (get_index() == 0)
+ if (get_index() == 0) {
return false;
+ }
+
Dictionary d = p_data;
- if (d.has("type") && String(d["type"]) == "move_audio_bus") {
+ if (d.has("type") && String(d["type"]) == "move_audio_bus" && (int)d["index"] != get_index()) {
+ hovering_drop = true;
return true;
}
return false;
}
+
void EditorAudioBus::drop_data(const Point2 &p_point, const Variant &p_data) {
Dictionary d = p_data;
@@ -589,7 +612,6 @@ Variant EditorAudioBus::get_drag_data_fw(const Point2 &p_point, Control *p_from)
}
Variant md = item->get_metadata(0);
-
if (md.get_type() == Variant::INT) {
Dictionary fxd;
fxd["type"] = "audio_bus_effect";
@@ -749,6 +771,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
buses = p_buses;
updating_bus = false;
is_master = p_is_master;
+ hovering_drop = false;
set_tooltip(TTR("Audio Bus, Drag and Drop to rearrange."));
@@ -756,7 +779,6 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
add_child(vb);
set_v_size_flags(SIZE_EXPAND_FILL);
- set_custom_minimum_size(Size2(110, 0) * EDSCALE);
track_name = memnew(LineEdit);
track_name->connect("text_entered", this, "_name_changed");
@@ -800,7 +822,9 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
child->add_style_override("pressed", sbempty);
}
- vb->add_child(memnew(HSeparator));
+ HSeparator *separator = memnew(HSeparator);
+ separator->set_mouse_filter(MOUSE_FILTER_PASS);
+ vb->add_child(separator);
HBoxContainer *hb = memnew(HBoxContainer);
vb->add_child(hb);
@@ -811,20 +835,19 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
slider->set_clip_contents(false);
audio_value_preview_box = memnew(Panel);
- {
- HBoxContainer *audioprev_hbc = memnew(HBoxContainer);
- audioprev_hbc->set_v_size_flags(SIZE_EXPAND_FILL);
- audioprev_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
- audioprev_hbc->set_mouse_filter(MOUSE_FILTER_PASS);
- audio_value_preview_box->add_child(audioprev_hbc);
-
- audio_value_preview_label = memnew(Label);
- audio_value_preview_label->set_v_size_flags(SIZE_EXPAND_FILL);
- audio_value_preview_label->set_h_size_flags(SIZE_EXPAND_FILL);
- audio_value_preview_label->set_mouse_filter(MOUSE_FILTER_PASS);
-
- audioprev_hbc->add_child(audio_value_preview_label);
- }
+ HBoxContainer *audioprev_hbc = memnew(HBoxContainer);
+ audioprev_hbc->set_v_size_flags(SIZE_EXPAND_FILL);
+ audioprev_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
+ audioprev_hbc->set_mouse_filter(MOUSE_FILTER_PASS);
+ audio_value_preview_box->add_child(audioprev_hbc);
+
+ audio_value_preview_label = memnew(Label);
+ audio_value_preview_label->set_v_size_flags(SIZE_EXPAND_FILL);
+ audio_value_preview_label->set_h_size_flags(SIZE_EXPAND_FILL);
+ audio_value_preview_label->set_mouse_filter(MOUSE_FILTER_PASS);
+
+ audioprev_hbc->add_child(audio_value_preview_label);
+
slider->add_child(audio_value_preview_box);
audio_value_preview_box->set_as_toplevel(true);
Ref<StyleBoxFlat> panel_style = memnew(StyleBoxFlat);
@@ -863,17 +886,18 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
channel[i].peak_r = 0.0f;
}
- scale = memnew(EditorAudioMeterNotches);
+ EditorAudioMeterNotches *scale = memnew(EditorAudioMeterNotches);
for (float db = 6.0f; db >= -80.0f; db -= 6.0f) {
bool renderNotch = (db >= -6.0f || db == -24.0f || db == -72.0f);
scale->add_notch(_scaled_db_to_normalized_volume(db), db, renderNotch);
}
+ scale->set_mouse_filter(MOUSE_FILTER_PASS);
hb->add_child(scale);
effects = memnew(Tree);
effects->set_hide_root(true);
- effects->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
+ effects->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
effects->set_hide_folding(true);
effects->set_v_size_flags(SIZE_EXPAND_FILL);
vb->add_child(effects);
@@ -923,6 +947,36 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
delete_effect_popup->connect("index_pressed", this, "_delete_effect_pressed");
}
+void EditorAudioBusDrop::_notification(int p_what) {
+
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ draw_style_box(get_stylebox("normal", "Button"), Rect2(Vector2(), get_size()));
+
+ if (hovering_drop) {
+ Color accent = get_color("accent_color", "Editor");
+ accent.a *= 0.7;
+ draw_rect(Rect2(Point2(), get_size()), accent, false);
+ }
+ } break;
+ case NOTIFICATION_MOUSE_ENTER: {
+
+ if (!hovering_drop) {
+ hovering_drop = true;
+ update();
+ }
+ } break;
+ case NOTIFICATION_MOUSE_EXIT:
+ case NOTIFICATION_DRAG_END: {
+
+ if (hovering_drop) {
+ hovering_drop = false;
+ update();
+ }
+ } break;
+ }
+}
+
bool EditorAudioBusDrop::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
Dictionary d = p_data;
@@ -932,10 +986,11 @@ bool EditorAudioBusDrop::can_drop_data(const Point2 &p_point, const Variant &p_d
return false;
}
+
void EditorAudioBusDrop::drop_data(const Point2 &p_point, const Variant &p_data) {
Dictionary d = p_data;
- emit_signal("dropped", d["index"], -1);
+ emit_signal("dropped", d["index"], AudioServer::get_singleton()->get_bus_count());
}
void EditorAudioBusDrop::_bind_methods() {
@@ -944,6 +999,8 @@ void EditorAudioBusDrop::_bind_methods() {
}
EditorAudioBusDrop::EditorAudioBusDrop() {
+
+ hovering_drop = false;
}
void EditorAudioBuses::_update_buses() {
@@ -976,37 +1033,43 @@ EditorAudioBuses *EditorAudioBuses::register_editor() {
void EditorAudioBuses::_notification(int p_what) {
- if (p_what == NOTIFICATION_READY) {
- _update_buses();
- }
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
- if (p_what == NOTIFICATION_DRAG_END) {
- if (drop_end) {
- drop_end->queue_delete();
- drop_end = NULL;
- }
- }
+ bus_scroll->add_style_override("bg", get_stylebox("bg", "Tree"));
+ } break;
+ case NOTIFICATION_READY: {
- if (p_what == NOTIFICATION_PROCESS) {
+ _update_buses();
+ } break;
+ case NOTIFICATION_DRAG_END: {
- //check if anything was edited
- bool edited = AudioServer::get_singleton()->is_edited();
- for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
- for (int j = 0; j < AudioServer::get_singleton()->get_bus_effect_count(i); j++) {
- Ref<AudioEffect> effect = AudioServer::get_singleton()->get_bus_effect(i, j);
- if (effect->is_edited()) {
- edited = true;
- effect->set_edited(false);
+ if (drop_end) {
+ drop_end->queue_delete();
+ drop_end = NULL;
+ }
+ } break;
+ case NOTIFICATION_PROCESS: {
+
+ // Check if anything was edited.
+ bool edited = AudioServer::get_singleton()->is_edited();
+ for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
+ for (int j = 0; j < AudioServer::get_singleton()->get_bus_effect_count(i); j++) {
+ Ref<AudioEffect> effect = AudioServer::get_singleton()->get_bus_effect(i, j);
+ if (effect->is_edited()) {
+ edited = true;
+ effect->set_edited(false);
+ }
}
}
- }
-
- AudioServer::get_singleton()->set_edited(false);
- if (edited) {
+ AudioServer::get_singleton()->set_edited(false);
- save_timer->start();
- }
+ if (edited) {
+ save_timer->start();
+ }
+ } break;
}
}
@@ -1014,7 +1077,6 @@ void EditorAudioBuses::_add_bus() {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- //need to simulate new name, so we can undi :(
ur->create_action(TTR("Add Audio Bus"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_count", AudioServer::get_singleton()->get_bus_count() + 1);
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_count", AudioServer::get_singleton()->get_bus_count());
@@ -1119,21 +1181,12 @@ void EditorAudioBuses::_request_drop_end() {
void EditorAudioBuses::_drop_at_index(int p_bus, int p_index) {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
-
- //need to simulate new name, so we can undi :(
ur->create_action(TTR("Move Audio Bus"));
+
ur->add_do_method(AudioServer::get_singleton(), "move_bus", p_bus, p_index);
- int final_pos;
- if (p_index == p_bus) {
- final_pos = p_bus;
- } else if (p_index == -1) {
- final_pos = AudioServer::get_singleton()->get_bus_count() - 1;
- } else if (p_index < p_bus) {
- final_pos = p_index;
- } else {
- final_pos = p_index - 1;
- }
- ur->add_undo_method(AudioServer::get_singleton(), "move_bus", final_pos, p_bus);
+ int real_bus = p_index > p_bus ? p_bus : p_bus + 1;
+ int real_index = p_index > p_bus ? p_index - 1 : p_index;
+ ur->add_undo_method(AudioServer::get_singleton(), "move_bus", real_index, real_bus);
ur->add_do_method(this, "_update_buses");
ur->add_undo_method(this, "_update_buses");
@@ -1189,7 +1242,7 @@ void EditorAudioBuses::_load_default_layout() {
}
edited_path = layout_path;
- file->set_text(layout_path.get_file());
+ file->set_text(String(TTR("Layout")) + ": " + layout_path.get_file());
AudioServer::get_singleton()->set_bus_layout(state);
_update_buses();
EditorNode::get_singleton()->get_undo_redo()->clear_history();
@@ -1206,7 +1259,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
}
edited_path = p_string;
- file->set_text(p_string.get_file());
+ file->set_text(String(TTR("Layout")) + ": " + p_string.get_file());
AudioServer::get_singleton()->set_bus_layout(state);
_update_buses();
EditorNode::get_singleton()->get_undo_redo()->clear_history();
@@ -1228,7 +1281,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
}
edited_path = p_string;
- file->set_text(p_string.get_file());
+ file->set_text(String(TTR("Layout")) + ": " + p_string.get_file());
_update_buses();
EditorNode::get_singleton()->get_undo_redo()->clear_history();
call_deferred("_select_layout");
@@ -1262,19 +1315,20 @@ EditorAudioBuses::EditorAudioBuses() {
top_hb = memnew(HBoxContainer);
add_child(top_hb);
- file = memnew(ToolButton);
- file->set_text("default_bus_layout.tres");
+ file = memnew(Label);
+ file->set_text(String(TTR("Layout")) + ": " + "default_bus_layout.tres");
+ file->set_clip_text(true);
+ file->set_h_size_flags(SIZE_EXPAND_FILL);
top_hb->add_child(file);
- file->connect("pressed", this, "_select_layout");
add = memnew(Button);
top_hb->add_child(add);
add->set_text(TTR("Add Bus"));
add->set_tooltip(TTR("Add a new Audio Bus to this layout."));
-
add->connect("pressed", this, "_add_bus");
- top_hb->add_spacer();
+ VSeparator *separator = memnew(VSeparator);
+ top_hb->add_child(separator);
load = memnew(Button);
load->set_text(TTR("Load"));
@@ -1301,7 +1355,6 @@ EditorAudioBuses::EditorAudioBuses() {
_new->connect("pressed", this, "_new_layout");
bus_scroll = memnew(ScrollContainer);
- bus_scroll->add_style_override("panel", memnew(StyleBoxEmpty));
bus_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
bus_scroll->set_enable_h_scroll(true);
bus_scroll->set_enable_v_scroll(false);
@@ -1377,38 +1430,65 @@ AudioBusesEditorPlugin::AudioBusesEditorPlugin(EditorAudioBuses *p_node) {
AudioBusesEditorPlugin::~AudioBusesEditorPlugin() {
}
-void EditorAudioMeterNotches::add_notch(float normalized_offset, float db_value, bool render_value) {
- notches.push_back(AudioNotch(normalized_offset, db_value, render_value));
+void EditorAudioMeterNotches::add_notch(float p_normalized_offset, float p_db_value, bool p_render_value) {
+
+ notches.push_back(AudioNotch(p_normalized_offset, p_db_value, p_render_value));
+}
+
+Size2 EditorAudioMeterNotches::get_minimum_size() const {
+
+ Ref<Font> font = get_font("font", "Label");
+ float font_height = font->get_height();
+
+ float width = 0;
+ float height = top_padding + btm_padding;
+
+ for (uint8_t i = 0; i < notches.size(); i++) {
+ if (notches[i].render_db_value) {
+ width = MAX(width, font->get_string_size(String::num(Math::abs(notches[i].db_value)) + "dB").x);
+ height += font_height;
+ }
+ }
+ width += line_length + label_space;
+
+ return Size2(width, height);
}
void EditorAudioMeterNotches::_bind_methods() {
+
ClassDB::bind_method("add_notch", &EditorAudioMeterNotches::add_notch);
ClassDB::bind_method("_draw_audio_notches", &EditorAudioMeterNotches::_draw_audio_notches);
}
void EditorAudioMeterNotches::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1.0f, 1.0f, 1.0f, 0.8f) : Color(0.0f, 0.0f, 0.0f, 0.8f);
- _draw_audio_notches();
+
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1, 1, 1) : Color(0, 0, 0);
+ } break;
+ case NOTIFICATION_DRAW: {
+ _draw_audio_notches();
+ } break;
}
}
void EditorAudioMeterNotches::_draw_audio_notches() {
+
Ref<Font> font = get_font("font", "Label");
float font_height = font->get_height();
for (uint8_t i = 0; i < notches.size(); i++) {
AudioNotch n = notches[i];
- draw_line(Vector2(0.0f, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
+ draw_line(Vector2(0, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
Vector2(line_length, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
notch_color,
- 1.0f);
+ 1);
if (n.render_db_value) {
draw_string(font,
Vector2(line_length + label_space,
(1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + (font_height / 4) + top_padding),
- String("{0}dB").format(varray(Math::abs(n.db_value))),
+ String::num(Math::abs(n.db_value)) + "dB",
notch_color);
}
}
@@ -1419,7 +1499,6 @@ EditorAudioMeterNotches::EditorAudioMeterNotches() :
label_space(2.0f),
btm_padding(9.0f),
top_padding(5.0f) {
- this->set_v_size_flags(SIZE_EXPAND_FILL);
- this->set_h_size_flags(SIZE_EXPAND_FILL);
- notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1.0f, 1.0f, 1.0f, 0.8f) : Color(0.0f, 0.0f, 0.0f, 0.8f);
+
+ notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1, 1, 1) : Color(0, 0, 0);
}
diff --git a/editor/editor_audio_buses.h b/editor/editor_audio_buses.h
index 50f2101fd8..20890fd3b5 100644
--- a/editor/editor_audio_buses.h
+++ b/editor/editor_audio_buses.h
@@ -72,7 +72,6 @@ class EditorAudioBus : public PanelContainer {
TextureProgress *vu_r;
} channel[CHANNELS_MAX];
- class EditorAudioMeterNotches *scale;
OptionButton *send;
PopupMenu *effect_options;
@@ -90,8 +89,8 @@ class EditorAudioBus : public PanelContainer {
Tree *effects;
bool updating_bus;
-
bool is_master;
+ mutable bool hovering_drop;
void _gui_input(const Ref<InputEvent> &p_event);
void _bus_popup_pressed(int p_option);
@@ -137,15 +136,18 @@ public:
EditorAudioBus(EditorAudioBuses *p_buses = NULL, bool p_is_master = false);
};
-class EditorAudioBusDrop : public Panel {
+class EditorAudioBusDrop : public Control {
- GDCLASS(EditorAudioBusDrop, Panel);
+ GDCLASS(EditorAudioBusDrop, Control);
virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
virtual void drop_data(const Point2 &p_point, const Variant &p_data);
+ mutable bool hovering_drop;
+
protected:
static void _bind_methods();
+ void _notification(int p_what);
public:
EditorAudioBusDrop();
@@ -157,13 +159,14 @@ class EditorAudioBuses : public VBoxContainer {
HBoxContainer *top_hb;
- Button *add;
ScrollContainer *bus_scroll;
HBoxContainer *bus_hb;
EditorAudioBusDrop *drop_end;
- Button *file;
+ Label *file;
+
+ Button *add;
Button *load;
Button *save_as;
Button *_default;
@@ -242,7 +245,8 @@ public:
float top_padding;
Color notch_color;
- void add_notch(float normalized_offset, float db_value, bool render_value = false);
+ void add_notch(float p_normalized_offset, float p_db_value, bool p_render_value = false);
+ Size2 get_minimum_size() const;
private:
static void _bind_methods();
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index f61a831015..38f30df169 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -560,6 +560,7 @@ void EditorData::move_edited_scene_index(int p_idx, int p_to_idx) {
ERR_FAIL_INDEX(p_to_idx, edited_scene.size());
SWAP(edited_scene.write[p_idx], edited_scene.write[p_to_idx]);
}
+
void EditorData::remove_scene(int p_idx) {
ERR_FAIL_INDEX(p_idx, edited_scene.size());
if (edited_scene[p_idx].root) {
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 37f148bdbb..b5325a07a5 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -907,7 +907,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c
String tmppath = EditorSettings::get_singleton()->get_cache_dir().plus_file("packtmp");
FileAccess *ftmp = FileAccess::open(tmppath, FileAccess::WRITE);
- ERR_FAIL_COND_V(!ftmp, ERR_CANT_CREATE)
+ ERR_FAIL_COND_V(!ftmp, ERR_CANT_CREATE);
PackData pd;
pd.ep = &ep;
@@ -924,7 +924,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c
pd.file_ofs.sort(); //do sort, so we can do binary search later
FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V(!f, ERR_CANT_CREATE)
+ ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
f->store_32(0x43504447); //GDPK
f->store_32(1); //pack version
f->store_32(VERSION_MAJOR);
@@ -977,7 +977,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c
ftmp = FileAccess::open(tmppath, FileAccess::READ);
if (!ftmp) {
memdelete(f);
- ERR_FAIL_COND_V(!ftmp, ERR_CANT_CREATE)
+ ERR_FAIL_COND_V(!ftmp, ERR_CANT_CREATE);
}
const int bufsize = 16384;
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index 714df44e25..56358f059a 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* editor_feature_profile.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 "editor_feature_profile.h"
#include "core/io/json.h"
#include "core/os/dir_access.h"
diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h
index b7c2ebc1b2..d670719d7d 100644
--- a/editor/editor_feature_profile.h
+++ b/editor/editor_feature_profile.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* editor_feature_profile.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 EDITOR_FEATURE_PROFILE_H
#define EDITOR_FEATURE_PROFILE_H
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 3d198dec67..8025fc9795 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -63,6 +63,7 @@ void EditorFileDialog::_notification(int p_what) {
dir_up->set_icon(get_icon("ArrowUp", "EditorIcons"));
refresh->set_icon(get_icon("Reload", "EditorIcons"));
favorite->set_icon(get_icon("Favorites", "EditorIcons"));
+ show_hidden->set_icon(get_icon("GuiVisibilityVisible", "EditorIcons"));
fav_up->set_icon(get_icon("MoveUp", "EditorIcons"));
fav_down->set_icon(get_icon("MoveDown", "EditorIcons"));
@@ -86,9 +87,9 @@ void EditorFileDialog::_notification(int p_what) {
} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
- bool show_hidden = EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files");
- if (show_hidden_files != show_hidden)
- set_show_hidden_files(show_hidden);
+ bool is_showing_hidden = EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files");
+ if (show_hidden_files != is_showing_hidden)
+ set_show_hidden_files(is_showing_hidden);
set_display_mode((DisplayMode)EditorSettings::get_singleton()->get("filesystem/file_dialog/display_mode").operator int());
// update icons
@@ -140,7 +141,7 @@ void EditorFileDialog::_unhandled_input(const Ref<InputEvent> &p_event) {
handled = true;
}
if (ED_IS_SHORTCUT("file_dialog/toggle_favorite", p_event)) {
- _favorite_toggled(favorite->is_pressed());
+ _favorite_pressed();
handled = true;
}
if (ED_IS_SHORTCUT("file_dialog/toggle_mode", p_event)) {
@@ -231,6 +232,7 @@ void EditorFileDialog::_file_entered(const String &p_file) {
}
void EditorFileDialog::_save_confirm_pressed() {
+
String f = dir_access->get_current_dir().plus_file(file->get_text());
_save_to_recent();
hide();
@@ -717,20 +719,19 @@ void EditorFileDialog::update_file_list() {
List<String> files;
List<String> dirs;
- bool isdir;
- bool ishidden;
- bool show_hidden = show_hidden_files;
+ bool is_dir;
+ bool is_hidden;
String item;
- while ((item = dir_access->get_next(&isdir)) != "") {
+ while ((item = dir_access->get_next(&is_dir)) != "") {
if (item == "." || item == "..")
continue;
- ishidden = dir_access->current_is_hidden();
+ is_hidden = dir_access->current_is_hidden();
- if (show_hidden || !ishidden) {
- if (!isdir)
+ if (show_hidden_files || !is_hidden) {
+ if (!is_dir)
files.push_back(item);
else
dirs.push_back(item);
@@ -1130,6 +1131,7 @@ void EditorFileDialog::_update_drives() {
}
void EditorFileDialog::_favorite_selected(int p_idx) {
+
dir_access->change_dir(favorites->get_item_metadata(p_idx));
file->set_text("");
update_dir();
@@ -1210,7 +1212,7 @@ void EditorFileDialog::_update_favorites() {
favorites->add_item(name, folder_icon);
} else {
- continue; // We don't handle favorite files here
+ continue; // We don't handle favorite files here.
}
favorites->set_item_metadata(favorites->get_item_count() - 1, favorited[i]);
@@ -1218,11 +1220,12 @@ void EditorFileDialog::_update_favorites() {
if (setthis) {
favorite->set_pressed(true);
favorites->set_current(favorites->get_item_count() - 1);
+ recent->unselect_all();
}
}
}
-void EditorFileDialog::_favorite_toggled(bool p_toggle) {
+void EditorFileDialog::_favorite_pressed() {
bool res = access == ACCESS_RESOURCES;
String cd = get_current_dir();
@@ -1375,7 +1378,7 @@ void EditorFileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_go_forward"), &EditorFileDialog::_go_forward);
ClassDB::bind_method(D_METHOD("_go_up"), &EditorFileDialog::_go_up);
- ClassDB::bind_method(D_METHOD("_favorite_toggled"), &EditorFileDialog::_favorite_toggled);
+ ClassDB::bind_method(D_METHOD("_favorite_pressed"), &EditorFileDialog::_favorite_pressed);
ClassDB::bind_method(D_METHOD("_favorite_selected"), &EditorFileDialog::_favorite_selected);
ClassDB::bind_method(D_METHOD("_favorite_move_up"), &EditorFileDialog::_favorite_move_up);
ClassDB::bind_method(D_METHOD("_favorite_move_down"), &EditorFileDialog::_favorite_move_down);
@@ -1411,6 +1414,7 @@ void EditorFileDialog::_bind_methods() {
void EditorFileDialog::set_show_hidden_files(bool p_show) {
show_hidden_files = p_show;
+ show_hidden->set_pressed(p_show);
invalidate();
}
@@ -1516,17 +1520,23 @@ EditorFileDialog::EditorFileDialog() {
pathhb->add_child(refresh);
favorite = memnew(ToolButton);
- favorite->set_flat(true);
favorite->set_toggle_mode(true);
favorite->set_tooltip(TTR("(Un)favorite current folder."));
- favorite->connect("toggled", this, "_favorite_toggled");
+ favorite->connect("pressed", this, "_favorite_pressed");
pathhb->add_child(favorite);
- Ref<ButtonGroup> view_mode_group;
- view_mode_group.instance();
+ show_hidden = memnew(ToolButton);
+ show_hidden->set_toggle_mode(true);
+ show_hidden->set_pressed(is_showing_hidden_files());
+ show_hidden->set_tooltip(TTR("Toggle visibility of hidden files."));
+ show_hidden->connect("toggled", this, "set_show_hidden_files");
+ pathhb->add_child(show_hidden);
pathhb->add_child(memnew(VSeparator));
+ Ref<ButtonGroup> view_mode_group;
+ view_mode_group.instance();
+
mode_thumbnails = memnew(ToolButton);
mode_thumbnails->connect("pressed", this, "set_display_mode", varray(DISPLAY_THUMBNAILS));
mode_thumbnails->set_toggle_mode(true);
@@ -1586,6 +1596,7 @@ EditorFileDialog::EditorFileDialog() {
rec_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE);
rec_vb->set_v_size_flags(SIZE_EXPAND_FILL);
recent = memnew(ItemList);
+ recent->set_allow_reselect(true);
rec_vb->add_margin_child(TTR("Recent:"), recent, true);
recent->connect("item_selected", this, "_recent_selected");
@@ -1602,7 +1613,7 @@ EditorFileDialog::EditorFileDialog() {
list_vb->add_child(memnew(Label(TTR("Directories & Files:"))));
preview_hb->add_child(list_vb);
- // Item (files and folders) list with context menu
+ // Item (files and folders) list with context menu.
item_list = memnew(ItemList);
item_list->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -1615,7 +1626,7 @@ EditorFileDialog::EditorFileDialog() {
item_menu->connect("id_pressed", this, "_item_menu_id_pressed");
add_child(item_menu);
- // Other stuff
+ // Other stuff.
preview_vb = memnew(VBoxContainer);
preview_hb->add_child(preview_vb);
@@ -1634,7 +1645,7 @@ EditorFileDialog::EditorFileDialog() {
filter = memnew(OptionButton);
filter->set_stretch_ratio(3);
filter->set_h_size_flags(SIZE_EXPAND_FILL);
- filter->set_clip_text(true); // too many extensions overflow it
+ filter->set_clip_text(true); // Too many extensions overflow it.
filename_hbc->add_child(filter);
filename_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
item_vb->add_child(filename_hbc);
diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h
index edaccac51d..6578be8563 100644
--- a/editor/editor_file_dialog.h
+++ b/editor/editor_file_dialog.h
@@ -116,11 +116,13 @@ private:
DirAccess *dir_access;
ConfirmationDialog *confirm_save;
DependencyRemoveDialog *remove_dialog;
+
ToolButton *mode_thumbnails;
ToolButton *mode_list;
ToolButton *refresh;
ToolButton *favorite;
+ ToolButton *show_hidden;
ToolButton *fav_up;
ToolButton *fav_down;
@@ -150,7 +152,7 @@ private:
void update_filters();
void _update_favorites();
- void _favorite_toggled(bool p_toggle);
+ void _favorite_pressed();
void _favorite_selected(int p_idx);
void _favorite_move_up();
void _favorite_move_down();
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index cddabbc4e4..40ecffbb3b 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -204,9 +204,8 @@ void editor_register_fonts(Ref<Theme> p_theme) {
dfmono->set_antialiased(font_source_antialiased);
dfmono->set_hinting(font_source_hinting);
dfmono->set_font_ptr(_font_Hack_Regular, _font_Hack_Regular_size);
- //dfd->set_force_autohinter(true); //just looks better..i think?
- int default_font_size = int(EditorSettings::get_singleton()->get("interface/editor/main_font_size")) * EDSCALE;
+ int default_font_size = int(EDITOR_GET("interface/editor/main_font_size")) * EDSCALE;
// Default font
MAKE_DEFAULT_FONT(df, default_font_size);
@@ -220,15 +219,14 @@ void editor_register_fonts(Ref<Theme> p_theme) {
MAKE_BOLD_FONT(df_title, default_font_size + 2 * EDSCALE);
p_theme->set_font("title", "EditorFonts", df_title);
- // Doc font
- MAKE_BOLD_FONT(df_doc_title, int(EDITOR_DEF("text_editor/help/help_title_font_size", 23)) * EDSCALE);
-
- MAKE_DEFAULT_FONT(df_doc, int(EDITOR_DEF("text_editor/help/help_font_size", 15)) * EDSCALE);
-
+ // Documentation fonts
+ MAKE_DEFAULT_FONT(df_doc, int(EDITOR_GET("text_editor/help/help_font_size")) * EDSCALE);
+ MAKE_BOLD_FONT(df_doc_bold, int(EDITOR_GET("text_editor/help/help_font_size")) * EDSCALE);
+ MAKE_BOLD_FONT(df_doc_title, int(EDITOR_GET("text_editor/help/help_title_font_size")) * EDSCALE);
+ MAKE_SOURCE_FONT(df_doc_code, int(EDITOR_GET("text_editor/help/help_source_font_size")) * EDSCALE);
p_theme->set_font("doc", "EditorFonts", df_doc);
+ p_theme->set_font("doc_bold", "EditorFonts", df_doc_bold);
p_theme->set_font("doc_title", "EditorFonts", df_doc_title);
-
- MAKE_SOURCE_FONT(df_doc_code, int(EDITOR_GET("text_editor/help/help_source_font_size")) * EDSCALE);
p_theme->set_font("doc_source", "EditorFonts", df_doc_code);
// Ruler font
@@ -236,13 +234,13 @@ void editor_register_fonts(Ref<Theme> p_theme) {
p_theme->set_font("rulers", "EditorFonts", df_rulers);
// Code font
- MAKE_SOURCE_FONT(df_code, int(EditorSettings::get_singleton()->get("interface/editor/code_font_size")) * EDSCALE);
+ MAKE_SOURCE_FONT(df_code, int(EDITOR_GET("interface/editor/code_font_size")) * EDSCALE);
p_theme->set_font("source", "EditorFonts", df_code);
- MAKE_SOURCE_FONT(df_expression, (int(EditorSettings::get_singleton()->get("interface/editor/code_font_size")) - 1) * EDSCALE);
+ MAKE_SOURCE_FONT(df_expression, (int(EDITOR_GET("interface/editor/code_font_size")) - 1) * EDSCALE);
p_theme->set_font("expression", "EditorFonts", df_expression);
- MAKE_SOURCE_FONT(df_output_code, int(EDITOR_DEF("run/output/font_size", 13)) * EDSCALE);
+ MAKE_SOURCE_FONT(df_output_code, int(EDITOR_GET("run/output/font_size")) * EDSCALE);
p_theme->set_font("output_source", "EditorFonts", df_output_code);
MAKE_SOURCE_FONT(df_text_editor_status_code, default_font_size);
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 5917869988..26d793cf65 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -203,8 +203,9 @@ String EditorHelp::_fix_constant(const String &p_constant) const {
if (p_constant.strip_edges() == "2147483647") {
return "0x7FFFFFFF";
}
+
if (p_constant.strip_edges() == "1048575") {
- return "0xfffff";
+ return "0xFFFFF";
}
return p_constant;
@@ -324,6 +325,7 @@ void EditorHelp::_update_doc() {
DocData::ClassDoc cd = doc->class_list[edited_class]; //make a copy, so we can sort without worrying
Ref<Font> doc_font = get_font("doc", "EditorFonts");
+ Ref<Font> doc_bold_font = get_font("doc_bold", "EditorFonts");
Ref<Font> doc_title_font = get_font("doc_title", "EditorFonts");
Ref<Font> doc_code_font = get_font("doc_source", "EditorFonts");
String link_color_text = title_color.to_html(false);
@@ -1131,6 +1133,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
String base_path;
Ref<Font> doc_font = p_rt->get_font("doc", "EditorFonts");
+ Ref<Font> doc_bold_font = p_rt->get_font("doc_bold", "EditorFonts");
Ref<Font> doc_code_font = p_rt->get_font("doc_source", "EditorFonts");
Color font_color_hl = p_rt->get_color("headline_color", "EditorHelp");
Color link_color = p_rt->get_color("accent_color", "Editor").linear_interpolate(font_color_hl, 0.8);
@@ -1218,7 +1221,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
} else if (tag == "b") {
//use bold font
- p_rt->push_font(doc_code_font);
+ p_rt->push_font(doc_bold_font);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "i") {
@@ -1236,13 +1239,13 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
tag_stack.push_front(tag);
} else if (tag == "center") {
- //use monospace font
+ //align to center
p_rt->push_align(RichTextLabel::ALIGN_CENTER);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "br") {
- //use monospace font
+ //force a line break
p_rt->add_newline();
pos = brk_end + 1;
} else if (tag == "u") {
@@ -1253,14 +1256,13 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
tag_stack.push_front(tag);
} else if (tag == "s") {
- //use strikethrough (not supported underline instead)
- p_rt->push_underline();
+ //use strikethrough
+ p_rt->push_strikethrough();
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "url") {
- //use strikethrough (not supported underline instead)
int end = bbcode.find("[", brk_end);
if (end == -1)
end = bbcode.length();
@@ -1277,7 +1279,6 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
tag_stack.push_front("url");
} else if (tag == "img") {
- //use strikethrough (not supported underline instead)
int end = bbcode.find("[", brk_end);
if (end == -1)
end = bbcode.length();
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index 616a52e25b..55ab38ba6c 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -250,6 +250,25 @@ EditorHelpSearch::EditorHelpSearch() {
vbox->add_child(results_tree, true);
}
+bool EditorHelpSearch::Runner::_is_class_disabled_by_feature_profile(const StringName &p_class) {
+
+ Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
+ if (profile.is_null()) {
+ return false;
+ }
+
+ StringName class_name = p_class;
+ while (class_name != StringName()) {
+
+ if (!ClassDB::class_exists(class_name) || profile->is_class_disabled(class_name)) {
+ return true;
+ }
+ class_name = ClassDB::get_parent_class(class_name);
+ }
+
+ return false;
+}
+
bool EditorHelpSearch::Runner::_slice() {
bool phase_done = false;
@@ -299,43 +318,45 @@ bool EditorHelpSearch::Runner::_phase_match_classes_init() {
bool EditorHelpSearch::Runner::_phase_match_classes() {
DocData::ClassDoc &class_doc = iterator_doc->value();
-
- matches[class_doc.name] = ClassMatch();
- ClassMatch &match = matches[class_doc.name];
-
- match.doc = &class_doc;
-
- // Match class name.
- if (search_flags & SEARCH_CLASSES)
- match.name = term == "" || _match_string(term, class_doc.name);
-
- // Match members if the term is long enough.
- if (term.length() > 1) {
- if (search_flags & SEARCH_METHODS)
- for (int i = 0; i < class_doc.methods.size(); i++) {
- String method_name = search_flags & SEARCH_CASE_SENSITIVE ? class_doc.methods[i].name : class_doc.methods[i].name.to_lower();
- if (method_name.find(term) > -1 ||
- (term.begins_with(".") && method_name.begins_with(term.right(1))) ||
- (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
- (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges()))
- match.methods.push_back(const_cast<DocData::MethodDoc *>(&class_doc.methods[i]));
- }
- if (search_flags & SEARCH_SIGNALS)
- for (int i = 0; i < class_doc.signals.size(); i++)
- if (_match_string(term, class_doc.signals[i].name))
- match.signals.push_back(const_cast<DocData::MethodDoc *>(&class_doc.signals[i]));
- if (search_flags & SEARCH_CONSTANTS)
- for (int i = 0; i < class_doc.constants.size(); i++)
- if (_match_string(term, class_doc.constants[i].name))
- match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc.constants[i]));
- if (search_flags & SEARCH_PROPERTIES)
- for (int i = 0; i < class_doc.properties.size(); i++)
- if (_match_string(term, class_doc.properties[i].name))
- match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.properties[i]));
- if (search_flags & SEARCH_THEME_ITEMS)
- for (int i = 0; i < class_doc.theme_properties.size(); i++)
- if (_match_string(term, class_doc.theme_properties[i].name))
- match.theme_properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.theme_properties[i]));
+ if (!_is_class_disabled_by_feature_profile(class_doc.name)) {
+
+ matches[class_doc.name] = ClassMatch();
+ ClassMatch &match = matches[class_doc.name];
+
+ match.doc = &class_doc;
+
+ // Match class name.
+ if (search_flags & SEARCH_CLASSES)
+ match.name = term == "" || _match_string(term, class_doc.name);
+
+ // Match members if the term is long enough.
+ if (term.length() > 1) {
+ if (search_flags & SEARCH_METHODS)
+ for (int i = 0; i < class_doc.methods.size(); i++) {
+ String method_name = search_flags & SEARCH_CASE_SENSITIVE ? class_doc.methods[i].name : class_doc.methods[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ (term.begins_with(".") && method_name.begins_with(term.right(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges()))
+ match.methods.push_back(const_cast<DocData::MethodDoc *>(&class_doc.methods[i]));
+ }
+ if (search_flags & SEARCH_SIGNALS)
+ for (int i = 0; i < class_doc.signals.size(); i++)
+ if (_match_string(term, class_doc.signals[i].name))
+ match.signals.push_back(const_cast<DocData::MethodDoc *>(&class_doc.signals[i]));
+ if (search_flags & SEARCH_CONSTANTS)
+ for (int i = 0; i < class_doc.constants.size(); i++)
+ if (_match_string(term, class_doc.constants[i].name))
+ match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc.constants[i]));
+ if (search_flags & SEARCH_PROPERTIES)
+ for (int i = 0; i < class_doc.properties.size(); i++)
+ if (_match_string(term, class_doc.properties[i].name))
+ match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.properties[i]));
+ if (search_flags & SEARCH_THEME_ITEMS)
+ for (int i = 0; i < class_doc.theme_properties.size(); i++)
+ if (_match_string(term, class_doc.theme_properties[i].name))
+ match.theme_properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.theme_properties[i]));
+ }
}
iterator_doc = iterator_doc->next();
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index 93cf66a0dd..12ffd024a7 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -125,6 +125,8 @@ class EditorHelpSearch::Runner : public Reference {
Map<String, TreeItem *> class_items;
TreeItem *matched_item;
+ bool _is_class_disabled_by_feature_profile(const StringName &p_class);
+
bool _slice();
bool _phase_match_classes_init();
bool _phase_match_classes();
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index ecb9ea5f35..e4ddf44bc4 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -66,7 +66,7 @@ Size2 EditorProperty::get_minimum_size() const {
if (checkable) {
Ref<Texture> check = get_icon("checked", "CheckBox");
- ms.width += check->get_width() + get_constant("hseparator", "Tree");
+ ms.width += check->get_width() + get_constant("hseparation", "CheckBox") + get_constant("hseparator", "Tree");
}
if (bottom_editor != NULL && bottom_editor->is_visible()) {
@@ -228,8 +228,7 @@ void EditorProperty::_notification(int p_what) {
}
check_rect = Rect2(ofs, ((size.height - checkbox->get_height()) / 2), checkbox->get_width(), checkbox->get_height());
draw_texture(checkbox, check_rect.position, color2);
- ofs += get_constant("hseparator", "Tree");
- ofs += checkbox->get_width();
+ ofs += get_constant("hseparator", "Tree") + checkbox->get_width() + get_constant("hseparation", "CheckBox");
text_limit -= ofs;
} else {
check_rect = Rect2();
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 1dd68309ae..79c312b7b1 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -523,6 +523,7 @@ void EditorNode::_fs_changed() {
void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
List<String> scenes; //will load later
+ int current_tab = scene_tabs->get_current_tab();
for (int i = 0; i < p_resources.size(); i++) {
String file_type = ResourceLoader::get_resource_type(p_resources[i]);
@@ -545,6 +546,8 @@ void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
for (List<String>::Element *E = scenes.front(); E; E = E->next()) {
reload_scene(E->get());
}
+
+ scene_tabs->set_current_tab(current_tab);
}
void EditorNode::_sources_changed(bool p_exist) {
@@ -1214,6 +1217,17 @@ void EditorNode::save_all_scenes() {
_save_all_scenes();
}
+void EditorNode::save_scene_list(Vector<String> p_scene_filenames) {
+
+ for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
+ Node *scene = editor_data.get_edited_scene_root(i);
+
+ if (scene && (p_scene_filenames.find(scene->get_filename()) >= 0)) {
+ _save_scene(scene->get_filename(), i);
+ }
+ }
+}
+
void EditorNode::restart_editor() {
exiting = true;
@@ -1283,7 +1297,7 @@ void EditorNode::_dialog_action(String p_file) {
Node *scene = editor_data.get_edited_scene_root();
// If the previous scene is rootless, just close it in favor of the new one.
if (!scene)
- _menu_option_confirm(FILE_CLOSE, false);
+ _menu_option_confirm(FILE_CLOSE, true);
load_scene(p_file, false, true);
} break;
@@ -1296,7 +1310,12 @@ void EditorNode::_dialog_action(String p_file) {
ProjectSettings::get_singleton()->set("application/run/main_scene", p_file);
ProjectSettings::get_singleton()->save();
//would be nice to show the project manager opened with the highlighted field..
- _run(false, ""); // automatically run the project
+
+ if (pick_main_scene->has_meta("from_native") && (bool)pick_main_scene->get_meta("from_native")) {
+ run_native->resume_run_native();
+ } else {
+ _run(false, ""); // automatically run the project
+ }
} break;
case FILE_CLOSE:
case FILE_CLOSE_ALL_AND_QUIT:
@@ -1392,7 +1411,7 @@ void EditorNode::_dialog_action(String p_file) {
case RESOURCE_SAVE:
case RESOURCE_SAVE_AS: {
- ERR_FAIL_COND(saving_resource.is_null())
+ ERR_FAIL_COND(saving_resource.is_null());
save_resource_in_path(saving_resource, p_file);
saving_resource = Ref<Resource>();
ObjectID current = editor_history.get_current();
@@ -1809,28 +1828,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
if (run_filename == "") {
//evidently, run the scene
- main_scene = GLOBAL_DEF("application/run/main_scene", "");
- if (main_scene == "") {
-
- current_option = -1;
- pick_main_scene->set_text(TTR("No main scene has ever been defined, select one?\nYou can change it later in \"Project Settings\" under the 'application' category."));
- pick_main_scene->popup_centered_minsize();
- return;
- }
-
- if (!FileAccess::exists(main_scene)) {
-
- current_option = -1;
- pick_main_scene->set_text(vformat(TTR("Selected scene '%s' does not exist, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
- pick_main_scene->popup_centered_minsize();
- return;
- }
-
- if (ResourceLoader::get_resource_type(main_scene) != "PackedScene") {
-
- current_option = -1;
- pick_main_scene->set_text(vformat(TTR("Selected scene '%s' is not a scene file, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
- pick_main_scene->popup_centered_minsize();
+ if (!ensure_main_scene(false)) {
return;
}
}
@@ -2875,7 +2873,7 @@ bool EditorNode::is_addon_plugin_enabled(const String &p_addon) const {
return plugin_addons.has(p_addon);
}
-void EditorNode::_remove_edited_scene() {
+void EditorNode::_remove_edited_scene(bool p_change_tab) {
int new_index = editor_data.get_edited_scene();
int old_index = new_index;
@@ -2891,18 +2889,19 @@ void EditorNode::_remove_edited_scene() {
if (editor_data.get_scene_path(old_index) != String()) {
ScriptEditor::get_singleton()->close_builtin_scripts_from_scene(editor_data.get_scene_path(old_index));
}
- _scene_tab_changed(new_index);
+
+ if (p_change_tab) _scene_tab_changed(new_index);
editor_data.remove_scene(old_index);
editor_data.get_undo_redo().clear_history(false);
_update_title();
_update_scene_tabs();
}
-void EditorNode::_remove_scene(int index) {
+void EditorNode::_remove_scene(int index, bool p_change_tab) {
if (editor_data.get_edited_scene() == index) {
//Scene to remove is current scene
- _remove_edited_scene();
+ _remove_edited_scene(p_change_tab);
} else {
//Scene to remove is not active scene
editor_data.remove_scene(index);
@@ -4167,6 +4166,45 @@ bool EditorNode::has_scenes_in_session() {
return !scenes.empty();
}
+bool EditorNode::ensure_main_scene(bool p_from_native) {
+ pick_main_scene->set_meta("from_native", p_from_native); //whether from play button or native run
+ String main_scene = GLOBAL_DEF("application/run/main_scene", "");
+
+ if (main_scene == "") {
+
+ current_option = -1;
+ pick_main_scene->set_text(TTR("No main scene has ever been defined, select one?\nYou can change it later in \"Project Settings\" under the 'application' category."));
+ pick_main_scene->popup_centered_minsize();
+ return false;
+ }
+
+ if (!FileAccess::exists(main_scene)) {
+
+ current_option = -1;
+ pick_main_scene->set_text(vformat(TTR("Selected scene '%s' does not exist, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
+ pick_main_scene->popup_centered_minsize();
+ return false;
+ }
+
+ if (ResourceLoader::get_resource_type(main_scene) != "PackedScene") {
+
+ current_option = -1;
+ pick_main_scene->set_text(vformat(TTR("Selected scene '%s' is not a scene file, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
+ pick_main_scene->popup_centered_minsize();
+ return false;
+ }
+
+ return true;
+}
+
+int EditorNode::get_current_tab() {
+ return scene_tabs->get_current_tab();
+}
+
+void EditorNode::set_current_tab(int p_tab) {
+ scene_tabs->set_current_tab(p_tab);
+}
+
void EditorNode::_update_layouts_menu() {
editor_layouts->clear();
@@ -4794,8 +4832,7 @@ void EditorNode::reload_scene(const String &p_path) {
if (scene_idx == -1) {
if (get_edited_scene()) {
- //scene is not open, so at it might be instanced, just refresh, set tab to itself and it will reload
- set_current_scene(current_tab);
+ //scene is not open, so at it might be instanced. We'll refresh the whole scene later.
editor_data.get_undo_redo().clear_history();
}
return;
@@ -4805,17 +4842,19 @@ void EditorNode::reload_scene(const String &p_path) {
editor_data.apply_changes_in_editors();
_set_scene_metadata(p_path);
}
+
//remove scene
- _remove_scene(scene_idx);
- //reload scene
+ _remove_scene(scene_idx, false);
+ //reload scene
load_scene(p_path, true, false, true, true);
+
//adjust index so tab is back a the previous position
editor_data.move_edited_scene_to_index(scene_idx);
get_undo_redo()->clear_history();
+
//recover the tab
scene_tabs->set_current_tab(current_tab);
- _scene_tab_changed(current_tab);
}
int EditorNode::plugin_init_callback_count = 0;
@@ -5005,7 +5044,7 @@ void EditorNode::_feature_profile_changed() {
main_editor_buttons[EDITOR_3D]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D));
main_editor_buttons[EDITOR_SCRIPT]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT));
main_editor_buttons[EDITOR_ASSETLIB]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB));
- if (profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D) || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB) || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB)) {
+ if (profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D) || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT) || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB)) {
_editor_select(EDITOR_2D);
}
} else {
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 6e7b2b2443..f3bc95c409 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -525,8 +525,8 @@ private:
static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog);
void _cleanup_scene();
- void _remove_edited_scene();
- void _remove_scene(int index);
+ void _remove_edited_scene(bool p_change_tab = true);
+ void _remove_scene(int index, bool p_change_tab = true);
bool _find_and_save_resource(RES p_res, Map<RES, bool> &processed, int32_t flags);
bool _find_and_save_edited_subresources(Object *obj, Map<RES, bool> &processed, int32_t flags);
void _save_edited_subresources(Node *scene, Map<RES, bool> &processed, int32_t flags);
@@ -644,6 +644,12 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+protected:
+ friend class FileSystemDock;
+
+ int get_current_tab();
+ void set_current_tab(int p_tab);
+
public:
bool call_build();
@@ -815,6 +821,7 @@ public:
void remove_tool_menu_item(const String &p_name);
void save_all_scenes();
+ void save_scene_list(Vector<String> p_scene_filenames);
void restart_editor();
void dim_editor(bool p_dimming);
@@ -836,6 +843,8 @@ public:
static void add_init_callback(EditorNodeInitCallback p_callback) { _init_callbacks.push_back(p_callback); }
static void add_build_callback(EditorBuildCallback p_callback);
+
+ bool ensure_main_scene(bool p_from_native);
};
struct EditorProgress {
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 56fc1f87a5..055e1338dd 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -48,7 +48,7 @@ Array EditorInterface::_make_mesh_previews(const Array &p_meshes, int p_preview_
meshes.push_back(p_meshes[i]);
}
- Vector<Ref<Texture> > textures = make_mesh_previews(meshes, p_preview_size);
+ Vector<Ref<Texture> > textures = make_mesh_previews(meshes, NULL, p_preview_size);
Array ret;
for (int i = 0; i < textures.size(); i++) {
ret.push_back(textures[i]);
@@ -57,7 +57,7 @@ Array EditorInterface::_make_mesh_previews(const Array &p_meshes, int p_preview_
return ret;
}
-Vector<Ref<Texture> > EditorInterface::make_mesh_previews(const Vector<Ref<Mesh> > &p_meshes, int p_preview_size) {
+Vector<Ref<Texture> > EditorInterface::make_mesh_previews(const Vector<Ref<Mesh> > &p_meshes, Vector<Transform> *p_transforms, int p_preview_size) {
int size = p_preview_size;
@@ -74,25 +74,14 @@ Vector<Ref<Texture> > EditorInterface::make_mesh_previews(const Vector<Ref<Mesh>
RID camera = VS::get_singleton()->camera_create();
VS::get_singleton()->viewport_attach_camera(viewport, camera);
- VS::get_singleton()->camera_set_transform(camera, Transform(Basis(), Vector3(0, 0, 3)));
- //VS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
- VS::get_singleton()->camera_set_orthogonal(camera, 1.0, 0.01, 1000.0);
RID light = VS::get_singleton()->directional_light_create();
RID light_instance = VS::get_singleton()->instance_create2(light, scenario);
- VS::get_singleton()->instance_set_transform(light_instance, Transform().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0)));
RID light2 = VS::get_singleton()->directional_light_create();
VS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
- //VS::get_singleton()->light_set_color(light2, VS::LIGHT_COLOR_SPECULAR, Color(0.0, 0.0, 0.0));
RID light_instance2 = VS::get_singleton()->instance_create2(light2, scenario);
- VS::get_singleton()->instance_set_transform(light_instance2, Transform().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1)));
-
- //sphere = VS::get_singleton()->mesh_create();
- RID mesh_instance = VS::get_singleton()->instance_create();
- VS::get_singleton()->instance_set_scenario(mesh_instance, scenario);
-
EditorProgress ep("mlib", TTR("Creating Mesh Previews"), p_meshes.size());
Vector<Ref<Texture> > textures;
@@ -104,25 +93,38 @@ Vector<Ref<Texture> > EditorInterface::make_mesh_previews(const Vector<Ref<Mesh>
textures.push_back(Ref<Texture>());
continue;
}
+
+ Transform mesh_xform;
+ if (p_transforms != NULL) {
+ mesh_xform = (*p_transforms)[i];
+ }
+
+ RID inst = VS::get_singleton()->instance_create2(mesh->get_rid(), scenario);
+ VS::get_singleton()->instance_set_transform(inst, mesh_xform);
+
AABB aabb = mesh->get_aabb();
Vector3 ofs = aabb.position + aabb.size * 0.5;
aabb.position -= ofs;
Transform xform;
- xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI * 0.25);
- xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI * 0.25) * xform.basis;
+ xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI / 6);
+ xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI / 6) * xform.basis;
AABB rot_aabb = xform.xform(aabb);
float m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5;
if (m == 0) {
textures.push_back(Ref<Texture>());
continue;
}
- m = 1.0 / m;
- m *= 0.5;
- xform.basis.scale(Vector3(m, m, m));
xform.origin = -xform.basis.xform(ofs); //-ofs*m;
xform.origin.z -= rot_aabb.size.z * 2;
- RID inst = VS::get_singleton()->instance_create2(mesh->get_rid(), scenario);
- VS::get_singleton()->instance_set_transform(inst, xform);
+ xform.invert();
+ xform = mesh_xform * xform;
+
+ VS::get_singleton()->camera_set_transform(camera, xform * Transform(Basis(), Vector3(0, 0, 3)));
+ VS::get_singleton()->camera_set_orthogonal(camera, m * 2, 0.01, 1000.0);
+
+ VS::get_singleton()->instance_set_transform(light_instance, xform * Transform().looking_at(Vector3(-2, -1, -1), Vector3(0, 1, 0)));
+ VS::get_singleton()->instance_set_transform(light_instance2, xform * Transform().looking_at(Vector3(+1, -1, -2), Vector3(0, 1, 0)));
+
ep.step(TTR("Thumbnail..."), i);
Main::iteration();
Main::iteration();
@@ -136,7 +138,6 @@ Vector<Ref<Texture> > EditorInterface::make_mesh_previews(const Vector<Ref<Mesh>
textures.push_back(it);
}
- VS::get_singleton()->free(mesh_instance);
VS::get_singleton()->free(viewport);
VS::get_singleton()->free(light);
VS::get_singleton()->free(light_instance);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index ba4df35e66..c28e607c89 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -100,7 +100,7 @@ public:
Error save_scene();
void save_scene_as(const String &p_scene, bool p_with_preview = true);
- Vector<Ref<Texture> > make_mesh_previews(const Vector<Ref<Mesh> > &p_meshes, int p_preview_size);
+ Vector<Ref<Texture> > make_mesh_previews(const Vector<Ref<Mesh> > &p_meshes, Vector<Transform> *p_trnasforms, int p_preview_size);
EditorInterface();
};
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index b73cda6008..585ea0ec69 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -101,6 +101,12 @@ void EditorRunNative::_notification(int p_what) {
void EditorRunNative::_run_native(int p_idx, int p_platform) {
+ if (!EditorNode::get_singleton()->ensure_main_scene(true)) {
+ resume_idx = p_idx;
+ resume_platform = p_platform;
+ return;
+ }
+
Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(p_platform);
ERR_FAIL_COND(eep.is_null());
@@ -144,6 +150,10 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) {
eep->run(preset, p_idx, flags);
}
+void EditorRunNative::resume_run_native() {
+ _run_native(resume_idx, resume_platform);
+}
+
void EditorRunNative::_bind_methods() {
ClassDB::bind_method("_run_native", &EditorRunNative::_run_native);
@@ -198,4 +208,6 @@ EditorRunNative::EditorRunNative() {
deploy_debug_remote = false;
debug_collisions = false;
debug_navigation = false;
+ resume_idx = 0;
+ resume_platform = 0;
}
diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h
index 10cca014cf..d62c982725 100644
--- a/editor/editor_run_native.h
+++ b/editor/editor_run_native.h
@@ -45,6 +45,9 @@ class EditorRunNative : public HBoxContainer {
bool debug_collisions;
bool debug_navigation;
+ int resume_idx;
+ int resume_platform;
+
void _run_native(int p_idx, int p_platform);
protected:
@@ -64,6 +67,8 @@ public:
void set_debug_navigation(bool p_debug);
bool get_debug_navigation() const;
+ void resume_run_native();
+
EditorRunNative();
};
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 2c0b3a350f..58e3cc6fc1 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -318,21 +318,21 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("interface/editor/display_scale", 0);
hints["interface/editor/display_scale"] = PropertyInfo(Variant::INT, "interface/editor/display_scale", PROPERTY_HINT_ENUM, "Auto,75%,100%,125%,150%,175%,200%,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/custom_display_scale", 1.0f);
- hints["interface/editor/custom_display_scale"] = PropertyInfo(Variant::REAL, "interface/editor/custom_display_scale", PROPERTY_HINT_RANGE, "0.75,3,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+ hints["interface/editor/custom_display_scale"] = PropertyInfo(Variant::REAL, "interface/editor/custom_display_scale", PROPERTY_HINT_RANGE, "0.5,3,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/main_font_size", 14);
- hints["interface/editor/main_font_size"] = PropertyInfo(Variant::INT, "interface/editor/main_font_size", PROPERTY_HINT_RANGE, "10,40,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
- _initial_set("interface/editor/code_font_size", 14);
- hints["interface/editor/code_font_size"] = PropertyInfo(Variant::INT, "interface/editor/code_font_size", PROPERTY_HINT_RANGE, "8,96,1", PROPERTY_USAGE_DEFAULT);
+ hints["interface/editor/main_font_size"] = PropertyInfo(Variant::INT, "interface/editor/main_font_size", PROPERTY_HINT_RANGE, "8,48,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/main_font_antialiased", true);
- _initial_set("interface/editor/code_font_antialiased", true);
_initial_set("interface/editor/main_font_hinting", 2);
hints["interface/editor/main_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/main_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT);
- _initial_set("interface/editor/code_font_hinting", 2);
- hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/main_font", "");
hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/main_font_bold", "");
hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_font_bold", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT);
+ _initial_set("interface/editor/code_font_size", 14);
+ hints["interface/editor/code_font_size"] = PropertyInfo(Variant::INT, "interface/editor/code_font_size", PROPERTY_HINT_RANGE, "8,48,1", PROPERTY_USAGE_DEFAULT);
+ _initial_set("interface/editor/code_font_antialiased", true);
+ _initial_set("interface/editor/code_font_hinting", 2);
+ hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/code_font", "");
hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/dim_editor_on_dialog_popup", true);
@@ -491,8 +491,12 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Help
_initial_set("text_editor/help/show_help_index", true);
- _initial_set("text_editor/help_source_font_size", 14);
- hints["text_editor/help/help_source_font_size"] = PropertyInfo(Variant::REAL, "text_editor/help/help_source_font_size", PROPERTY_HINT_RANGE, "10, 50, 1");
+ _initial_set("text_editor/help/help_font_size", 15);
+ hints["text_editor/help/help_font_size"] = PropertyInfo(Variant::INT, "text_editor/help/help_font_size", PROPERTY_HINT_RANGE, "8,48,1");
+ _initial_set("text_editor/help/help_source_font_size", 14);
+ hints["text_editor/help/help_source_font_size"] = PropertyInfo(Variant::INT, "text_editor/help/help_source_font_size", PROPERTY_HINT_RANGE, "8,48,1");
+ _initial_set("text_editor/help/help_title_font_size", 23);
+ hints["text_editor/help/help_title_font_size"] = PropertyInfo(Variant::INT, "text_editor/help/help_title_font_size", PROPERTY_HINT_RANGE, "8,48,1");
/* Editors */
@@ -569,7 +573,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/2d/bone_outline_size", 2);
_initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4));
_initial_set("editors/2d/warped_mouse_panning", true);
- _initial_set("editors/2d/simple_spacebar_panning", false);
+ _initial_set("editors/2d/simple_panning", false);
_initial_set("editors/2d/scroll_to_pan", false);
_initial_set("editors/2d/pan_speed", 20);
@@ -600,7 +604,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("run/auto_save/save_before_running", true);
// Output
- hints["run/output/font_size"] = PropertyInfo(Variant::INT, "run/output/font_size", PROPERTY_HINT_RANGE, "8,96,1", PROPERTY_USAGE_DEFAULT);
+ _initial_set("run/output/font_size", 13);
+ hints["run/output/font_size"] = PropertyInfo(Variant::INT, "run/output/font_size", PROPERTY_HINT_RANGE, "8,48,1");
_initial_set("run/output/always_clear_output_on_play", true);
_initial_set("run/output/always_open_output_on_play", true);
_initial_set("run/output/always_close_output_on_stop", false);
@@ -699,6 +704,10 @@ bool EditorSettings::_save_text_editor_theme(String p_file) {
return false;
}
+bool EditorSettings::_is_default_text_editor_theme(String p_theme_name) {
+ return p_theme_name == "default" || p_theme_name == "adaptive" || p_theme_name == "custom";
+}
+
static Dictionary _get_builtin_script_templates() {
Dictionary templates;
@@ -1123,7 +1132,7 @@ Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default, bool p_re
Variant _EDITOR_GET(const String &p_setting) {
- ERR_FAIL_COND_V(!EditorSettings::get_singleton()->has_setting(p_setting), Variant())
+ ERR_FAIL_COND_V(!EditorSettings::get_singleton()->has_setting(p_setting), Variant());
return EditorSettings::get_singleton()->get(p_setting);
}
@@ -1291,7 +1300,7 @@ void EditorSettings::list_text_editor_themes() {
d->list_dir_begin();
String file = d->get_next();
while (file != String()) {
- if (file.get_extension() == "tet" && file.get_basename().to_lower() != "default" && file.get_basename().to_lower() != "adaptive" && file.get_basename().to_lower() != "custom") {
+ if (file.get_extension() == "tet" && !_is_default_text_editor_theme(file.get_basename().to_lower())) {
custom_themes.push_back(file.get_basename());
}
file = d->get_next();
@@ -1308,14 +1317,16 @@ void EditorSettings::list_text_editor_themes() {
}
void EditorSettings::load_text_editor_theme() {
- if (get("text_editor/theme/color_theme") == "Default" || get("text_editor/theme/color_theme") == "Adaptive" || get("text_editor/theme/color_theme") == "Custom") {
- if (get("text_editor/theme/color_theme") == "Default") {
+ String p_file = get("text_editor/theme/color_theme");
+
+ if (_is_default_text_editor_theme(p_file.get_file().to_lower())) {
+ if (p_file == "Default") {
_load_default_text_editor_theme();
}
return; // sorry for "Settings changed" console spam
}
- String theme_path = get_text_editor_themes_dir().plus_file((String)get("text_editor/theme/color_theme") + ".tet");
+ String theme_path = get_text_editor_themes_dir().plus_file(p_file + ".tet");
Ref<ConfigFile> cf = memnew(ConfigFile);
Error err = cf->load(theme_path);
@@ -1367,7 +1378,7 @@ bool EditorSettings::save_text_editor_theme() {
String p_file = get("text_editor/theme/color_theme");
- if (p_file.get_file().to_lower() == "default" || p_file.get_file().to_lower() == "adaptive" || p_file.get_file().to_lower() == "custom") {
+ if (_is_default_text_editor_theme(p_file.get_file().to_lower())) {
return false;
}
String theme_path = get_text_editor_themes_dir().plus_file(p_file + ".tet");
@@ -1379,7 +1390,7 @@ bool EditorSettings::save_text_editor_theme_as(String p_file) {
p_file += ".tet";
}
- if (p_file.get_file().to_lower() == "default.tet" || p_file.get_file().to_lower() == "adaptive.tet" || p_file.get_file().to_lower() == "custom.tet") {
+ if (_is_default_text_editor_theme(p_file.get_file().to_lower().trim_suffix(".tet"))) {
return false;
}
if (_save_text_editor_theme(p_file)) {
@@ -1397,6 +1408,11 @@ bool EditorSettings::save_text_editor_theme_as(String p_file) {
return false;
}
+bool EditorSettings::is_default_text_editor_theme() {
+ String p_file = get("text_editor/theme/color_theme");
+ return _is_default_text_editor_theme(p_file.get_file().to_lower());
+}
+
Vector<String> EditorSettings::get_script_templates(const String &p_extension) {
Vector<String> templates;
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index 43a8cbf739..2ee8dd805b 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -123,6 +123,7 @@ private:
void _load_defaults(Ref<ConfigFile> p_extra_config = NULL);
void _load_default_text_editor_theme();
bool _save_text_editor_theme(String p_file);
+ bool _is_default_text_editor_theme(String p_file);
protected:
static void _bind_methods();
@@ -187,6 +188,7 @@ public:
bool import_text_editor_theme(String p_file);
bool save_text_editor_theme();
bool save_text_editor_theme_as(String p_file);
+ bool is_default_text_editor_theme();
Vector<String> get_script_templates(const String &p_extension);
String get_editor_layouts_config() const;
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 194a131095..e57217bb11 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -451,9 +451,11 @@ void FileSystemDock::_navigate_to_path(const String &p_path, bool p_select_in_fa
} else if (dirAccess->dir_exists(p_path)) {
path = target_path + "/";
} else {
+ memdelete(dirAccess);
ERR_EXPLAIN(vformat(TTR("Cannot navigate to '%s' as it has not been found in the file system!"), p_path));
ERR_FAIL();
}
+ memdelete(dirAccess);
}
_set_current_path_text(path);
@@ -1201,6 +1203,21 @@ void FileSystemDock::_update_favorites_list_after_move(const Map<String, String>
EditorSettings::get_singleton()->set_favorites(new_favorites);
}
+void FileSystemDock::_save_scenes_after_move(const Map<String, String> &p_renames) const {
+ Vector<String> remaps;
+ _find_remaps(EditorFileSystem::get_singleton()->get_filesystem(), p_renames, remaps);
+ Vector<String> new_filenames;
+
+ for (int i = 0; i < remaps.size(); ++i) {
+ String file = p_renames.has(remaps[i]) ? p_renames[remaps[i]] : remaps[i];
+ if (ResourceLoader::get_resource_type(file) == "PackedScene") {
+ new_filenames.push_back(file);
+ }
+ }
+
+ editor->save_scene_list(new_filenames);
+}
+
void FileSystemDock::_make_dir_confirm() {
String dir_name = make_dir_dialog_text->get_text().strip_edges();
@@ -1279,14 +1296,21 @@ void FileSystemDock::_rename_operation_confirm() {
Map<String, String> file_renames;
Map<String, String> folder_renames;
_try_move_item(to_rename, new_path, file_renames, folder_renames);
+
+ int current_tab = editor->get_current_tab();
+
_update_dependencies_after_move(file_renames);
_update_resource_paths_after_move(file_renames);
_update_project_settings_after_move(file_renames);
_update_favorites_list_after_move(file_renames, folder_renames);
- //Rescan everything
+ editor->set_current_tab(current_tab);
+
print_verbose("FileSystem: calling rescan.");
_rescan();
+
+ print_verbose("FileSystem: saving moved scenes.");
+ _save_scenes_after_move(file_renames);
}
void FileSystemDock::_duplicate_operation_confirm() {
@@ -1382,13 +1406,20 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overw
}
if (is_moved) {
+ int current_tab = editor->get_current_tab();
+
_update_dependencies_after_move(file_renames);
_update_resource_paths_after_move(file_renames);
_update_project_settings_after_move(file_renames);
_update_favorites_list_after_move(file_renames, folder_renames);
+ editor->set_current_tab(current_tab);
+
print_verbose("FileSystem: calling rescan.");
_rescan();
+
+ print_verbose("FileSystem: saving moved scenes.");
+ _save_scenes_after_move(file_renames);
}
}
@@ -2038,7 +2069,7 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori
void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options) {
// Add options for files and folders
- ERR_FAIL_COND(p_paths.empty())
+ ERR_FAIL_COND(p_paths.empty());
Vector<String> filenames;
Vector<String> foldernames;
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 978235b328..46eaf71a8a 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -202,6 +202,7 @@ private:
void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const;
void _update_dependencies_after_move(const Map<String, String> &p_renames) const;
void _update_resource_paths_after_move(const Map<String, String> &p_renames) const;
+ void _save_scenes_after_move(const Map<String, String> &p_renames) const;
void _update_favorites_list_after_move(const Map<String, String> &p_files_renames, const Map<String, String> &p_folders_renames) const;
void _update_project_settings_after_move(const Map<String, String> &p_folders_renames) const;
diff --git a/editor/icons/icon_multi_mesh_instance_2d.svg b/editor/icons/icon_multi_mesh_instance_2d.svg
new file mode 100644
index 0000000000..07202ac659
--- /dev/null
+++ b/editor/icons/icon_multi_mesh_instance_2d.svg
@@ -0,0 +1,4 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
+<rect x="-1" y="-1" width="582" height="402" fill="none"/>
+<path d="m3 1c-1.1046 0-2 0.89543-2 2 5.6e-4 0.71397 0.38169 1.3735 1 1.7305v6.541c-0.61771 0.35664-0.99874 1.0152-1 1.7285 0 1.1046 0.89543 2 2 2 0.71397-5.6e-4 1.3735-0.38169 1.7305-1h1.2695v-2h-1.2715c-0.17478-0.30301-0.42598-0.55488-0.72852-0.73047v-5.8555l3.5859 3.5859h1.4141v-1.4141l-3.5859-3.5859h5.8574c0.17532 0.30158 0.42647 0.55205 0.72852 0.72656v1.2734h2v-1.2695c0.61831-0.35698 0.99944-1.0165 1-1.7305 0-1.1046-0.89543-2-2-2-0.71397 5.6e-4 -1.3735 0.38169-1.7305 1h-6.541c-0.35664-0.61771-1.0152-0.99874-1.7285-1zm8 7v3h-3v2h3v3h2v-3h3v-2h-3v-3h-2z" fill="#a5b7f3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
+</svg>
diff --git a/editor/icons/icon_project_icon_loading.svg b/editor/icons/icon_project_icon_loading.svg
new file mode 100644
index 0000000000..3802b67654
--- /dev/null
+++ b/editor/icons/icon_project_icon_loading.svg
@@ -0,0 +1 @@
+<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><path d="m8 0c-4.432 0-8 3.568-8 8v48c0 4.432 3.568 8 8 8h48c4.432 0 8-3.568 8-8v-48c0-4.432-3.568-8-8-8z" fill="#e0e0e0" fill-opacity=".188235"/></svg> \ No newline at end of file
diff --git a/editor/icons/icon_visual_shader.svg b/editor/icons/icon_visual_shader.svg
index e2c4f128b2..ded54276f4 100644
--- a/editor/icons/icon_visual_shader.svg
+++ b/editor/icons/icon_visual_shader.svg
@@ -1,105 +1,10 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="16"
- height="16"
- version="1.1"
- viewBox="0 0 16 16"
- id="svg20"
- sodipodi:docname="icon_visual_shader.svg"
- inkscape:version="0.92.3 (2405546, 2018-03-11)">
- <metadata
- id="metadata26">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs24" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="640"
- inkscape:window-height="480"
- id="namedview22"
- showgrid="false"
- inkscape:zoom="14.75"
- inkscape:cx="8"
- inkscape:cy="8"
- inkscape:window-x="67"
- inkscape:window-y="27"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg20" />
- <g
- id="g18"
- transform="matrix(1,0,0,0.50605327,0,0.49394673)">
- <path
- d="M 2,1 C 1.44774,1.0001 1.00006,1.4477 1,2 v 12 c 5.52e-5,0.5523 0.44774,0.9999 1,1 h 12 c 0.55226,-10e-5 0.99994,-0.4477 1,-1 V 6 L 10,1 Z m 1,2 h 6 v 3 c 0,0.554 0.44599,1 1,1 h 3 v 6 H 3 Z"
- id="path2"
- inkscape:connector-curvature="0"
- style="fill:#e0e0e0" />
- <path
- d="m 10,11 h 2 v 1 h -2 z"
- id="path4"
- inkscape:connector-curvature="0"
- style="fill:#9f70ff" />
- <path
- d="M 4,6 H 6 V 7 H 4 Z"
- id="path6"
- inkscape:connector-curvature="0"
- style="fill:#ffeb70" />
- <path
- d="m 8,8 h 4 V 9 H 8 Z"
- id="path8"
- inkscape:connector-curvature="0"
- style="fill:#9dff70" />
- <path
- d="M 7,6 H 8 V 7 H 7 Z"
- id="path10"
- inkscape:connector-curvature="0"
- style="fill:#70deff" />
- <path
- d="m 4,11 h 5 v 1 H 4 Z"
- id="path12"
- inkscape:connector-curvature="0"
- style="fill:#ff70ac" />
- <path
- d="M 4,4 H 7 V 5 H 4 Z"
- id="path14"
- inkscape:connector-curvature="0"
- style="fill:#ff7070" />
- <path
- d="M 4,8 H 7 V 9 H 4 Z"
- id="path16"
- inkscape:connector-curvature="0"
- style="fill:#70ffb9" />
- </g>
- <path
- inkscape:connector-curvature="0"
- style="fill:#e0e0e0"
- d="m 2.8642321,9 v 6 h 2 a 3,3 0 0 0 3,-3 V 9 h -2 v 3 a 1,1 0 0 1 -1,1 V 9 Z"
- id="path1394" />
- <path
- inkscape:connector-curvature="0"
- style="fill:#e0e0e0"
- d="m 10.864232,9 a 2,2 0 0 0 -1.7323999,1 2,2 0 0 0 0,2 2,2 0 0 0 1.7323999,1 H 8.8642321 v 2 h 1.9999999 a 2,2 0 0 0 1.7324,-1 2,2 0 0 0 0,-2 2,2 0 0 0 -1.7324,-1 h 2 V 9 Z"
- id="path30" />
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<path d="m2.8642 9.9954v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1-1 1v-4z" fill="#e0e0e0"/>
+<path d="m10.864 9.9954a2 2 0 0 0-1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0-1.7324-1h2v-2z" fill="#e0e0e0"/>
+<path d="m2 1c-0.55226 1e-4 -0.99994 0.4477-1 1v7h2v-6h6v3c0 0.554 0.44599 1 1 1h3v2h2v-3l-5-5z" fill="#e0e0e0"/>
+<path d="m4 6h2v1h-2z" fill="#ffeb70"/>
+<path d="m8 8h4v1h-4z" fill="#9dff70"/>
+<path d="m7 6h1v1h-1z" fill="#70deff"/>
+<path d="m4 4h3v1h-3z" fill="#ff7070"/>
+<path d="m4 8h3v1h-3z" fill="#70ffb9"/>
</svg>
diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp
index daa423e1d9..c97021433b 100644
--- a/editor/import/editor_scene_importer_gltf.cpp
+++ b/editor/import/editor_scene_importer_gltf.cpp
@@ -2090,22 +2090,23 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye
animation->add_track(Animation::TYPE_VALUE);
animation->track_set_path(track_idx, node_path);
- if (track.weight_tracks[i].interpolation <= GLTFAnimation::INTERP_STEP) {
- animation->track_set_interpolation_type(track_idx, track.weight_tracks[i].interpolation == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_NEAREST);
+ // Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation,
+ // the other modes have to be baked.
+ GLTFAnimation::Interpolation gltf_interp = track.weight_tracks[i].interpolation;
+ if (gltf_interp == GLTFAnimation::INTERP_LINEAR || gltf_interp == GLTFAnimation::INTERP_STEP) {
+ animation->track_set_interpolation_type(track_idx, gltf_interp == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_LINEAR);
for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
float t = track.weight_tracks[i].times[j];
float w = track.weight_tracks[i].values[j];
animation->track_insert_key(track_idx, t, w);
}
} else {
- //must bake, apologies.
+ // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
float increment = 1.0 / float(bake_fps);
float time = 0.0;
-
bool last = false;
while (true) {
-
- _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, track.weight_tracks[i].interpolation);
+ _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
if (last) {
break;
}
diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp
index 35fdd32e2c..eca4e58abe 100644
--- a/editor/import/resource_importer_texture_atlas.cpp
+++ b/editor/import/resource_importer_texture_atlas.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* resource_importer_texture_atlas.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 "resource_importer_texture_atlas.h"
#include "atlas_import_failed.xpm"
diff --git a/editor/import/resource_importer_texture_atlas.h b/editor/import/resource_importer_texture_atlas.h
index 62be570dc6..042deacfe3 100644
--- a/editor/import/resource_importer_texture_atlas.h
+++ b/editor/import/resource_importer_texture_atlas.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* resource_importer_texture_atlas.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 RESOURCE_IMPORTER_TEXTURE_ATLAS_H
#define RESOURCE_IMPORTER_TEXTURE_ATLAS_H
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index fdf1103258..e39f2dabaa 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -119,7 +119,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
file->close();
memdelete(file);
- ERR_EXPLAIN("Not a WAV file (no WAVE RIFF Header)")
+ ERR_EXPLAIN("Not a WAV file (no WAVE RIFF Header)");
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
}
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 8a2393ff60..8a0812973f 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -169,7 +169,7 @@ void InspectorDock::_save_resource(bool save_as) const {
uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current();
Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
+ ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj));
RES current_res = RES(Object::cast_to<Resource>(current_obj));
@@ -183,7 +183,7 @@ void InspectorDock::_unref_resource() const {
uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current();
Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
+ ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj));
RES current_res = RES(Object::cast_to<Resource>(current_obj));
current_res->set_path("");
@@ -194,7 +194,7 @@ void InspectorDock::_copy_resource() const {
uint32_t current = EditorNode::get_singleton()->get_editor_history()->get_current();
Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
+ ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj));
RES current_res = RES(Object::cast_to<Resource>(current_obj));
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index bfee76492b..dff6eb5c5e 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -471,7 +471,7 @@ void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) {
void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) {
Ref<AnimationNode> an = blend_tree->get_node(p_which);
- ERR_FAIL_COND(!an.is_valid())
+ ERR_FAIL_COND(!an.is_valid());
AnimationTreeEditor::get_singleton()->enter_editor(p_which);
}
@@ -798,7 +798,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima
String new_name = p_text;
- ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1)
+ ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1);
if (new_name == prev_name) {
return; //nothing to do
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 951e971615..5204565c06 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -289,39 +289,24 @@ void AnimationPlayerEditor::_pause_pressed() {
//player->set_pause( pause->is_pressed() );
}
+void AnimationPlayerEditor::_animation_selected(int p_which) {
-String AnimationPlayerEditor::_get_current_animation() const {
-
+ if (updating)
+ return;
// when selecting an animation, the idea is that the only interesting behavior
// ui-wise is that it should play/blend the next one if currently playing
+ String current;
if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) {
- return animation->get_item_text(animation->get_selected());
+ current = animation->get_item_text(animation->get_selected());
}
- return "";
-}
-
-void AnimationPlayerEditor::_animation_selected(int p_which) {
-
- if (updating)
- return;
-
- _current_animation_updated();
-}
-
-void AnimationPlayerEditor::_current_animation_updated() {
-
- String current = _get_current_animation();
-
if (current != "") {
- Ref<Animation> anim = player->get_animation(current);
player->set_assigned_animation(current);
- {
- if (!anim->is_connected("changed", this, "_current_animation_updated"))
- anim->connect("changed", this, "_current_animation_updated");
+ Ref<Animation> anim = player->get_animation(current);
+ {
track_editor->set_animation(anim);
Node *root = player->get_node(player->get_root());
@@ -784,7 +769,7 @@ void AnimationPlayerEditor::_dialog_action(String p_file) {
if (current != "") {
Ref<Animation> anim = player->get_animation(current);
- ERR_FAIL_COND(!Object::cast_to<Resource>(*anim))
+ ERR_FAIL_COND(!Object::cast_to<Resource>(*anim));
RES current_res = RES(Object::cast_to<Resource>(*anim));
@@ -1078,19 +1063,17 @@ void AnimationPlayerEditor::_list_changed() {
_update_player();
}
+void AnimationPlayerEditor::_animation_key_editor_anim_len_changed(float p_len) {
+
+ frame->set_max(p_len);
+}
+
void AnimationPlayerEditor::_animation_key_editor_anim_step_changed(float p_len) {
if (p_len)
frame->set_step(p_len);
else
frame->set_step(0.00001);
-
- String current = _get_current_animation();
-
- if (current != "") {
- Ref<Animation> anim = player->get_animation(current);
- anim->_change_notify("step");
- }
}
void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag) {
@@ -1568,7 +1551,6 @@ void AnimationPlayerEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_autoplay_pressed"), &AnimationPlayerEditor::_autoplay_pressed);
ClassDB::bind_method(D_METHOD("_pause_pressed"), &AnimationPlayerEditor::_pause_pressed);
ClassDB::bind_method(D_METHOD("_animation_selected"), &AnimationPlayerEditor::_animation_selected);
- ClassDB::bind_method(D_METHOD("_current_animation_updated"), &AnimationPlayerEditor::_current_animation_updated);
ClassDB::bind_method(D_METHOD("_animation_name_edited"), &AnimationPlayerEditor::_animation_name_edited);
ClassDB::bind_method(D_METHOD("_animation_new"), &AnimationPlayerEditor::_animation_new);
ClassDB::bind_method(D_METHOD("_animation_rename"), &AnimationPlayerEditor::_animation_rename);
@@ -1588,6 +1570,7 @@ void AnimationPlayerEditor::_bind_methods() {
//ClassDB::bind_method(D_METHOD("_editor_load_all"),&AnimationPlayerEditor::_editor_load_all);
ClassDB::bind_method(D_METHOD("_list_changed"), &AnimationPlayerEditor::_list_changed);
ClassDB::bind_method(D_METHOD("_animation_key_editor_seek"), &AnimationPlayerEditor::_animation_key_editor_seek);
+ ClassDB::bind_method(D_METHOD("_animation_key_editor_anim_len_changed"), &AnimationPlayerEditor::_animation_key_editor_anim_len_changed);
ClassDB::bind_method(D_METHOD("_animation_key_editor_anim_step_changed"), &AnimationPlayerEditor::_animation_key_editor_anim_step_changed);
ClassDB::bind_method(D_METHOD("_hide_anim_editors"), &AnimationPlayerEditor::_hide_anim_editors);
ClassDB::bind_method(D_METHOD("_animation_duplicate"), &AnimationPlayerEditor::_animation_duplicate);
@@ -1801,6 +1784,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
add_child(track_editor);
track_editor->set_v_size_flags(SIZE_EXPAND_FILL);
track_editor->connect("timeline_changed", this, "_animation_key_editor_seek");
+ track_editor->connect("animation_len_changed", this, "_animation_key_editor_anim_len_changed");
track_editor->connect("animation_step_changed", this, "_animation_key_editor_anim_step_changed");
_update_player();
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index b1026b5b9f..dedce16bbc 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -171,9 +171,7 @@ class AnimationPlayerEditor : public VBoxContainer {
void _autoplay_pressed();
void _stop_pressed();
void _pause_pressed();
- String _get_current_animation() const;
void _animation_selected(int p_which);
- void _current_animation_updated();
void _animation_new();
void _animation_rename();
void _animation_name_edited();
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index f06b4b2828..e25e7ac7ed 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -1096,7 +1096,7 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
String new_name = p_text;
- ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1)
+ ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1);
if (new_name == prev_name) {
return; // Nothing to do.
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 0dfb53b34a..1503258ff5 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -35,7 +35,7 @@
#include "editor_node.h"
#include "editor_settings.h"
-void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, int p_rating, const String &p_cost) {
+void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost) {
title->set_text(p_title);
asset_id = p_asset_id;
@@ -44,13 +44,6 @@ void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, co
author->set_text(p_author);
author_id = p_author_id;
price->set_text(p_cost);
-
- for (int i = 0; i < 5; i++) {
- if (i < p_rating)
- stars[i]->set_texture(get_icon("Favorites", "EditorIcons"));
- else
- stars[i]->set_texture(get_icon("NonFavorite", "EditorIcons"));
- }
}
void EditorAssetLibraryItem::set_image(int p_type, int p_index, const Ref<Texture> &p_image) {
@@ -65,9 +58,10 @@ void EditorAssetLibraryItem::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- icon->set_normal_texture(get_icon("DefaultProjectIcon", "EditorIcons"));
+ icon->set_normal_texture(get_icon("ProjectIconLoading", "EditorIcons"));
category->add_color_override("font_color", Color(0.5, 0.5, 0.5));
author->add_color_override("font_color", Color(0.5, 0.5, 0.5));
+ price->add_color_override("font_color", Color(0.5, 0.5, 0.5));
}
}
@@ -100,17 +94,19 @@ EditorAssetLibraryItem::EditorAssetLibraryItem() {
Ref<StyleBoxEmpty> border;
border.instance();
- border->set_default_margin(MARGIN_LEFT, 5);
- border->set_default_margin(MARGIN_RIGHT, 5);
- border->set_default_margin(MARGIN_BOTTOM, 5);
- border->set_default_margin(MARGIN_TOP, 5);
+ border->set_default_margin(MARGIN_LEFT, 5 * EDSCALE);
+ border->set_default_margin(MARGIN_RIGHT, 5 * EDSCALE);
+ border->set_default_margin(MARGIN_BOTTOM, 5 * EDSCALE);
+ border->set_default_margin(MARGIN_TOP, 5 * EDSCALE);
add_style_override("panel", border);
HBoxContainer *hb = memnew(HBoxContainer);
+ // Add some spacing to visually separate the icon from the asset details
+ hb->add_constant_override("separation", 15 * EDSCALE);
add_child(hb);
icon = memnew(TextureButton);
- icon->set_custom_minimum_size(Size2(64, 64));
+ icon->set_custom_minimum_size(Size2(64, 64) * EDSCALE);
icon->set_default_cursor_shape(CURSOR_POINTING_HAND);
icon->connect("pressed", this, "_asset_clicked");
@@ -139,18 +135,11 @@ EditorAssetLibraryItem::EditorAssetLibraryItem() {
author->connect("pressed", this, "_author_clicked");
vb->add_child(author);
- HBoxContainer *rating_hb = memnew(HBoxContainer);
- vb->add_child(rating_hb);
-
- for (int i = 0; i < 5; i++) {
- stars[i] = memnew(TextureRect);
- rating_hb->add_child(stars[i]);
- }
price = memnew(Label);
price->set_text(TTR("Free"));
vb->add_child(price);
- set_custom_minimum_size(Size2(250, 100));
+ set_custom_minimum_size(Size2(250, 100) * EDSCALE);
set_h_size_flags(SIZE_EXPAND_FILL);
set_mouse_filter(MOUSE_FILTER_PASS);
@@ -194,7 +183,6 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const
break;
}
}
- //item->call("set_image",p_type,p_index,p_image);
} break;
case EditorAssetLibrary::IMAGE_QUEUE_SCREENSHOT: {
@@ -207,7 +195,6 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const
break;
}
}
- //item->call("set_image",p_type,p_index,p_image);
} break;
}
}
@@ -248,13 +235,13 @@ void EditorAssetLibraryItemDescription::_preview_click(int p_id) {
}
}
-void EditorAssetLibraryItemDescription::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, int p_rating, const String &p_cost, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash) {
+void EditorAssetLibraryItemDescription::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash) {
asset_id = p_asset_id;
title = p_title;
download_url = p_download_url;
sha256 = p_sha256_hash;
- item->configure(p_title, p_asset_id, p_category, p_category_id, p_author, p_author_id, p_rating, p_cost);
+ item->configure(p_title, p_asset_id, p_category, p_category_id, p_author, p_author_id, p_cost);
description->clear();
description->add_text(TTR("Version:") + " " + p_version_string + "\n");
description->add_text(TTR("Contents:") + " ");
@@ -554,7 +541,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() {
hb2->add_child(retry);
hb2->add_child(install);
- set_custom_minimum_size(Size2(310, 0));
+ set_custom_minimum_size(Size2(310, 0) * EDSCALE);
download = memnew(HTTPRequest);
add_child(download);
@@ -666,7 +653,6 @@ void EditorAssetLibrary::_install_asset() {
}
const char *EditorAssetLibrary::sort_key[SORT_MAX] = {
- "rating",
"downloads",
"name",
"cost",
@@ -674,10 +660,9 @@ const char *EditorAssetLibrary::sort_key[SORT_MAX] = {
};
const char *EditorAssetLibrary::sort_text[SORT_MAX] = {
- "Rating",
"Downloads",
"Name",
- "Cost",
+ "License", // "cost" stores the SPDX license name in the Godot Asset Library
"Updated"
};
@@ -733,6 +718,7 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt
image_data = cached_data;
file->close();
+ memdelete(file);
}
}
@@ -807,6 +793,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
if (file) {
file->store_line(new_etag);
file->close();
+ memdelete(file);
}
int len = p_data.size();
@@ -816,6 +803,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
file->store_32(len);
file->store_buffer(r.ptr(), len);
file->close();
+ memdelete(file);
}
break;
@@ -855,6 +843,7 @@ void EditorAssetLibrary::_update_image_queue() {
if (file) {
headers.push_back("If-None-Match: " + file->get_line());
file->close();
+ memdelete(file);
}
}
@@ -984,7 +973,7 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int
to = p_page_count;
hbc->add_spacer();
- hbc->add_constant_override("separation", 5);
+ hbc->add_constant_override("separation", 5 * EDSCALE);
Button *first = memnew(Button);
first->set_text(TTR("First"));
@@ -1190,8 +1179,8 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
asset_items = memnew(GridContainer);
asset_items->set_columns(2);
- asset_items->add_constant_override("hseparation", 10);
- asset_items->add_constant_override("vseparation", 10);
+ asset_items->add_constant_override("hseparation", 10 * EDSCALE);
+ asset_items->add_constant_override("vseparation", 10 * EDSCALE);
library_vb->add_child(asset_items);
@@ -1208,12 +1197,11 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
ERR_CONTINUE(!r.has("author_id"));
ERR_CONTINUE(!r.has("category_id"));
ERR_FAIL_COND(!category_map.has(r["category_id"]));
- ERR_CONTINUE(!r.has("rating"));
ERR_CONTINUE(!r.has("cost"));
EditorAssetLibraryItem *item = memnew(EditorAssetLibraryItem);
asset_items->add_child(item);
- item->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["rating"], r["cost"]);
+ item->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["cost"]);
item->connect("asset_selected", this, "_select_asset");
item->connect("author_selected", this, "_select_author");
item->connect("category_selected", this, "_select_category");
@@ -1234,7 +1222,6 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
ERR_FAIL_COND(!r.has("version_string"));
ERR_FAIL_COND(!r.has("category_id"));
ERR_FAIL_COND(!category_map.has(r["category_id"]));
- ERR_FAIL_COND(!r.has("rating"));
ERR_FAIL_COND(!r.has("cost"));
ERR_FAIL_COND(!r.has("description"));
ERR_FAIL_COND(!r.has("download_url"));
@@ -1250,7 +1237,7 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
description->popup_centered_minsize();
description->connect("confirmed", this, "_install_asset");
- description->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["rating"], r["cost"], r["version"], r["version_string"], r["description"], r["download_url"], r["browse_url"], r["download_hash"]);
+ description->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["cost"], r["version"], r["version_string"], r["description"], r["download_url"], r["browse_url"], r["download_hash"]);
/*item->connect("asset_selected",this,"_select_asset");
item->connect("author_selected",this,"_select_author");
item->connect("category_selected",this,"_category_selected");*/
@@ -1357,7 +1344,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
HBoxContainer *search_hb = memnew(HBoxContainer);
library_main->add_child(search_hb);
- library_main->add_constant_override("separation", 10);
+ library_main->add_constant_override("separation", 10 * EDSCALE);
search_hb->add_child(memnew(Label(TTR("Search:") + " ")));
filter = memnew(LineEdit);
@@ -1459,10 +1446,10 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
Ref<StyleBoxEmpty> border2;
border2.instance();
- border2->set_default_margin(MARGIN_LEFT, 15);
- border2->set_default_margin(MARGIN_RIGHT, 35);
- border2->set_default_margin(MARGIN_BOTTOM, 15);
- border2->set_default_margin(MARGIN_TOP, 15);
+ border2->set_default_margin(MARGIN_LEFT, 15 * EDSCALE);
+ border2->set_default_margin(MARGIN_RIGHT, 35 * EDSCALE);
+ border2->set_default_margin(MARGIN_BOTTOM, 15 * EDSCALE);
+ border2->set_default_margin(MARGIN_TOP, 15 * EDSCALE);
PanelContainer *library_vb_border = memnew(PanelContainer);
library_scroll->add_child(library_vb_border);
@@ -1474,15 +1461,14 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
library_vb->set_h_size_flags(SIZE_EXPAND_FILL);
library_vb_border->add_child(library_vb);
- //margin_panel->set_stop_mouse(false);
asset_top_page = memnew(HBoxContainer);
library_vb->add_child(asset_top_page);
asset_items = memnew(GridContainer);
asset_items->set_columns(2);
- asset_items->add_constant_override("hseparation", 10);
- asset_items->add_constant_override("vseparation", 10);
+ asset_items->add_constant_override("hseparation", 10 * EDSCALE);
+ asset_items->add_constant_override("vseparation", 10 * EDSCALE);
library_vb->add_child(asset_items);
@@ -1496,7 +1482,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
last_queue_id = 0;
- library_vb->add_constant_override("separation", 20);
+ library_vb->add_constant_override("separation", 20 * EDSCALE);
load_status = memnew(ProgressBar);
load_status->set_min(0);
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index dd5f3c2077..81288ae831 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -77,7 +77,7 @@ protected:
static void _bind_methods();
public:
- void configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, int p_rating, const String &p_cost);
+ void configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost);
EditorAssetLibraryItem();
};
@@ -120,7 +120,7 @@ protected:
static void _bind_methods();
public:
- void configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, int p_rating, const String &p_cost, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash);
+ void configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash);
void add_preview(int p_id, bool p_video, const String &p_url);
String get_title() { return title; }
@@ -216,7 +216,6 @@ class EditorAssetLibrary : public PanelContainer {
};
enum SortOrder {
- SORT_RATING,
SORT_DOWNLOADS,
SORT_NAME,
SORT_COST,
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp
index 172096b1a7..0ab3d26c85 100644
--- a/editor/plugins/audio_stream_editor_plugin.cpp
+++ b/editor/plugins/audio_stream_editor_plugin.cpp
@@ -53,7 +53,6 @@ void AudioStreamEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_PROCESS) {
_current = _player->get_playback_position();
_indicator->update();
- _preview->update();
}
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
@@ -121,15 +120,19 @@ void AudioStreamEditor::_play() {
void AudioStreamEditor::_stop() {
_player->stop();
- _on_finished();
+ _play_button->set_icon(get_icon("MainPlay", "EditorIcons"));
+ _current = 0;
+ _indicator->update();
+ set_process(false);
}
void AudioStreamEditor::_on_finished() {
_play_button->set_icon(get_icon("MainPlay", "EditorIcons"));
- _current = 0;
- _indicator->update();
- set_process(false);
+ if (_current == _player->get_stream()->get_length()) {
+ _current = 0;
+ _indicator->update();
+ }
}
void AudioStreamEditor::_draw_indicator() {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 92cc12d931..fbf01a9405 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1081,7 +1081,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
if (b->is_pressed() &&
(b->get_button_index() == BUTTON_MIDDLE ||
(b->get_button_index() == BUTTON_LEFT && tool == TOOL_PAN) ||
- (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) {
+ (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_panning") && pan_pressed))) {
// Pan the viewport
panning = true;
}
@@ -1097,7 +1097,9 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
- if (k->get_scancode() == KEY_SPACE && (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || drag_type != DRAG_NONE)) {
+ bool is_pan_key = pan_view_shortcut.is_valid() && pan_view_shortcut->is_shortcut(p_event);
+
+ if (is_pan_key && (EditorSettings::get_singleton()->get("editors/2d/simple_panning") || drag_type != DRAG_NONE)) {
if (!panning) {
if (k->is_pressed() && !k->is_echo()) {
//Pan the viewport
@@ -1110,6 +1112,9 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
}
}
}
+
+ if (is_pan_key)
+ pan_pressed = k->is_pressed();
}
Ref<InputEventMouseMotion> m = p_event;
@@ -2214,7 +2219,7 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) {
void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
bool accepted = false;
- if (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || !Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
+ if (EditorSettings::get_singleton()->get("editors/2d/simple_panning") || !pan_pressed) {
if ((accepted = _gui_input_rulers_and_guides(p_event))) {
//printf("Rulers and guides\n");
} else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) {
@@ -3280,6 +3285,7 @@ void CanvasItemEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels"));
+ bool has_container_parents = false;
int nb_control = 0;
int nb_having_pivot = 0;
@@ -3323,6 +3329,10 @@ void CanvasItemEditor::_notification(int p_what) {
viewport->update();
}
nb_control++;
+
+ if (Object::cast_to<Container>(control->get_parent())) {
+ has_container_parents = true;
+ }
}
if (canvas_item->_edit_use_pivot()) {
@@ -3336,25 +3346,22 @@ void CanvasItemEditor::_notification(int p_what) {
// Show / Hide the layout and anchors mode buttons
if (nb_control > 0 && nb_control == selection.size()) {
presets_menu->set_visible(true);
- presets_menu->set_tooltip(TTR("Presets for the anchors and margins values of a Control node."));
anchor_mode_button->set_visible(true);
- anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their margins."));
// Set the pressed state of the node
anchor_mode_button->set_pressed(anchors_mode);
// Disable if the selected node is child of a container
- presets_menu->set_disabled(false);
- anchor_mode_button->set_disabled(false);
- for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
- Control *control = Object::cast_to<Control>(E->get());
- if (!control || Object::cast_to<Container>(control->get_parent())) {
- presets_menu->set_disabled(true);
- presets_menu->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent."));
- anchor_mode_button->set_disabled(true);
- anchor_mode_button->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent."));
- break;
- }
+ if (has_container_parents) {
+ presets_menu->set_disabled(true);
+ presets_menu->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent."));
+ anchor_mode_button->set_disabled(true);
+ anchor_mode_button->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent."));
+ } else {
+ presets_menu->set_disabled(false);
+ presets_menu->set_tooltip(TTR("Presets for the anchors and margins values of a Control node."));
+ anchor_mode_button->set_disabled(false);
+ anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their margins."));
}
} else {
presets_menu->set_visible(false);
@@ -4778,6 +4785,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
dragged_guide_pos = Point2();
dragged_guide_index = -1;
panning = false;
+ pan_pressed = false;
bone_last_frame = 0;
@@ -5127,6 +5135,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY);
divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE);
+ pan_view_shortcut = ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), KEY_SPACE);
skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true);
singleton = this;
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index e098d261c0..ff221eb758 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -265,6 +265,7 @@ private:
bool key_rot;
bool key_scale;
bool panning;
+ bool pan_pressed;
MenuOption last_option;
@@ -383,6 +384,7 @@ private:
Ref<ShortCut> set_pivot_shortcut;
Ref<ShortCut> multiply_grid_step_shortcut;
Ref<ShortCut> divide_grid_step_shortcut;
+ Ref<ShortCut> pan_view_shortcut;
bool _is_node_locked(const Node *p_node);
bool _is_node_movable(const Node *p_node, bool p_popup_warning = false);
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index 55feb40c2c..3b1d728b8b 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -593,7 +593,8 @@ struct CanvasItemPlotCurve {
color2(p_color2) {}
void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) {
- ci.draw_line(pos0, pos1, in_definition ? color1 : color2);
+ // FIXME: Using a line width greater than 1 breaks curve rendering
+ ci.draw_line(pos0, pos1, in_definition ? color1 : color2, 1, true);
}
};
@@ -616,8 +617,8 @@ void CurveEditor::_draw() {
Vector2 min_edge = get_world_pos(Vector2(0, view_size.y));
Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0));
- const Color grid_color0 = Color(1.0, 1.0, 1.0, 0.15);
- const Color grid_color1 = Color(1.0, 1.0, 1.0, 0.07);
+ const Color grid_color0 = get_color("mono_color", "Editor") * Color(1, 1, 1, 0.15);
+ const Color grid_color1 = get_color("mono_color", "Editor") * Color(1, 1, 1, 0.07);
draw_line(Vector2(min_edge.x, curve.get_min_value()), Vector2(max_edge.x, curve.get_min_value()), grid_color0);
draw_line(Vector2(max_edge.x, curve.get_max_value()), Vector2(min_edge.x, curve.get_max_value()), grid_color0);
draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color0);
@@ -674,13 +675,13 @@ void CurveEditor::_draw() {
if (i != 0) {
Vector2 control_pos = get_tangent_view_pos(i, TANGENT_LEFT);
- draw_line(get_view_pos(pos), control_pos, tangent_color);
+ draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE), true);
draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color);
}
if (i != curve.get_point_count() - 1) {
Vector2 control_pos = get_tangent_view_pos(i, TANGENT_RIGHT);
- draw_line(get_view_pos(pos), control_pos, tangent_color);
+ draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE), true);
draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color);
}
}
@@ -689,8 +690,8 @@ void CurveEditor::_draw() {
draw_set_transform_matrix(_world_to_view);
- const Color line_color = get_color("highlight_color", "Editor");
- const Color edge_line_color = get_color("font_color", "Editor");
+ const Color line_color = get_color("font_color", "Editor");
+ const Color edge_line_color = get_color("highlight_color", "Editor");
CanvasItemPlotCurve plot_func(*this, line_color, edge_line_color);
plot_curve_accurate(curve, 4.f / view_size.x, plot_func);
@@ -736,10 +737,10 @@ void CurveEditor::stroke_rect(Rect2 rect, Color color) {
Vector2 c(rect.position.x, rect.position.y + rect.size.y);
Vector2 d(rect.position + rect.size);
- draw_line(a, b, color);
- draw_line(b, d, color);
- draw_line(d, c, color);
- draw_line(c, a, color);
+ draw_line(a, b, color, Math::round(EDSCALE));
+ draw_line(b, d, color, Math::round(EDSCALE));
+ draw_line(d, c, color, Math::round(EDSCALE));
+ draw_line(c, a, color, Math::round(EDSCALE));
}
void CurveEditor::_bind_methods() {
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 28e57ac48a..285823d95a 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -643,7 +643,7 @@ Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from, const S
float max = -1000;
float min = 1000;
int from = uint64_t(i) * frame_length / w;
- int to = uint64_t(i + 1) * frame_length / w;
+ int to = (uint64_t(i) + 1) * frame_length / w;
to = MIN(to, frame_length);
from = MIN(from, frame_length - 1);
if (to == from) {
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index 18586b2fe5..e582f6ded2 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -71,6 +71,8 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
if (!p_merge)
p_library->clear();
+ Map<int, MeshInstance *> mesh_instances;
+
for (int i = 0; i < p_scene->get_child_count(); i++) {
Node *child = p_scene->get_child(i);
@@ -91,6 +93,15 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
if (mesh.is_null())
continue;
+ mesh = mesh->duplicate();
+ for (int j = 0; j < mesh->get_surface_count(); ++j) {
+ Ref<Material> mat = mi->get_surface_material(j);
+
+ if (mat.is_valid()) {
+ mesh->surface_set_material(j, mat);
+ }
+ }
+
int id = p_library->find_item_by_name(mi->get_name());
if (id < 0) {
@@ -100,6 +111,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
}
p_library->set_item_mesh(id, mesh);
+ mesh_instances[id] = mi;
Vector<MeshLibrary::ShapeData> collisions;
@@ -159,14 +171,26 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
if (1) {
Vector<Ref<Mesh> > meshes;
+ Vector<Transform> transforms;
Vector<int> ids = p_library->get_item_list();
for (int i = 0; i < ids.size(); i++) {
- meshes.push_back(p_library->get_item_mesh(ids[i]));
+
+ if (mesh_instances.find(ids[i])) {
+
+ meshes.push_back(p_library->get_item_mesh(ids[i]));
+ transforms.push_back(mesh_instances[ids[i]]->get_transform());
+ }
}
- Vector<Ref<Texture> > textures = EditorInterface::get_singleton()->make_mesh_previews(meshes, EditorSettings::get_singleton()->get("editors/grid_map/preview_size"));
+ Vector<Ref<Texture> > textures = EditorInterface::get_singleton()->make_mesh_previews(meshes, &transforms, EditorSettings::get_singleton()->get("editors/grid_map/preview_size"));
+ int j = 0;
for (int i = 0; i < ids.size(); i++) {
- p_library->set_item_preview(ids[i], textures[i]);
+
+ if (mesh_instances.find(ids[i])) {
+
+ p_library->set_item_preview(ids[i], textures[j]);
+ j++;
+ }
}
}
}
diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp
index fc0a425bfc..cae705a697 100644
--- a/editor/plugins/multimesh_editor_plugin.cpp
+++ b/editor/plugins/multimesh_editor_plugin.cpp
@@ -197,7 +197,7 @@ void MultiMeshEditor::_populate() {
float areapos = Math::random(0.0f, area_accum);
Map<float, int>::Element *E = triangle_area_map.find_closest(areapos);
- ERR_FAIL_COND(!E)
+ ERR_FAIL_COND(!E);
int index = E->get();
ERR_FAIL_INDEX(index, facecount);
diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp
index 4c354b8002..6fabdc93c6 100644
--- a/editor/plugins/particles_2d_editor_plugin.cpp
+++ b/editor/plugins/particles_2d_editor_plugin.cpp
@@ -94,7 +94,7 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) {
cpu_particles->set_z_index(particles->get_z_index());
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- ur->create_action(TTR("Convert from Particles2D"));
+ ur->create_action(TTR("Convert to CPUParticles"));
ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", particles, cpu_particles, true, false);
ur->add_do_reference(particles);
ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, particles, false, false);
diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp
index 09180edf2a..3f4f66a26d 100644
--- a/editor/plugins/particles_editor_plugin.cpp
+++ b/editor/plugins/particles_editor_plugin.cpp
@@ -67,7 +67,7 @@ bool ParticlesEditorBase::_generate(PoolVector<Vector3> &points, PoolVector<Vect
float areapos = Math::random(0.0f, area_accum);
Map<float, int>::Element *E = triangle_area_map.find_closest(areapos);
- ERR_FAIL_COND_V(!E, false)
+ ERR_FAIL_COND_V(!E, false);
int index = E->get();
ERR_FAIL_INDEX_V(index, geometry.size(), false);
@@ -312,7 +312,13 @@ void ParticlesEditor::_menu_option(int p_option) {
cpu_particles->set_visible(node->is_visible());
cpu_particles->set_pause_mode(node->get_pause_mode());
- EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, cpu_particles, false);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Convert to CPUParticles"));
+ ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, cpu_particles, true, false);
+ ur->add_do_reference(node);
+ ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, node, false, false);
+ ur->add_undo_reference(this);
+ ur->commit_action();
} break;
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 839c9483d7..7456c5d016 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -388,6 +388,14 @@ void ScriptEditor::_save_history() {
void ScriptEditor::_go_to_tab(int p_idx) {
+ ScriptEditorBase *current = _get_current_editor();
+ if (current) {
+ if (current->is_unsaved()) {
+
+ current->apply_code();
+ }
+ }
+
Control *c = Object::cast_to<Control>(tab_container->get_child(p_idx));
if (!c)
return;
@@ -715,6 +723,8 @@ void ScriptEditor::_resave_scripts(const String &p_str) {
se->trim_trailing_whitespace();
}
+ se->insert_final_newline();
+
if (convert_indent_on_save) {
if (use_space_indentation) {
se->convert_indent_to_spaces();
@@ -1070,6 +1080,8 @@ void ScriptEditor::_menu_option(int p_option) {
if (trim_trailing_whitespace_on_save)
current->trim_trailing_whitespace();
+ current->insert_final_newline();
+
if (convert_indent_on_save) {
if (use_space_indentation) {
current->convert_indent_to_spaces();
@@ -1089,7 +1101,10 @@ void ScriptEditor::_menu_option(int p_option) {
} break;
case FILE_SAVE_AS: {
- current->trim_trailing_whitespace();
+ if (trim_trailing_whitespace_on_save)
+ current->trim_trailing_whitespace();
+
+ current->insert_final_newline();
if (convert_indent_on_save) {
if (use_space_indentation) {
@@ -1296,23 +1311,29 @@ void ScriptEditor::_theme_option(int p_option) {
EditorSettings::get_singleton()->load_text_editor_theme();
} break;
case THEME_SAVE: {
- if (!EditorSettings::get_singleton()->save_text_editor_theme()) {
+ if (EditorSettings::get_singleton()->is_default_text_editor_theme()) {
+ ScriptEditor::_show_save_theme_as_dialog();
+ } else if (!EditorSettings::get_singleton()->save_text_editor_theme()) {
editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
}
} break;
case THEME_SAVE_AS: {
- file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
- file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
- file_dialog_option = THEME_SAVE_AS;
- file_dialog->clear_filters();
- file_dialog->add_filter("*.tet");
- file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
- file_dialog->popup_centered_ratio();
- file_dialog->set_title(TTR("Save Theme As..."));
+ ScriptEditor::_show_save_theme_as_dialog();
} break;
}
}
+void ScriptEditor::_show_save_theme_as_dialog() {
+ file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ file_dialog_option = THEME_SAVE_AS;
+ file_dialog->clear_filters();
+ file_dialog->add_filter("*.tet");
+ file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
+ file_dialog->popup_centered_ratio();
+ file_dialog->set_title(TTR("Save Theme As..."));
+}
+
void ScriptEditor::_tab_changed(int p_which) {
ensure_select_current();
@@ -1733,7 +1754,19 @@ void ScriptEditor::_update_script_names() {
Ref<Texture> icon = se->get_icon();
String path = se->get_edited_resource()->get_path();
bool built_in = !path.is_resource_file();
- String name = built_in ? path.get_file() : se->get_name();
+ String name;
+
+ if (built_in) {
+
+ name = path.get_file();
+ String resource_name = se->get_edited_resource()->get_name();
+ if (resource_name != "") {
+ name = name.substr(0, name.find("::", 0) + 2) + resource_name;
+ }
+ } else {
+
+ name = se->get_name();
+ }
_ScriptEditorItemData sd;
sd.icon = icon;
@@ -1949,10 +1982,11 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
List<String> args;
+ bool has_file_flag = false;
+ String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path());
if (flags.size()) {
String project_path = ProjectSettings::get_singleton()->get_resource_path();
- String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path());
flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0));
flags = flags.replacen("{col}", itos(p_col));
@@ -1974,6 +2008,9 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
} else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) {
String arg = flags.substr(from, num_chars);
+ if (arg.find("{file}") != -1) {
+ has_file_flag = true;
+ }
// do path replacement here, else there will be issues with spaces and quotes
arg = arg.replacen("{project}", project_path);
@@ -1988,6 +2025,11 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
}
}
+ // Default to passing script path if no {file} flag is specified.
+ if (!has_file_flag) {
+ args.push_back(script_path);
+ }
+
Error err = OS::get_singleton()->execute(path, args, false);
if (err == OK)
return false;
@@ -2101,6 +2143,8 @@ void ScriptEditor::save_all_scripts() {
se->trim_trailing_whitespace();
}
+ se->insert_final_newline();
+
if (!se->is_unsaved())
continue;
@@ -3408,7 +3452,8 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/list_script_names_as", PROPERTY_HINT_ENUM, "Name,Parent Directory And Name,Full Path"));
EDITOR_DEF("text_editor/open_scripts/list_script_names_as", 0);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE));
- EDITOR_DEF("text_editor/external/exec_flags", "");
+ EDITOR_DEF("text_editor/external/exec_flags", "{file}");
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}."));
ED_SHORTCUT("script_editor/open_recent", TTR("Open Recent"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T);
ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files"));
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 683fa881f8..7cd347e2d0 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -99,6 +99,7 @@ public:
virtual void set_executing_line(int p_line) = 0;
virtual void clear_executing_line() = 0;
virtual void trim_trailing_whitespace() = 0;
+ virtual void insert_final_newline() = 0;
virtual void convert_indent_to_spaces() = 0;
virtual void convert_indent_to_tabs() = 0;
virtual void ensure_focus() = 0;
@@ -263,6 +264,7 @@ class ScriptEditor : public PanelContainer {
void _tab_changed(int p_which);
void _menu_option(int p_option);
void _theme_option(int p_option);
+ void _show_save_theme_as_dialog();
Tree *disk_changed_list;
ConfirmationDialog *disk_changed;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index f66ae0465f..c7a438948d 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -457,6 +457,11 @@ void ScriptTextEditor::trim_trailing_whitespace() {
code_editor->trim_trailing_whitespace();
}
+void ScriptTextEditor::insert_final_newline() {
+
+ code_editor->insert_final_newline();
+}
+
void ScriptTextEditor::convert_indent_to_spaces() {
code_editor->convert_indent_to_spaces();
@@ -542,7 +547,6 @@ void ScriptTextEditor::_validate_script() {
script->set_source_code(text);
script->update_exports();
_update_member_keywords();
- //script->reload(); //will update all the variables in property editors
}
functions.clear();
@@ -553,30 +557,36 @@ void ScriptTextEditor::_validate_script() {
}
_update_connected_methods();
- code_editor->set_warning_nb(missing_connections.size() + warnings.size());
+ int warning_nb = warnings.size();
warnings_panel->clear();
- // add missing connections
- Node *base = get_tree()->get_edited_scene_root();
- if (base && missing_connections.size() > 0) {
- warnings_panel->push_table(1);
- for (List<Connection>::Element *E = missing_connections.front(); E; E = E->next()) {
- Connection connection = E->get();
-
- String base_path = base->get_name();
- String source_path = base == connection.source ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.source)));
- String target_path = base == connection.target ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.target)));
+ // Add missing connections.
+ if (GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
+ Node *base = get_tree()->get_edited_scene_root();
+ if (base && missing_connections.size() > 0) {
+ warnings_panel->push_table(1);
+ for (List<Connection>::Element *E = missing_connections.front(); E; E = E->next()) {
+ Connection connection = E->get();
+
+ String base_path = base->get_name();
+ String source_path = base == connection.source ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.source)));
+ String target_path = base == connection.target ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.target)));
+
+ warnings_panel->push_cell();
+ warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor"));
+ warnings_panel->add_text(vformat(TTR("Missing connected method '%s' for signal '%s' from node '%s' to node '%s'."), connection.method, connection.signal, source_path, target_path));
+ warnings_panel->pop(); // Color.
+ warnings_panel->pop(); // Cell.
+ }
+ warnings_panel->pop(); // Table.
- warnings_panel->push_cell();
- warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor"));
- warnings_panel->add_text(vformat(TTR("Missing connected method '%s' for signal '%s' from node '%s' to node '%s'"), connection.method, connection.signal, source_path, target_path));
- warnings_panel->pop(); // Color
- warnings_panel->pop(); // Cell
+ warning_nb += missing_connections.size();
}
- warnings_panel->pop(); // Table
}
- // add script warnings
+ code_editor->set_warning_nb(warning_nb);
+
+ // Add script warnings.
warnings_panel->push_table(3);
for (List<ScriptLanguage::Warning>::Element *E = warnings.front(); E; E = E->next()) {
ScriptLanguage::Warning w = E->get();
@@ -586,13 +596,13 @@ void ScriptTextEditor::_validate_script() {
warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor"));
warnings_panel->add_text(TTR("Line") + " " + itos(w.line));
warnings_panel->add_text(" (" + w.string_code + "):");
- warnings_panel->pop(); // Color
- warnings_panel->pop(); // Meta goto
- warnings_panel->pop(); // Cell
+ warnings_panel->pop(); // Color.
+ warnings_panel->pop(); // Meta goto.
+ warnings_panel->pop(); // Cell.
warnings_panel->push_cell();
warnings_panel->add_text(w.message);
- warnings_panel->pop(); // Cell
+ warnings_panel->pop(); // Cell.
Dictionary ignore_meta;
ignore_meta["line"] = w.line;
@@ -600,11 +610,10 @@ void ScriptTextEditor::_validate_script() {
warnings_panel->push_cell();
warnings_panel->push_meta(ignore_meta);
warnings_panel->add_text(TTR("(ignore)"));
- warnings_panel->pop(); // Meta ignore
- warnings_panel->pop(); // Cell
- //warnings_panel->add_newline();
+ warnings_panel->pop(); // Meta ignore.
+ warnings_panel->pop(); // Cell.
}
- warnings_panel->pop(); // Table
+ warnings_panel->pop(); // Table.
line--;
bool highlight_safe = EDITOR_DEF("text_editor/highlighting/highlight_type_safe_lines", true);
@@ -735,7 +744,7 @@ void ScriptTextEditor::_code_complete_script(const String &p_code, List<String>
base = _find_node_for_script(base, base, script);
}
String hint;
- Error err = script->get_language()->complete_code(p_code, script->get_path().get_base_dir(), base, r_options, r_force, hint);
+ Error err = script->get_language()->complete_code(p_code, script->get_path(), base, r_options, r_force, hint);
if (err == OK && hint != "") {
code_editor->get_text_edit()->set_code_hint(hint);
}
@@ -766,7 +775,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
EditorNode::get_singleton()->load_resource(p_symbol);
}
- } else if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), p_symbol, script->get_path().get_base_dir(), base, result) == OK) {
+ } else if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), p_symbol, script->get_path(), base, result) == OK) {
_goto_line(p_row);
@@ -867,12 +876,36 @@ void ScriptTextEditor::_update_connected_methods() {
continue;
}
- int line = script->get_language()->find_function(connection.method, text_edit->get_text());
- if (line < 0) {
- missing_connections.push_back(connection);
+ // As deleted nodes are still accessible via the undo/redo system, check if they're still on the tree.
+ Node *source = Object::cast_to<Node>(connection.source);
+ if (source && !source->is_inside_tree()) {
continue;
}
- text_edit->set_line_info_icon(line - 1, get_parent_control()->get_icon("Slot", "EditorIcons"), connection.method);
+
+ if (!ClassDB::has_method(script->get_instance_base_type(), connection.method)) {
+
+ int line = script->get_language()->find_function(connection.method, text_edit->get_text());
+ if (line < 0) {
+ // There is a chance that the method is inherited from another script.
+ bool found_inherited_function = false;
+ Ref<Script> inherited_script = script->get_base_script();
+ while (!inherited_script.is_null()) {
+ line = inherited_script->get_language()->find_function(connection.method, inherited_script->get_source_code());
+ if (line != -1) {
+ found_inherited_function = true;
+ break;
+ }
+
+ inherited_script = inherited_script->get_base_script();
+ }
+
+ if (!found_inherited_function) {
+ missing_connections.push_back(connection);
+ }
+ } else {
+ text_edit->set_line_info_icon(line - 1, get_parent_control()->get_icon("Slot", "EditorIcons"), connection.method);
+ }
+ }
}
}
}
@@ -1447,7 +1480,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
base = _find_node_for_script(base, base, script);
}
ScriptLanguage::LookupResult result;
- if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), word_at_mouse, script->get_path().get_base_dir(), base, result) == OK) {
+ if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), word_at_mouse, script->get_path(), base, result) == OK) {
open_docs = true;
}
}
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index bdfdf18d45..7f5b6c065d 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -194,6 +194,7 @@ public:
virtual void set_edit_state(const Variant &p_state);
virtual void ensure_focus();
virtual void trim_trailing_whitespace();
+ virtual void insert_final_newline();
virtual void convert_indent_to_spaces();
virtual void convert_indent_to_tabs();
virtual void tag_saved_version();
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index a795405dfc..f9ca38b919 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -60,6 +60,26 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) {
_line_col_changed();
}
+void ShaderTextEditor::reload_text() {
+ ERR_FAIL_COND(shader.is_null());
+
+ TextEdit *te = get_text_edit();
+ int column = te->cursor_get_column();
+ int row = te->cursor_get_line();
+ int h = te->get_h_scroll();
+ int v = te->get_v_scroll();
+
+ te->set_text(shader->get_code());
+ te->cursor_set_line(row);
+ te->cursor_set_column(column);
+ te->set_h_scroll(h);
+ te->set_v_scroll(v);
+
+ te->tag_saved_version();
+
+ update_line_and_column();
+}
+
void ShaderTextEditor::_load_theme_settings() {
get_text_edit()->clear_colors();
@@ -330,9 +350,8 @@ void ShaderEditor::_menu_option(int p_option) {
void ShaderEditor::_notification(int p_what) {
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- //if (is_visible_in_tree())
- // shader_editor->get_text_edit()->grab_focus();
+ if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_IN) {
+ _check_for_external_edit();
}
}
@@ -363,12 +382,14 @@ void ShaderEditor::_editor_settings_changed() {
void ShaderEditor::_bind_methods() {
+ ClassDB::bind_method("_reload_shader_from_disk", &ShaderEditor::_reload_shader_from_disk);
ClassDB::bind_method("_editor_settings_changed", &ShaderEditor::_editor_settings_changed);
ClassDB::bind_method("_text_edit_gui_input", &ShaderEditor::_text_edit_gui_input);
ClassDB::bind_method("_menu_option", &ShaderEditor::_menu_option);
ClassDB::bind_method("_params_changed", &ShaderEditor::_params_changed);
ClassDB::bind_method("apply_shaders", &ShaderEditor::apply_shaders);
+ ClassDB::bind_method("save_external_data", &ShaderEditor::save_external_data);
}
void ShaderEditor::ensure_select_current() {
@@ -389,6 +410,37 @@ void ShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
shader_editor->goto_line_selection(p_line, p_begin, p_end);
}
+void ShaderEditor::_check_for_external_edit() {
+
+ if (shader.is_null() || !shader.is_valid()) {
+ return;
+ }
+
+ // internal shader.
+ if (shader->get_path() == "" || shader->get_path().find("local://") != -1 || shader->get_path().find("::") != -1) {
+ return;
+ }
+
+ bool use_autoreload = bool(EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", false));
+ if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) {
+ if (use_autoreload) {
+ _reload_shader_from_disk();
+ } else {
+ disk_changed->call_deferred("popup_centered");
+ }
+ }
+}
+
+void ShaderEditor::_reload_shader_from_disk() {
+
+ Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), true);
+ ERR_FAIL_COND(!rel_shader.is_valid());
+
+ shader->set_code(rel_shader->get_code());
+ shader->set_last_modified_time(rel_shader->get_last_modified_time());
+ shader_editor->reload_text();
+}
+
void ShaderEditor::edit(const Ref<Shader> &p_shader) {
if (p_shader.is_null() || !p_shader->is_text_shader())
@@ -405,16 +457,20 @@ void ShaderEditor::edit(const Ref<Shader> &p_shader) {
// see if already has it
}
-void ShaderEditor::save_external_data() {
+void ShaderEditor::save_external_data(const String &p_str) {
- if (shader.is_null())
+ if (shader.is_null()) {
+ disk_changed->hide();
return;
- apply_shaders();
+ }
+ apply_shaders();
if (shader->get_path() != "" && shader->get_path().find("local://") == -1 && shader->get_path().find("::") == -1) {
//external shader, save it
ResourceSaver::save(shader->get_path(), shader);
}
+
+ disk_changed->hide();
}
void ShaderEditor::apply_shaders() {
@@ -573,6 +629,23 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) {
goto_line_dialog = memnew(GotoLineDialog);
add_child(goto_line_dialog);
+ disk_changed = memnew(ConfirmationDialog);
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ disk_changed->add_child(vbc);
+
+ Label *dl = memnew(Label);
+ dl->set_text(TTR("This shader has been modified on on disk.\nWhat action should be taken?"));
+ vbc->add_child(dl);
+
+ disk_changed->connect("confirmed", this, "_reload_shader_from_disk");
+ disk_changed->get_ok()->set_text(TTR("Reload"));
+
+ disk_changed->add_button(TTR("Resave"), !OS::get_singleton()->get_swap_ok_cancel(), "resave");
+ disk_changed->connect("custom_action", this, "save_external_data");
+
+ add_child(disk_changed);
+
_editor_settings_changed();
}
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 28ac9faaa5..b56c1451ad 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -58,6 +58,8 @@ protected:
public:
virtual void _validate_script();
+ void reload_text();
+
Ref<Shader> get_edited_shader() const;
void set_edited_shader(const Ref<Shader> &p_shader);
ShaderTextEditor();
@@ -103,6 +105,7 @@ class ShaderEditor : public PanelContainer {
GotoLineDialog *goto_line_dialog;
ConfirmationDialog *erase_tab_confirm;
+ ConfirmationDialog *disk_changed;
ShaderTextEditor *shader_editor;
@@ -112,6 +115,9 @@ class ShaderEditor : public PanelContainer {
void _editor_settings_changed();
+ void _check_for_external_edit();
+ void _reload_shader_from_disk();
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -127,7 +133,7 @@ public:
void goto_line_selection(int p_line, int p_begin, int p_end);
virtual Size2 get_minimum_size() const { return Size2(0, 200); }
- void save_external_data();
+ void save_external_data(const String &p_str = "");
ShaderEditor(EditorNode *p_node);
};
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 60f1248ace..a1c0b732fa 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -4211,7 +4211,7 @@ void SpatialEditor::set_state(const Dictionary &p_state) {
Array vp = d["viewports"];
uint32_t vp_size = static_cast<uint32_t>(vp.size());
if (vp_size > VIEWPORTS_COUNT) {
- WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state.")
+ WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state.");
vp_size = VIEWPORTS_COUNT;
}
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index a0f3c253d1..d036d7e965 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -251,6 +251,11 @@ void TextEditor::trim_trailing_whitespace() {
code_editor->trim_trailing_whitespace();
}
+void TextEditor::insert_final_newline() {
+
+ code_editor->insert_final_newline();
+}
+
void TextEditor::convert_indent_to_spaces() {
code_editor->convert_indent_to_spaces();
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index 2da7474793..e06d816177 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -130,6 +130,7 @@ public:
virtual void set_executing_line(int p_line);
virtual void clear_executing_line();
virtual void trim_trailing_whitespace();
+ virtual void insert_final_newline();
virtual void convert_indent_to_spaces();
virtual void convert_indent_to_tabs();
virtual void ensure_focus();
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index 772e47ac3a..16f93b8fd3 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -299,9 +299,13 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p
Vector2 position;
int current = manual_palette->get_current();
if (current != -1) {
- position = manual_palette->get_item_metadata(current);
+ if (tool != TOOL_PASTING) {
+ position = manual_palette->get_item_metadata(current);
+ } else {
+ position = p_autotile_coord;
+ }
} else {
- // if there is no manual tile selected, that either means that
+ // If there is no manual tile selected, that either means that
// autotiling is enabled, or the given tile is not autotiling. Either
// way, the coordinate of the tile does not matter, so assigning it to
// the coordinate of the existing tile works fine.
@@ -309,7 +313,7 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p
}
if (p_value == prev_val && p_flip_h == prev_flip_h && p_flip_v == prev_flip_v && p_transpose == prev_transpose && prev_position == position)
- return; //check that it's actually different
+ return; // Check that it's actually different.
for (int y = p_pos.y - 1; y <= p_pos.y + 1; y++) {
for (int x = p_pos.x - 1; x <= p_pos.x + 1; x++) {
@@ -322,15 +326,19 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p
node->_set_celld(p_pos, _create_cell_dictionary(p_value, p_flip_h, p_flip_v, p_transpose, p_autotile_coord));
+ if (tool == TOOL_PASTING)
+ return;
+
if (manual_autotile || (p_value != -1 && node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE)) {
if (current != -1) {
node->set_cell_autotile_coord(p_pos.x, p_pos.y, position);
+ } else if (node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE && priority_atlastile) {
+ // BIND_CENTER is used to indicate that bitmask should not update for this tile cell.
+ node->get_tileset()->autotile_set_bitmask(p_value, Vector2(p_pos.x, p_pos.y), TileSet::BIND_CENTER);
+ node->update_cell_bitmask(p_pos.x, p_pos.y);
}
} else {
- // manually placing tiles should not update bitmasks
- if (tool != TOOL_PASTING) {
- node->update_bitmask_area(Point2(p_pos));
- }
+ node->update_bitmask_area(Point2(p_pos));
}
}
@@ -339,6 +347,11 @@ void TileMapEditor::_manual_toggled(bool p_enabled) {
_update_palette();
}
+void TileMapEditor::_priority_toggled(bool p_enabled) {
+ priority_atlastile = p_enabled;
+ _update_palette();
+}
+
void TileMapEditor::_text_entered(const String &p_text) {
canvas_item_editor_viewport->grab_focus();
@@ -385,6 +398,8 @@ void TileMapEditor::_update_palette() {
// Update the palette
Vector<int> selected = get_selected_tiles();
+ int selected_single = palette->get_current();
+ int selected_manual = manual_palette->get_current();
palette->clear();
manual_palette->clear();
manual_palette->hide();
@@ -492,12 +507,13 @@ void TileMapEditor::_update_palette() {
if (selected.get(0) != TileMap::INVALID_CELL) {
set_selected_tiles(selected);
sel_tile = selected.get(Math::rand() % selected.size());
- } else {
+ } else if (palette->get_item_count() > 0) {
palette->select(0);
}
if (sel_tile != TileMap::INVALID_CELL) {
- if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE) {
+ if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) ||
+ (!priority_atlastile && tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE)) {
const Map<Vector2, uint32_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile);
@@ -533,16 +549,19 @@ void TileMapEditor::_update_palette() {
if (manual_palette->get_item_count() > 0) {
// Only show the manual palette if at least tile exists in it
- int selected2 = manual_palette->get_current();
- if (selected2 == -1) selected2 = 0;
- manual_palette->set_current(selected2);
+ if (selected_manual == -1 || selected_single != palette->get_current())
+ selected_manual = 0;
+ if (selected_manual < manual_palette->get_item_count())
+ manual_palette->set_current(selected_manual);
manual_palette->show();
}
if (sel_tile != TileMap::INVALID_CELL && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) {
manual_button->show();
+ priority_button->hide();
} else {
manual_button->hide();
+ priority_button->show();
}
}
@@ -553,23 +572,25 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) {
if (id == TileMap::INVALID_CELL)
return;
- if (search_box->get_text().strip_edges() != "") {
-
+ if (search_box->get_text() != "") {
search_box->set_text("");
_update_palette();
}
- Vector<int> selected;
-
- selected.push_back(id);
- set_selected_tiles(selected);
-
flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y);
flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y);
transpose = node->is_cell_transposed(p_pos.x, p_pos.y);
autotile_coord = node->get_cell_autotile_coord(p_pos.x, p_pos.y);
+ Vector<int> selected;
+ selected.push_back(id);
+ set_selected_tiles(selected);
_update_palette();
+
+ if ((manual_autotile && node->get_tileset()->tile_get_tile_mode(id) == TileSet::AUTO_TILE) || (!priority_atlastile && node->get_tileset()->tile_get_tile_mode(id) == TileSet::ATLAS_TILE)) {
+ manual_palette->select(manual_palette->find_metadata((Point2)autotile_coord));
+ }
+
CanvasItemEditor::get_singleton()->update_viewport();
}
@@ -686,7 +707,8 @@ void TileMapEditor::_fill_points(const PoolVector<Vector2> p_points, const Dicti
_set_cell(pr[i], ids, xf, yf, tr);
node->make_bitmask_area_dirty(pr[i]);
}
- node->update_dirty_bitmask();
+ if (!manual_autotile)
+ node->update_dirty_bitmask();
}
void TileMapEditor::_erase_points(const PoolVector<Vector2> p_points) {
@@ -744,15 +766,15 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p
Rect2 r = node->get_tileset()->tile_get_region(p_cell);
if (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::AUTO_TILE || node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE) {
Vector2 offset;
- int selected = manual_palette->get_current();
- if ((manual_autotile || node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE) && selected != -1) {
- offset = manual_palette->get_item_metadata(selected);
- } else {
- if (tool != TOOL_PASTING) {
- offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell);
+ if (tool != TOOL_PASTING) {
+ int selected = manual_palette->get_current();
+ if ((manual_autotile || (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE && !priority_atlastile)) && selected != -1) {
+ offset = manual_palette->get_item_metadata(selected);
} else {
- offset = p_autotile_coord;
+ offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell);
}
+ } else {
+ offset = p_autotile_coord;
}
int spacing = node->get_tileset()->autotile_get_spacing(p_cell);
@@ -760,7 +782,8 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p
r.position += (r.size + Vector2(spacing, spacing)) * offset;
}
Size2 sc = p_xform.get_scale();
- Size2 cell_size = node->get_cell_size();
+ /* For a future CheckBox to Center Texture:
+ Size2 cell_size = node->get_cell_size(); */
Rect2 rect = Rect2();
rect.position = node->map_to_world(p_point) + node->get_cell_draw_offset();
@@ -772,10 +795,11 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p
if (p_transpose) {
SWAP(tile_ofs.x, tile_ofs.y);
+ /* For a future CheckBox to Center Texture:
rect.position.x += cell_size.x / 2 - rect.size.y / 2;
rect.position.y += cell_size.y / 2 - rect.size.x / 2;
} else {
- rect.position += cell_size / 2 - rect.size / 2;
+ rect.position += cell_size / 2 - rect.size / 2; */
}
if (p_flip_h) {
@@ -795,10 +819,11 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p
Color modulate = node->get_tileset()->tile_get_modulate(p_cell);
modulate.a = 0.5;
- if (r.has_no_area())
+ if (r.has_no_area()) {
p_viewport->draw_texture_rect(t, rect, false, modulate, p_transpose);
- else
+ } else {
p_viewport->draw_texture_rect_region(t, rect, r, modulate, p_transpose);
+ }
}
void TileMapEditor::_draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i p_autotile_coord, const Transform2D &p_xform) {
@@ -833,7 +858,6 @@ void TileMapEditor::_update_copydata() {
TileData tcd;
tcd.cell = node->get_cell(j, i);
-
if (tcd.cell != TileMap::INVALID_CELL) {
tcd.pos = Point2i(j, i);
tcd.flip_h = node->is_cell_x_flipped(j, i);
@@ -960,7 +984,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
} else {
- // Mousebutton was released
+ // Mousebutton was released.
if (tool != TOOL_NONE) {
if (tool == TOOL_PAINTING) {
@@ -1024,7 +1048,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
CanvasItemEditor::get_singleton()->update_viewport();
- return true; // We want to keep the Pasting tool
+ return true; // We want to keep the Pasting tool.
} else if (tool == TOOL_SELECTING) {
CanvasItemEditor::get_singleton()->update_viewport();
@@ -1048,7 +1072,10 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
_finish_undo();
- // We want to keep the bucket-tool active
+ // So the fill preview is cleared right after the click.
+ CanvasItemEditor::get_singleton()->update_viewport();
+
+ // We want to keep the bucket-tool active.
return true;
}
@@ -1175,7 +1202,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (tool == TOOL_PAINTING) {
- // Paint using bresenham line to prevent holes in painting if the user moves fast
+ // Paint using bresenham line to prevent holes in painting if the user moves fast.
Vector<Point2i> points = line(old_over_tile.x, over_tile.x, old_over_tile.y, over_tile.y);
Vector<int> ids = get_selected_tiles();
@@ -1196,7 +1223,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (tool == TOOL_ERASING) {
- // erase using bresenham line to prevent holes in painting if the user moves fast
+ // Erase using bresenham line to prevent holes in painting if the user moves fast.
Vector<Point2i> points = line(old_over_tile.x, over_tile.x, old_over_tile.y, over_tile.y);
@@ -1321,13 +1348,13 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
if (!mouse_over) {
- // Editor shortcuts should not fire if mouse not in viewport
+ // Editor shortcuts should not fire if mouse not in viewport.
return false;
}
if (ED_IS_SHORTCUT("tile_map_editor/paint_tile", p_event)) {
// NOTE: We do not set tool = TOOL_PAINTING as this begins painting
- // immediately without pressing the left mouse button first
+ // immediately without pressing the left mouse button first.
tool = TOOL_NONE;
CanvasItemEditor::get_singleton()->update_viewport();
@@ -1409,7 +1436,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
- } else if (k.is_valid()) { // release event
+ } else if (k.is_valid()) { // Release event.
if (tool == TOOL_NONE) {
@@ -1425,7 +1452,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
#else
if (k->get_scancode() == KEY_CONTROL) {
#endif
- // go back to that last tool if KEY_CONTROL was released
+ // Go back to that last tool if KEY_CONTROL was released.
tool = last_tool;
CanvasItemEditor::get_singleton()->update_viewport();
@@ -1713,6 +1740,7 @@ void TileMapEditor::_icon_size_changed(float p_value) {
void TileMapEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_manual_toggled"), &TileMapEditor::_manual_toggled);
+ ClassDB::bind_method(D_METHOD("_priority_toggled"), &TileMapEditor::_priority_toggled);
ClassDB::bind_method(D_METHOD("_text_entered"), &TileMapEditor::_text_entered);
ClassDB::bind_method(D_METHOD("_text_changed"), &TileMapEditor::_text_changed);
ClassDB::bind_method(D_METHOD("_sbox_input"), &TileMapEditor::_sbox_input);
@@ -1816,6 +1844,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
node = NULL;
manual_autotile = false;
+ priority_atlastile = false;
manual_position = Vector2(0, 0);
canvas_item_editor_viewport = NULL;
editor = p_editor;
@@ -1846,10 +1875,15 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
add_child(tool_hb);
manual_button = memnew(CheckBox);
- manual_button->set_text("Disable Autotile");
+ manual_button->set_text(TTR("Disable Autotile"));
manual_button->connect("toggled", this, "_manual_toggled");
add_child(manual_button);
+ priority_button = memnew(CheckBox);
+ priority_button->set_text(TTR("Enable Priority"));
+ priority_button->connect("toggled", this, "_priority_toggled");
+ add_child(priority_button);
+
search_box = memnew(LineEdit);
search_box->set_h_size_flags(SIZE_EXPAND_FILL);
search_box->connect("text_entered", this, "_text_entered");
@@ -1958,31 +1992,31 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
p->connect("id_pressed", this, "_menu_option");
rotate_left_button = memnew(ToolButton);
- rotate_left_button->set_tooltip(TTR("Rotate left"));
+ rotate_left_button->set_tooltip(TTR("Rotate Left"));
rotate_left_button->set_focus_mode(FOCUS_NONE);
rotate_left_button->connect("pressed", this, "_rotate", varray(-1));
tool_hb->add_child(rotate_left_button);
rotate_right_button = memnew(ToolButton);
- rotate_right_button->set_tooltip(TTR("Rotate right"));
+ rotate_right_button->set_tooltip(TTR("Rotate Right"));
rotate_right_button->set_focus_mode(FOCUS_NONE);
rotate_right_button->connect("pressed", this, "_rotate", varray(1));
tool_hb->add_child(rotate_right_button);
flip_horizontal_button = memnew(ToolButton);
- flip_horizontal_button->set_tooltip(TTR("Flip horizontally"));
+ flip_horizontal_button->set_tooltip(TTR("Flip Horizontally"));
flip_horizontal_button->set_focus_mode(FOCUS_NONE);
flip_horizontal_button->connect("pressed", this, "_flip_horizontal");
tool_hb->add_child(flip_horizontal_button);
flip_vertical_button = memnew(ToolButton);
- flip_vertical_button->set_tooltip(TTR("Flip vertically"));
+ flip_vertical_button->set_tooltip(TTR("Flip Vertically"));
flip_vertical_button->set_focus_mode(FOCUS_NONE);
flip_vertical_button->connect("pressed", this, "_flip_vertical");
tool_hb->add_child(flip_vertical_button);
clear_transform_button = memnew(ToolButton);
- clear_transform_button->set_tooltip(TTR("Clear transform"));
+ clear_transform_button->set_tooltip(TTR("Clear Transform"));
clear_transform_button->set_focus_mode(FOCUS_NONE);
clear_transform_button->connect("pressed", this, "_clear_transform");
tool_hb->add_child(clear_transform_button);
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index fcdada1111..3f0abd1e6e 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -74,6 +74,7 @@ class TileMapEditor : public VBoxContainer {
TileMap *node;
bool manual_autotile;
+ bool priority_atlastile;
Vector2 manual_position;
EditorNode *editor;
@@ -103,6 +104,7 @@ class TileMapEditor : public VBoxContainer {
ToolButton *clear_transform_button;
CheckBox *manual_button;
+ CheckBox *priority_button;
Tool tool;
Tool last_tool;
@@ -183,6 +185,7 @@ class TileMapEditor : public VBoxContainer {
void set_selected_tiles(Vector<int> p_tile);
void _manual_toggled(bool p_enabled);
+ void _priority_toggled(bool p_enabled);
void _text_entered(const String &p_text);
void _text_changed(const String &p_text);
void _sbox_input(const Ref<InputEvent> &p_ie);
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index a00be3c0ce..4b225fddf5 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -3104,7 +3104,6 @@ void TileSetEditor::update_workspace_tile_mode() {
_select_edited_shape_coord();
tool_editmode[EDITMODE_BITMASK]->hide();
- tool_editmode[EDITMODE_PRIORITY]->hide();
}
_on_edit_mode_changed(edit_mode);
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index b0baf954d2..9a3991bef1 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -294,13 +294,13 @@ private:
String sp = _test_path();
if (sp != "") {
- // set the project name to the select folder name
- if (project_name->get_text() == "") {
+ // If the project name is empty or default, infer the project name from the selected folder name
+ if (project_name->get_text() == "" || project_name->get_text() == TTR("New Game Project")) {
sp = sp.replace("\\", "/");
int lidx = sp.find_last("/");
if (lidx != -1) {
- sp = sp.substr(lidx + 1, sp.length());
+ sp = sp.substr(lidx + 1, sp.length()).capitalize();
}
if (sp == "" && mode == MODE_IMPORT)
sp = TTR("Imported Project");
@@ -961,9 +961,25 @@ void ProjectManager::_notification(int p_what) {
set_process_unhandled_input(is_visible_in_tree());
} break;
+ case NOTIFICATION_WM_QUIT_REQUEST: {
+
+ _dim_window();
+ } break;
}
}
+void ProjectManager::_dim_window() {
+
+ // This method must be called before calling `get_tree()->quit()`.
+ // Otherwise, its effect won't be visible
+
+ // Dim the project manager window while it's quitting to make it clearer that it's busy.
+ // No transition is applied, as the effect needs to be visible immediately
+ float c = 1.0f - float(EDITOR_GET("interface/editor/dim_amount"));
+ Color dim_color = Color(c, c, c);
+ gui_base->set_modulate(dim_color);
+}
+
void ProjectManager::_panel_draw(Node *p_hb) {
HBoxContainer *hb = Object::cast_to<HBoxContainer>(p_hb);
@@ -1514,6 +1530,7 @@ void ProjectManager::_open_selected_projects() {
ERR_FAIL_COND(err);
}
+ _dim_window();
get_tree()->quit();
}
@@ -1780,11 +1797,13 @@ void ProjectManager::_restart_confirm() {
Error err = OS::get_singleton()->execute(exec, args, false, &pid);
ERR_FAIL_COND(err);
+ _dim_window();
get_tree()->quit();
}
void ProjectManager::_exit_dialog() {
+ _dim_window();
get_tree()->quit();
}
@@ -1971,7 +1990,7 @@ ProjectManager::ProjectManager() {
l = memnew(Label);
String hash = String(VERSION_HASH);
if (hash.length() != 0)
- hash = "." + hash.left(7);
+ hash = "." + hash.left(9);
l->set_text("v" VERSION_FULL_BUILD "" + hash);
l->set_align(Label::ALIGN_CENTER);
top_hb->add_child(l);
diff --git a/editor/project_manager.h b/editor/project_manager.h
index fa878e75a6..a7cc6549f5 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -110,6 +110,7 @@ class ProjectManager : public Control {
void _install_project(const String &p_zip_path, const String &p_title);
+ void _dim_window();
void _panel_draw(Node *p_hb);
void _panel_input(const Ref<InputEvent> &p_ev, Node *p_hb);
void _unhandled_input(const Ref<InputEvent> &p_ev);
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index ce2795f37b..e8f5139cd5 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1082,7 +1082,7 @@ void SceneTreeDock::_notification(int p_what) {
bool show_create_root = bool(EDITOR_GET("interface/editors/show_scene_tree_root_selection")) && get_tree()->get_edited_scene_root() == NULL;
- if (show_create_root != create_root_dialog->is_visible_in_tree()) {
+ if (show_create_root != create_root_dialog->is_visible_in_tree() && !remote_tree->is_visible()) {
if (show_create_root) {
create_root_dialog->show();
scene_tree->hide();
@@ -2367,6 +2367,7 @@ void SceneTreeDock::hide_tab_buttons() {
void SceneTreeDock::_remote_tree_selected() {
scene_tree->hide();
+ create_root_dialog->hide();
if (remote_tree)
remote_tree->show();
edit_remote->set_pressed(true);
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index 5ca3448693..d6c8e6b452 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -51,6 +51,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
if (connect_to_script_mode) {
return; //don't do anything in this mode
}
+
TreeItem *item = Object::cast_to<TreeItem>(p_item);
ERR_FAIL_COND(!item);
@@ -220,23 +221,27 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
}
if (marked.has(p_node)) {
- item->set_text(0, String(p_node->get_name()) + " " + TTR("(Connecting From)"));
-
+ String node_name = p_node->get_name();
+ if (connecting_signal) {
+ node_name += " " + TTR("(Connecting From)");
+ }
+ item->set_text(0, node_name);
item->set_custom_color(0, accent);
}
} else if (part_of_subscene) {
- //item->set_selectable(0,marked_selectable);
if (valid_types.size() == 0) {
item->set_custom_color(0, get_color("disabled_font_color", "Editor"));
}
-
} else if (marked.has(p_node)) {
- if (!connect_to_script_mode) {
- item->set_selectable(0, marked_selectable);
+ String node_name = p_node->get_name();
+ if (connecting_signal) {
+ node_name += " " + TTR("(Connecting From)");
}
- item->set_custom_color(0, get_color("error_color", "Editor"));
+ item->set_text(0, node_name);
+ item->set_selectable(0, marked_selectable);
+ item->set_custom_color(0, get_color("accent_color", "Editor"));
} else if (!marked_selectable && !marked_children_selectable) {
Node *node = p_node;
@@ -286,8 +291,8 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
p_node->connect("script_changed", this, "_node_script_changed", varray(p_node));
if (!p_node->get_script().is_null()) {
-
- item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT, false, TTR("Open Script"));
+ Ref<Script> script = p_node->get_script();
+ item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT, false, TTR("Open Script:") + " " + script->get_path());
}
if (p_node->is_class("CanvasItem")) {
@@ -394,15 +399,17 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
void SceneTreeEditor::_node_visibility_changed(Node *p_node) {
- if (p_node != get_scene_node() && !p_node->get_owner()) {
+ if (!p_node || (p_node != get_scene_node() && !p_node->get_owner())) {
return;
}
- TreeItem *item = p_node ? _find(tree->get_root(), p_node->get_path()) : NULL;
- if (!item) {
+ TreeItem *item = _find(tree->get_root(), p_node->get_path());
+
+ if (!item) {
return;
}
+
int idx = item->get_button_by_id(0, BUTTON_VISIBILITY);
ERR_FAIL_COND(idx == -1);
@@ -1033,6 +1040,11 @@ void SceneTreeEditor::set_connect_to_script_mode(bool p_enable) {
update_tree();
}
+void SceneTreeEditor::set_connecting_signal(bool p_enable) {
+ connecting_signal = p_enable;
+ update_tree();
+}
+
void SceneTreeEditor::_bind_methods() {
ClassDB::bind_method("_tree_changed", &SceneTreeEditor::_tree_changed);
@@ -1077,6 +1089,7 @@ void SceneTreeEditor::_bind_methods() {
SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_open_instance) {
connect_to_script_mode = false;
+ connecting_signal = false;
undo_redo = NULL;
tree_dirty = true;
selected = NULL;
diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h
index 1c14da0d3a..68642910e8 100644
--- a/editor/scene_tree_editor.h
+++ b/editor/scene_tree_editor.h
@@ -68,6 +68,7 @@ class SceneTreeEditor : public Control {
AcceptDialog *warning;
bool connect_to_script_mode;
+ bool connecting_signal;
int blocked;
@@ -155,6 +156,7 @@ public:
void update_tree() { _update_tree(); }
void set_connect_to_script_mode(bool p_enable);
+ void set_connecting_signal(bool p_enable);
Tree *get_scene_tree() { return tree; }
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 751ae4fcf7..ffa221edaf 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -100,17 +100,25 @@ void ScriptCreateDialog::set_inheritance_base_type(const String &p_base) {
base_type = p_base;
}
-bool ScriptCreateDialog::_validate(const String &p_string) {
+bool ScriptCreateDialog::_validate_parent(const String &p_string) {
if (p_string.length() == 0)
return false;
- if (ScriptServer::get_language(language_menu->get_selected())->can_inherit_from_file() && p_string.is_quoted()) {
+ if (can_inherit_from_file && p_string.is_quoted()) {
String p = p_string.substr(1, p_string.length() - 2);
if (_validate_path(p, true) == "")
return true;
}
+ return ClassDB::class_exists(p_string) || ScriptServer::is_global_class(p_string);
+}
+
+bool ScriptCreateDialog::_validate_class(const String &p_string) {
+
+ if (p_string.length() == 0)
+ return false;
+
for (int i = 0; i < p_string.length(); i++) {
if (i == 0) {
@@ -118,7 +126,7 @@ bool ScriptCreateDialog::_validate(const String &p_string) {
return false; // no start with number plz
}
- bool valid_char = (p_string[i] >= '0' && p_string[i] <= '9') || (p_string[i] >= 'a' && p_string[i] <= 'z') || (p_string[i] >= 'A' && p_string[i] <= 'Z') || p_string[i] == '_' || p_string[i] == '-';
+ bool valid_char = (p_string[i] >= '0' && p_string[i] <= '9') || (p_string[i] >= 'a' && p_string[i] <= 'z') || (p_string[i] >= 'A' && p_string[i] <= 'Z') || p_string[i] == '_';
if (!valid_char)
return false;
@@ -193,7 +201,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
void ScriptCreateDialog::_class_name_changed(const String &p_name) {
- if (_validate(class_name->get_text())) {
+ if (_validate_class(class_name->get_text())) {
is_class_name_valid = true;
} else {
is_class_name_valid = false;
@@ -203,7 +211,7 @@ void ScriptCreateDialog::_class_name_changed(const String &p_name) {
void ScriptCreateDialog::_parent_name_changed(const String &p_parent) {
- if (_validate(parent_name->get_text())) {
+ if (_validate_parent(parent_name->get_text())) {
is_parent_name_valid = true;
} else {
is_parent_name_valid = false;
@@ -298,27 +306,13 @@ void ScriptCreateDialog::_load_exist() {
void ScriptCreateDialog::_lang_changed(int l) {
- l = language_menu->get_selected();
ScriptLanguage *language = ScriptServer::get_language(l);
- if (language->has_named_classes()) {
- has_named_classes = true;
- } else {
- has_named_classes = false;
- }
-
- if (language->supports_builtin_mode()) {
- supports_built_in = true;
- } else {
- supports_built_in = false;
+ has_named_classes = language->has_named_classes();
+ can_inherit_from_file = language->can_inherit_from_file();
+ supports_built_in = language->supports_builtin_mode();
+ if (!supports_built_in)
is_built_in = false;
- }
-
- if (ScriptServer::get_language(l)->can_inherit_from_file()) {
- can_inherit_from_file = true;
- } else {
- can_inherit_from_file = false;
- }
String selected_ext = "." + language->get_extension();
String path = file_path->get_text();
@@ -430,7 +424,7 @@ void ScriptCreateDialog::_file_selected(const String &p_file) {
String p = ProjectSettings::get_singleton()->localize_path(p_file);
if (is_browsing_parent) {
parent_name->set_text("\"" + p + "\"");
- _class_name_changed("\"" + p + "\"");
+ _parent_name_changed(parent_name->get_text());
} else {
file_path->set_text(p);
_path_changed(p);
@@ -445,7 +439,8 @@ void ScriptCreateDialog::_file_selected(const String &p_file) {
void ScriptCreateDialog::_create() {
- parent_name->set_text(select_class->get_selected_type());
+ parent_name->set_text(select_class->get_selected_type().split(" ")[0]);
+ _parent_name_changed(parent_name->get_text());
}
void ScriptCreateDialog::_browse_class_in_tree() {
@@ -542,14 +537,7 @@ void ScriptCreateDialog::_update_dialog() {
class_name->set_editable(false);
class_name->set_placeholder(TTR("N/A"));
class_name->set_placeholder_alpha(1);
- }
-
- /* Can script inherit from a file */
-
- if (can_inherit_from_file) {
- parent_browse_button->set_disabled(false);
- } else {
- parent_browse_button->set_disabled(true);
+ class_name->set_text("");
}
/* Is script Built-in */
@@ -572,7 +560,8 @@ void ScriptCreateDialog::_update_dialog() {
if (is_built_in) {
get_ok()->set_text(TTR("Create"));
parent_name->set_editable(true);
- parent_browse_button->set_disabled(false);
+ parent_search_button->set_disabled(false);
+ parent_browse_button->set_disabled(!can_inherit_from_file);
internal->set_visible(_can_be_built_in());
internal_label->set_visible(_can_be_built_in());
_msg_path_valid(true, TTR("Built-in script (into scene file)."));
@@ -580,7 +569,8 @@ void ScriptCreateDialog::_update_dialog() {
// New Script Created
get_ok()->set_text(TTR("Create"));
parent_name->set_editable(true);
- parent_browse_button->set_disabled(false);
+ parent_search_button->set_disabled(false);
+ parent_browse_button->set_disabled(!can_inherit_from_file);
internal->set_visible(_can_be_built_in());
internal_label->set_visible(_can_be_built_in());
if (is_path_valid) {
@@ -590,6 +580,7 @@ void ScriptCreateDialog::_update_dialog() {
// Script Loaded
get_ok()->set_text(TTR("Load"));
parent_name->set_editable(false);
+ parent_search_button->set_disabled(true);
parent_browse_button->set_disabled(true);
internal->set_disabled(!_can_be_built_in());
if (is_path_valid) {
@@ -755,7 +746,6 @@ ScriptCreateDialog::ScriptCreateDialog() {
internal->set_h_size_flags(0);
internal->connect("pressed", this, "_built_in_pressed");
internal_label = memnew(Label(TTR("Built-in Script")));
- internal_label->set_text(TTR("Built-in Script"));
internal_label->set_align(Label::ALIGN_RIGHT);
gc->add_child(internal_label);
gc->add_child(internal);
diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h
index c6dba78f56..61f87f5732 100644
--- a/editor/script_create_dialog.h
+++ b/editor/script_create_dialog.h
@@ -87,7 +87,8 @@ class ScriptCreateDialog : public ConfirmationDialog {
void _path_entered(const String &p_path = String());
void _lang_changed(int l = 0);
void _built_in_pressed();
- bool _validate(const String &p_string);
+ bool _validate_parent(const String &p_string);
+ bool _validate_class(const String &p_string);
String _validate_path(const String &p_path, bool p_file_must_exist);
void _class_name_changed(const String &p_name);
void _parent_name_changed(const String &p_parent);
diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp
index 621ab039f4..3b086c6316 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -727,20 +727,8 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
String tt = vs;
switch (Performance::MonitorType((int)perf_items[i]->get_metadata(1))) {
case Performance::MONITOR_TYPE_MEMORY: {
- // for the time being, going above GBs is a bad sign.
- String unit = "B";
- if ((int)v > 1073741824) {
- unit = "GB";
- v /= 1073741824.0;
- } else if ((int)v > 1048576) {
- unit = "MB";
- v /= 1048576.0;
- } else if ((int)v > 1024) {
- unit = "KB";
- v /= 1024.0;
- }
- tt += " bytes";
- vs = String::num(v, 2) + " " + unit;
+ vs = String::humanize_size(v);
+ tt = vs;
} break;
case Performance::MONITOR_TYPE_TIME: {
tt += " seconds";
@@ -1025,7 +1013,9 @@ void ScriptEditorDebugger::_performance_draw() {
int pi = which[i];
Color c = get_color("accent_color", "Editor");
float h = (float)which[i] / (float)(perf_items.size());
- c.set_hsv(Math::fmod(h + 0.4, 0.9), c.get_s() * 0.9, c.get_v() * 1.4);
+ // Use a darker color on light backgrounds for better visibility
+ float value_multiplier = EditorSettings::get_singleton()->is_dark_theme() ? 1.4 : 0.55;
+ c.set_hsv(Math::fmod(h + 0.4, 0.9), c.get_s() * 0.9, c.get_v() * value_multiplier);
c.a = 0.6;
perf_draw->draw_string(graph_font, r.position + Point2(0, graph_font->get_ascent()), perf_items[pi]->get_text(0), c, r.size.x);
@@ -1045,9 +1035,8 @@ void ScriptEditorDebugger::_performance_draw() {
float h2 = E->get()[pi] / m;
h2 = (1.0 - h2) * r.size.y;
- c.a = 0.7;
if (E != perf_history.front())
- perf_draw->draw_line(r.position + Point2(from, h2), r.position + Point2(from + spacing, prev), c, 2.0);
+ perf_draw->draw_line(r.position + Point2(from, h2), r.position + Point2(from + spacing, prev), c, Math::round(EDSCALE), true);
prev = h2;
E = E->next();
from -= spacing;
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index 104bac190e..67cbcf5de4 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -58,6 +58,7 @@
#include "scene/resources/concave_polygon_shape.h"
#include "scene/resources/convex_polygon_shape.h"
#include "scene/resources/cylinder_shape.h"
+#include "scene/resources/height_map_shape.h"
#include "scene/resources/plane_shape.h"
#include "scene/resources/primitive_meshes.h"
#include "scene/resources/ray_shape.h"
@@ -3637,6 +3638,14 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
handles.push_back(Vector3(0, 0, rs->get_length()));
p_gizmo->add_handles(handles, handles_material);
}
+
+ if (Object::cast_to<HeightMapShape>(*s)) {
+
+ Ref<HeightMapShape> hms = s;
+
+ Ref<ArrayMesh> mesh = hms->get_debug_mesh();
+ p_gizmo->add_mesh(mesh, false, RID(), material);
+ }
}
/////
diff --git a/main/main.cpp b/main/main.cpp
index 63ce165d80..7e4fdeeaa7 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -161,7 +161,7 @@ static String unescape_cmdline(const String &p_str) {
static String get_full_version_string() {
String hash = String(VERSION_HASH);
if (hash.length() != 0)
- hash = "." + hash.left(7);
+ hash = "." + hash.left(9);
return String(VERSION_FULL_BUILD) + hash;
}
@@ -375,7 +375,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
while (I) {
- I->get() = unescape_cmdline(I->get().strip_escapes());
+ I->get() = unescape_cmdline(I->get().strip_edges());
I = I->next();
}
@@ -939,7 +939,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/per_pixel_transparency/allowed", false);
video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency/enabled", false);
- video_mode.layered_splash = GLOBAL_DEF("display/window/per_pixel_transparency/splash", false);
GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_allocation", 2);
GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_allocation.mobile", 3);
@@ -1021,6 +1020,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/common/physics_fps", 60));
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/common/physics_fps", PropertyInfo(Variant::INT, "physics/common/physics_fps", PROPERTY_HINT_RANGE, "1,120,1,or_greater"));
Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5));
Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0));
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps", PropertyInfo(Variant::INT, "debug/settings/fps/force_fps", PROPERTY_HINT_RANGE, "0,120,1,or_greater"));
@@ -1754,7 +1754,7 @@ bool Main::start() {
scene = scenedata->instance();
ERR_EXPLAIN("Failed loading scene: " + local_game_path);
- ERR_FAIL_COND_V(!scene, false)
+ ERR_FAIL_COND_V(!scene, false);
sml->add_current_scene(scene);
#ifdef OSX_ENABLED
diff --git a/misc/dist/html/full-size.html b/misc/dist/html/full-size.html
index 44b009524c..0e8a41a9fc 100644
--- a/misc/dist/html/full-size.html
+++ b/misc/dist/html/full-size.html
@@ -162,8 +162,13 @@ $GODOT_HEAD_INCLUDE
requestAnimationFrame(animate);
function adjustCanvasDimensions() {
- canvas.width = innerWidth;
- canvas.height = innerHeight;
+ var scale = window.devicePixelRatio || 1;
+ var width = window.innerWidth;
+ var height = window.innerHeight;
+ canvas.width = width * scale;
+ canvas.height = height * scale;
+ canvas.style.width = width + "px";
+ canvas.style.height = height + "px";
}
animationCallbacks.push(adjustCanvasDimensions);
adjustCanvasDimensions();
diff --git a/misc/scripts/fix_headers.py b/misc/scripts/fix_headers.py
index 823c9acfde..d94db22b42 100755
--- a/misc/scripts/fix_headers.py
+++ b/misc/scripts/fix_headers.py
@@ -33,7 +33,7 @@ header = """\
/*************************************************************************/
"""
-files = open("files", "rb")
+files = open("files", "r")
fname = files.readline()
@@ -67,7 +67,7 @@ while (fname != ""):
# In a second pass, we skip all consecutive comment lines starting with "/*",
# then we can append the rest (step 2).
- fileread = open(fname.strip(), "rb")
+ fileread = open(fname.strip(), "r")
line = fileread.readline()
header_done = False
@@ -92,11 +92,11 @@ while (fname != ""):
fileread.close()
# Write
- filewrite = open(fname.strip(), "wb")
+ filewrite = open(fname.strip(), "w")
filewrite.write(text)
filewrite.close()
# Next file
fname = files.readline()
-files.close() \ No newline at end of file
+files.close()
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub
index 16d694c238..2fe7a1b4c0 100644
--- a/modules/bullet/SCsub
+++ b/modules/bullet/SCsub
@@ -186,7 +186,11 @@ if env['builtin_bullet']:
thirdparty_sources = [thirdparty_dir + file for file in bullet2_src]
- env_bullet.Prepend(CPPPATH=[thirdparty_dir])
+ # Treat Bullet headers as system headers to avoid raising warnings. Not supported on MSVC.
+ if not env.msvc:
+ env_bullet.Append(CPPFLAGS=['-isystem', Dir(thirdparty_dir).path])
+ else:
+ env_bullet.Prepend(CPPPATH=[thirdparty_dir])
# if env['target'] == "debug" or env['target'] == "release_debug":
# env_bullet.Append(CPPFLAGS=['-DBT_DEBUG'])
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index be4e0b88e8..038001996d 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -267,7 +267,7 @@ RID BulletPhysicsServer::area_get_space(RID p_area) const {
void BulletPhysicsServer::area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) {
AreaBullet *area = area_owner.get(p_area);
- ERR_FAIL_COND(!area)
+ ERR_FAIL_COND(!area);
area->set_spOv_mode(p_mode);
}
diff --git a/modules/bullet/cone_twist_joint_bullet.cpp b/modules/bullet/cone_twist_joint_bullet.cpp
index d9a82d6179..bc7fd52cf6 100644
--- a/modules/bullet/cone_twist_joint_bullet.cpp
+++ b/modules/bullet/cone_twist_joint_bullet.cpp
@@ -84,7 +84,7 @@ void ConeTwistJointBullet::set_param(PhysicsServer::ConeTwistJointParam p_param,
break;
default:
ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
break;
}
}
diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp
index 8fed933854..0d2c46c579 100644
--- a/modules/bullet/generic_6dof_joint_bullet.cpp
+++ b/modules/bullet/generic_6dof_joint_bullet.cpp
@@ -175,7 +175,7 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO
break;
default:
ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
break;
}
}
@@ -256,7 +256,7 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF
break;
default:
ERR_EXPLAIN("This flag " + itos(p_flag) + " is deprecated");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
break;
}
}
diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp
index 7b99d3d89f..b7e1e1a4c2 100644
--- a/modules/bullet/hinge_joint_bullet.cpp
+++ b/modules/bullet/hinge_joint_bullet.cpp
@@ -118,7 +118,7 @@ void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t
break;
default:
ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated.");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
break;
}
}
diff --git a/modules/bullet/pin_joint_bullet.cpp b/modules/bullet/pin_joint_bullet.cpp
index 58b090006a..c9c4d1af7e 100644
--- a/modules/bullet/pin_joint_bullet.cpp
+++ b/modules/bullet/pin_joint_bullet.cpp
@@ -86,7 +86,7 @@ real_t PinJointBullet::get_param(PhysicsServer::PinJointParam p_param) const {
return p2pConstraint->m_setting.m_impulseClamp;
default:
ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
return 0;
}
}
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 3a61afa023..aa4d7d7d32 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -45,7 +45,7 @@ void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const Poo
int vc = p_vertices.size();
- ERR_FAIL_COND((vc % 3) != 0)
+ ERR_FAIL_COND((vc % 3) != 0);
PoolVector<Vector3>::Read rv = p_vertices.read();
int uvc = p_uvs.size();
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index a5b2238e6b..2171f27f96 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -116,9 +116,9 @@ protected:
virtual void _validate_property(PropertyInfo &property) const;
+public:
Array get_meshes() const;
-public:
void set_operation(Operation p_operation);
Operation get_operation() const;
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index 059c06c37c..50fdc8ab20 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -31,6 +31,8 @@
#include "texture_loader_dds.h"
#include "core/os/file_access.h"
+#define PF_FOURCC(s) (((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0]))
+
enum {
DDS_MAGIC = 0x20534444,
DDSD_CAPS = 0x00000001,
@@ -51,6 +53,7 @@ enum DDSFormat {
DDS_DXT5,
DDS_ATI1,
DDS_ATI2,
+ DDS_A2XY,
DDS_BGRA8,
DDS_BGR8,
DDS_RGBA8, //flipped in dds
@@ -74,9 +77,12 @@ struct DDSFormatInfo {
};
static const DDSFormatInfo dds_format_info[DDS_MAX] = {
- { "DXT1", true, false, 4, 8, Image::FORMAT_DXT1 },
- { "DXT3", true, false, 4, 16, Image::FORMAT_DXT3 },
- { "DXT5", true, false, 4, 16, Image::FORMAT_DXT5 },
+ { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 },
+ { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 },
+ { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 },
+ { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R },
+ { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
+ { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
{ "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 },
{ "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
@@ -158,22 +164,25 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
DDSFormat dds_format;
- if (format_flags & DDPF_FOURCC && format_fourcc == 0x31545844) { //'1TXD'
+ if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) {
dds_format = DDS_DXT1;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x33545844) { //'3TXD'
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) {
dds_format = DDS_DXT3;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x35545844) { //'5TXD'
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) {
dds_format = DDS_DXT5;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x31495441) { //'1ITA'
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) {
dds_format = DDS_ATI1;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x32495441) { //'2ITA'
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) {
dds_format = DDS_ATI2;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) {
+
+ dds_format = DDS_A2XY;
} else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) {
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index 18dfe08e85..dcb4b7fd75 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -80,6 +80,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER);
ENetAddress address;
+ memset(&address, 0, sizeof(address));
#ifdef GODOT_ENET
if (bind_ip.is_wildcard()) {
@@ -346,7 +347,7 @@ void NetworkedMultiplayerENet::poll() {
uint32_t *id = (uint32_t *)event.peer->data;
- ERR_CONTINUE(event.packet->dataLength < 8)
+ ERR_CONTINUE(event.packet->dataLength < 8);
uint32_t source = decode_uint32(&event.packet->data[0]);
int target = decode_uint32(&event.packet->data[4]);
@@ -462,7 +463,7 @@ void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) {
ERR_FAIL_COND(!active);
ERR_FAIL_COND(!is_server());
- ERR_FAIL_COND(!peer_map.has(p_peer))
+ ERR_FAIL_COND(!peer_map.has(p_peer));
if (now) {
enet_peer_disconnect_now(peer_map[p_peer], 0);
diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp
index c3f8688adb..da01f573ce 100644
--- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp
+++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp
@@ -115,6 +115,17 @@ void ARVRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) {
interface->set_anchor_detection_is_enabled(data, p_enable);
}
+int ARVRInterfaceGDNative::get_camera_feed_id() {
+
+ ERR_FAIL_COND_V(interface == NULL, 0);
+
+ if ((interface->version.major > 1) || ((interface->version.major) == 1 && (interface->version.minor >= 1))) {
+ return (unsigned int)interface->get_camera_feed_id(data);
+ } else {
+ return 0;
+ }
+}
+
bool ARVRInterfaceGDNative::is_stereo() {
bool stereo;
diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.h b/modules/gdnative/arvr/arvr_interface_gdnative.h
index 86396b067a..e0e5b67849 100644
--- a/modules/gdnative/arvr/arvr_interface_gdnative.h
+++ b/modules/gdnative/arvr/arvr_interface_gdnative.h
@@ -66,6 +66,7 @@ public:
/** specific to AR **/
virtual bool get_anchor_detection_is_enabled() const;
virtual void set_anchor_detection_is_enabled(bool p_enable);
+ virtual int get_camera_feed_id();
/** rendering and internal **/
virtual Size2 get_render_targetsize();
diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp
index 8fa29580d6..a2ac61b35e 100644
--- a/modules/gdnative/gdnative/vector2.cpp
+++ b/modules/gdnative/gdnative/vector2.cpp
@@ -119,6 +119,14 @@ godot_vector2 GDAPI godot_vector2_cubic_interpolate(const godot_vector2 *p_self,
return dest;
}
+godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 *p_to, const godot_real p_delta) {
+ godot_vector2 dest;
+ const Vector2 *self = (const Vector2 *)p_self;
+ const Vector2 *to = (const Vector2 *)p_to;
+ *((Vector2 *)&dest) = self->move_toward(*to, p_delta);
+ return dest;
+}
+
godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi) {
godot_vector2 dest;
const Vector2 *self = (const Vector2 *)p_self;
diff --git a/modules/gdnative/gdnative/vector3.cpp b/modules/gdnative/gdnative/vector3.cpp
index ef86c6f7e9..894683ab38 100644
--- a/modules/gdnative/gdnative/vector3.cpp
+++ b/modules/gdnative/gdnative/vector3.cpp
@@ -124,6 +124,14 @@ godot_vector3 GDAPI godot_vector3_cubic_interpolate(const godot_vector3 *p_self,
return dest;
}
+godot_vector3 GDAPI godot_vector3_move_toward(const godot_vector3 *p_self, const godot_vector3 *p_to, const godot_real p_delta) {
+ godot_vector3 dest;
+ const Vector3 *self = (const Vector3 *)p_self;
+ const Vector3 *to = (const Vector3 *)p_to;
+ *((Vector3 *)&dest) = self->move_toward(*to, p_delta);
+ return dest;
+}
+
godot_real GDAPI godot_vector3_dot(const godot_vector3 *p_self, const godot_vector3 *p_b) {
const Vector3 *self = (const Vector3 *)p_self;
const Vector3 *b = (const Vector3 *)p_b;
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 9f3c5752fd..6c12ee6534 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -26,6 +26,24 @@
["const godot_dictionary *", "p_self"],
["const godot_bool", "p_deep"]
]
+ },
+ {
+ "name": "godot_vector3_move_toward",
+ "return_type": "godot_vector3",
+ "arguments": [
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_to"],
+ ["const godot_real", "p_delta"]
+ ]
+ },
+ {
+ "name": "godot_vector2_move_toward",
+ "return_type": "godot_vector2",
+ "arguments": [
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_to"],
+ ["const godot_real", "p_delta"]
+ ]
}
]
},
diff --git a/modules/gdnative/include/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h
index 657090fa70..d465bbde54 100644
--- a/modules/gdnative/include/arvr/godot_arvr.h
+++ b/modules/gdnative/include/arvr/godot_arvr.h
@@ -64,6 +64,7 @@ typedef struct {
// only in 1.1 onwards
godot_int (*get_external_texture_for_eye)(void *, godot_int);
void (*notification)(void *, godot_int);
+ godot_int (*get_camera_feed_id)(void *);
} godot_arvr_interface_gdnative;
void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface);
diff --git a/modules/gdnative/include/gdnative/vector2.h b/modules/gdnative/include/gdnative/vector2.h
index 9e37b8e0c6..7a5ae6afa9 100644
--- a/modules/gdnative/include/gdnative/vector2.h
+++ b/modules/gdnative/include/gdnative/vector2.h
@@ -83,6 +83,8 @@ godot_vector2 GDAPI godot_vector2_linear_interpolate(const godot_vector2 *p_self
godot_vector2 GDAPI godot_vector2_cubic_interpolate(const godot_vector2 *p_self, const godot_vector2 *p_b, const godot_vector2 *p_pre_a, const godot_vector2 *p_post_b, const godot_real p_t);
+godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 *p_to, const godot_real p_delta);
+
godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi);
godot_vector2 GDAPI godot_vector2_tangent(const godot_vector2 *p_self);
diff --git a/modules/gdnative/include/gdnative/vector3.h b/modules/gdnative/include/gdnative/vector3.h
index 61f0c6c62e..70ec6422ac 100644
--- a/modules/gdnative/include/gdnative/vector3.h
+++ b/modules/gdnative/include/gdnative/vector3.h
@@ -90,6 +90,8 @@ godot_vector3 GDAPI godot_vector3_linear_interpolate(const godot_vector3 *p_self
godot_vector3 GDAPI godot_vector3_cubic_interpolate(const godot_vector3 *p_self, const godot_vector3 *p_b, const godot_vector3 *p_pre_a, const godot_vector3 *p_post_b, const godot_real p_t);
+godot_vector3 GDAPI godot_vector3_move_toward(const godot_vector3 *p_self, const godot_vector3 *p_to, const godot_real p_delta);
+
godot_real GDAPI godot_vector3_dot(const godot_vector3 *p_self, const godot_vector3 *p_b);
godot_vector3 GDAPI godot_vector3_cross(const godot_vector3 *p_self, const godot_vector3 *p_b);
diff --git a/modules/gdnative/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h
index 968f91ae9f..a9e83d1524 100644
--- a/modules/gdnative/include/pluginscript/godot_pluginscript.h
+++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h
@@ -136,7 +136,7 @@ typedef struct {
godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_pool_string_array *r_functions);
int (*find_function)(godot_pluginscript_language_data *p_data, const godot_string *p_function, const godot_string *p_code); // Can be NULL
godot_string (*make_function)(godot_pluginscript_language_data *p_data, const godot_string *p_class, const godot_string *p_name, const godot_pool_string_array *p_args);
- godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_base_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint);
+ godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint);
void (*auto_indent_code)(godot_pluginscript_language_data *p_data, godot_string *p_code, int p_from_line, int p_to_line);
void (*add_global_constant)(godot_pluginscript_language_data *p_data, const godot_string *p_variable, const godot_variant *p_value);
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index c9d92c09ed..7cb47ec623 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -159,13 +159,13 @@ String PluginScriptLanguage::make_function(const String &p_class, const String &
return String();
}
-Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) {
+Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) {
if (_desc.complete_code) {
Array options;
godot_error tmp = _desc.complete_code(
_data,
(godot_string *)&p_code,
- (godot_string *)&p_base_path,
+ (godot_string *)&p_path,
(godot_object *)p_owner,
(godot_array *)&options,
&r_force,
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
index 991be0bf12..a11e916975 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.h
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -81,7 +81,7 @@ public:
virtual bool can_inherit_from_file() { return true; }
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
- virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint);
+ virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint);
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const;
virtual void add_global_constant(const StringName &p_variable, const Variant &p_value);
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index 8dbbd2e4eb..1d6f9db349 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -39,12 +39,12 @@
#define ASSERT_SCRIPT_VALID() \
{ \
ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \
- ERR_FAIL_COND(!can_instance()) \
+ ERR_FAIL_COND(!can_instance()); \
}
-#define ASSERT_SCRIPT_VALID_V(ret) \
- { \
- ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \
- ERR_FAIL_COND_V(!can_instance(), ret) \
+#define ASSERT_SCRIPT_VALID_V(ret) \
+ { \
+ ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \
+ ERR_FAIL_COND_V(!can_instance(), ret); \
}
#else
#define ASSERT_SCRIPT_VALID()
@@ -77,7 +77,7 @@ PluginScriptInstance *PluginScript::_create_instance(const Variant **p_args, int
// There is currently no way to get the constructor function name of the script.
// instance->call("__init__", p_args, p_argcount, r_error);
if (p_argcount > 0) {
- WARN_PRINT("PluginScript doesn't support arguments in the constructor")
+ WARN_PRINT("PluginScript doesn't support arguments in the constructor");
}
return instance;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 716f536e89..40b773d99f 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -458,9 +458,9 @@ public:
virtual bool can_inherit_from_file() { return true; }
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
- virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint);
+ virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint);
#ifdef TOOLS_ENABLED
- virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result);
+ virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result);
#endif
virtual String _get_indentation() const;
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index f7be0ce37c..189317b163 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1977,12 +1977,12 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
-
- GDScript *subclass = p_script->subclasses[name].ptr();
+ Ref<GDScript> &subclass = p_script->subclasses[name];
+ GDScript *subclass_ptr = subclass.ptr();
// Subclass might still be parsing, just skip it
- if (!parsed_classes.has(subclass) && !parsing_classes.has(subclass)) {
- Error err = _parse_class_level(subclass, p_class->subclasses[i], p_keep_state);
+ if (!parsed_classes.has(subclass_ptr) && !parsing_classes.has(subclass_ptr)) {
+ Error err = _parse_class_level(subclass_ptr, p_class->subclasses[i], p_keep_state);
if (err)
return err;
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index fa80c31984..6c77968f44 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2461,13 +2461,13 @@ static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDS
r_forced = r_result.size() > 0;
}
-Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) {
+Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) {
const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
GDScriptParser parser;
- parser.parse(p_code, p_base_path, false, "", true);
+ parser.parse(p_code, p_path.get_base_dir(), false, p_path, true);
r_forced = false;
Set<String> options;
GDScriptCompletionContext context;
@@ -2478,7 +2478,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
if (!context._class || context._class->owner == NULL) {
context.base = p_owner;
- context.base_path = p_base_path;
+ context.base_path = p_path.get_base_dir();
}
bool is_function = false;
@@ -2884,7 +2884,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
#else
-Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) {
+Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) {
return OK;
}
@@ -3155,7 +3155,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return ERR_CANT_RESOLVE;
}
-Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result) {
+Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {
//before parsing, try the usual stuff
if (ClassDB::class_exists(p_symbol)) {
@@ -3197,7 +3197,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
GDScriptParser parser;
- parser.parse(p_code, p_base_path, false, "", true);
+ parser.parse(p_code, p_path.get_base_dir(), false, p_path, true);
if (parser.get_completion_type() == GDScriptParser::COMPLETION_NONE) {
return ERR_CANT_RESOLVE;
@@ -3209,7 +3209,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
context.block = parser.get_completion_block();
context.line = parser.get_completion_line();
context.base = p_owner;
- context.base_path = p_base_path;
+ context.base_path = p_path.get_base_dir();
if (context._class && context._class->extends_class.size() > 0) {
bool success = false;
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index 5ebcddfd7c..0736f3d010 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -78,6 +78,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"inverse_lerp",
"range_lerp",
"smoothstep",
+ "move_toward",
"dectime",
"randomize",
"randi",
@@ -341,7 +342,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_NUM(0);
r_ret = Math::step_decimals((double)*p_args[0]);
ERR_EXPLAIN("GDScript method 'decimals' is deprecated and has been renamed to 'step_decimals', please update your code accordingly.");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
} break;
case MATH_STEP_DECIMALS: {
VALIDATE_ARG_COUNT(1);
@@ -398,6 +399,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_NUM(2);
r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
} break;
+ case MATH_MOVE_TOWARD: {
+ VALIDATE_ARG_COUNT(3);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ r_ret = Math::move_toward((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
+ } break;
case MATH_DECTIME: {
VALIDATE_ARG_COUNT(3);
VALIDATE_ARG_NUM(0);
@@ -1466,6 +1474,7 @@ bool GDScriptFunctions::is_deterministic(Function p_func) {
case MATH_INVERSE_LERP:
case MATH_RANGE_LERP:
case MATH_SMOOTHSTEP:
+ case MATH_MOVE_TOWARD:
case MATH_DECTIME:
case MATH_DEG2RAD:
case MATH_RAD2DEG:
@@ -1669,6 +1678,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::REAL;
return mi;
} break;
+ case MATH_MOVE_TOWARD: {
+ MethodInfo mi("move_toward", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "delta"));
+ mi.return_val.type = Variant::REAL;
+ return mi;
+ } break;
case MATH_DECTIME: {
MethodInfo mi("dectime", PropertyInfo(Variant::REAL, "value"), PropertyInfo(Variant::REAL, "amount"), PropertyInfo(Variant::REAL, "step"));
mi.return_val.type = Variant::REAL;
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index c594480ff8..6ad70f2eb4 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -69,6 +69,7 @@ public:
MATH_INVERSE_LERP,
MATH_RANGE_LERP,
MATH_SMOOTHSTEP,
+ MATH_MOVE_TOWARD,
MATH_DECTIME,
MATH_RANDOMIZE,
MATH_RAND,
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 9590009a54..ec3e72eef7 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1995,7 +1995,6 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
}
}
- ERR_FAIL_V(op);
} break;
default: {
return p_node;
@@ -3497,7 +3496,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
_set_error("'class_name' is only valid for the main class namespace.");
return;
}
- if (self_path.empty()) {
+ if (self_path.begins_with("res://") && self_path.find("::") != -1) {
_set_error("'class_name' not allowed in built-in scripts.");
return;
}
@@ -4025,7 +4024,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
ERR_EXPLAIN("Exporting bit flags hint requires string constants.");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
break;
}
if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
@@ -4068,6 +4067,50 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
break;
}
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_RENDER") {
+
+ tokenizer->advance();
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in layers 2D render hint.");
+ return;
+ }
+ current_export.hint = PROPERTY_HINT_LAYERS_2D_RENDER;
+ break;
+ }
+
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_PHYSICS") {
+
+ tokenizer->advance();
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in layers 2D physics hint.");
+ return;
+ }
+ current_export.hint = PROPERTY_HINT_LAYERS_2D_PHYSICS;
+ break;
+ }
+
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_RENDER") {
+
+ tokenizer->advance();
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in layers 3D render hint.");
+ return;
+ }
+ current_export.hint = PROPERTY_HINT_LAYERS_3D_RENDER;
+ break;
+ }
+
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_PHYSICS") {
+
+ tokenizer->advance();
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in layers 3D physics hint.");
+ return;
+ }
+ current_export.hint = PROPERTY_HINT_LAYERS_3D_PHYSICS;
+ break;
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) {
//enumeration
current_export.hint = PROPERTY_HINT_ENUM;
@@ -6021,7 +6064,11 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
}
GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
+#ifdef DEBUG_ENABLED
+ if (p_node->get_datatype().has_type && p_node->type != Node::TYPE_ARRAY && p_node->type != Node::TYPE_DICTIONARY) {
+#else
if (p_node->get_datatype().has_type) {
+#endif
return p_node->get_datatype();
}
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 32a014e76d..3caa7b1d12 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -197,7 +197,7 @@ bool GridMap::get_collision_layer_bit(int p_bit) const {
void GridMap::set_theme(const Ref<MeshLibrary> &p_theme) {
ERR_EXPLAIN("GridMap.theme/set_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/set_mesh_library() instead.");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
set_mesh_library(p_theme);
}
@@ -205,7 +205,7 @@ void GridMap::set_theme(const Ref<MeshLibrary> &p_theme) {
Ref<MeshLibrary> GridMap::get_theme() const {
ERR_EXPLAIN("GridMap.theme/get_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/get_mesh_library() instead.");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
return get_mesh_library();
}
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 341d57f3e4..6c3ecee272 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -20,11 +20,6 @@ if env['tools']:
'glue/cs_glue_version.gen.h'
)
-vars = Variables()
-vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
-vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
-vars.Update(env_mono)
-
# Glue sources
if env_mono['mono_glue']:
env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED'])
diff --git a/modules/mono/build_scripts/godotsharptools_build.py b/modules/mono/build_scripts/godotsharptools_build.py
index af3a5cb5c6..17f9a990af 100644
--- a/modules/mono/build_scripts/godotsharptools_build.py
+++ b/modules/mono/build_scripts/godotsharptools_build.py
@@ -1,6 +1,5 @@
# Build GodotSharpTools solution
-
import os
from SCons.Script import Builder, Dir
@@ -53,21 +52,9 @@ def find_nuget_windows(env):
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
- from . import mono_reg_utils as monoreg
-
- mono_root = ''
- bits = env['bits']
+ from . mono_reg_utils import find_mono_root_dir
- if bits == '32':
- if os.getenv('MONO32_PREFIX'):
- mono_root = os.getenv('MONO32_PREFIX')
- else:
- mono_root = monoreg.find_mono_root_dir(bits)
- else:
- if os.getenv('MONO64_PREFIX'):
- mono_root = os.getenv('MONO64_PREFIX')
- else:
- mono_root = monoreg.find_mono_root_dir(bits)
+ mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits'])
if mono_root:
mono_bin_dir = os.path.join(mono_root, 'bin')
@@ -114,21 +101,9 @@ def find_msbuild_unix(filename):
def find_msbuild_windows(env):
- from . import mono_reg_utils as monoreg
+ from . mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg
- mono_root = ''
- bits = env['bits']
-
- if bits == '32':
- if os.getenv('MONO32_PREFIX'):
- mono_root = os.getenv('MONO32_PREFIX')
- else:
- mono_root = monoreg.find_mono_root_dir(bits)
- else:
- if os.getenv('MONO64_PREFIX'):
- mono_root = os.getenv('MONO64_PREFIX')
- else:
- mono_root = monoreg.find_mono_root_dir(bits)
+ mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits'])
if not mono_root:
raise RuntimeError('Cannot find mono root directory')
@@ -148,7 +123,7 @@ def find_msbuild_windows(env):
}
return (msbuild_mono, framework_path, mono_msbuild_env)
- msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
+ msbuild_tools_path = find_msbuild_tools_path_reg()
if msbuild_tools_path:
return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {})
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 4cfa2a5b93..c549640d61 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -1,15 +1,30 @@
import imp
import os
+import os.path
import sys
import subprocess
from distutils.version import LooseVersion
-from SCons.Script import BoolVariable, Dir, Environment, Variables
+from SCons.Script import Dir, Environment
if os.name == 'nt':
from . import mono_reg_utils as monoreg
+android_arch_dirs = {
+ 'armv7': 'armeabi-v7a',
+ 'arm64v8': 'arm64-v8a',
+ 'x86': 'x86',
+ 'x86_64': 'x86_64'
+}
+
+
+def get_android_out_dir(env):
+ return os.path.join(Dir('#platform/android/java/libs').abspath,
+ 'release' if env['target'] == 'release' else 'debug',
+ android_arch_dirs[env['android_arch']])
+
+
def find_file_in_dir(directory, files, prefix='', extension=''):
if not extension.startswith('.'):
extension = '.' + extension
@@ -20,47 +35,47 @@ def find_file_in_dir(directory, files, prefix='', extension=''):
def copy_file(src_dir, dst_dir, name):
- from shutil import copyfile
+ from shutil import copy
- src_path = os.path.join(src_dir, name)
- dst_path = os.path.join(dst_dir, name)
+ src_path = os.path.join(Dir(src_dir).abspath, name)
+ dst_dir = Dir(dst_dir).abspath
if not os.path.isdir(dst_dir):
os.mkdir(dst_dir)
- copyfile(src_path, dst_path)
+ copy(src_path, dst_dir)
def configure(env, env_mono):
- envvars = Variables()
- envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
- envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False))
- envvars.Update(env)
-
bits = env['bits']
+ is_android = env['platform'] == 'android'
tools_enabled = env['tools']
mono_static = env['mono_static']
copy_mono_root = env['copy_mono_root']
+ mono_prefix = env['mono_prefix']
+
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
+ if is_android and not env['android_arch'] in android_arch_dirs:
+ raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch'])
+
+ if is_android and tools_enabled:
+ # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders.
+ raise RuntimeError('This module does not currently support building for android with tools enabled')
+
+ if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix:
+ print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead")
+
if env['platform'] == 'windows':
- mono_root = ''
+ mono_root = mono_prefix
- if bits == '32':
- if os.getenv('MONO32_PREFIX'):
- mono_root = os.getenv('MONO32_PREFIX')
- elif os.name == 'nt':
- mono_root = monoreg.find_mono_root_dir(bits)
- else:
- if os.getenv('MONO64_PREFIX'):
- mono_root = os.getenv('MONO64_PREFIX')
- elif os.name == 'nt':
- mono_root = monoreg.find_mono_root_dir(bits)
+ if not mono_root and os.name == 'nt':
+ mono_root = monoreg.find_mono_root_dir(bits)
if not mono_root:
- raise RuntimeError('Mono installation directory not found')
+ raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter")
print('Found Mono root directory: ' + mono_root)
@@ -113,21 +128,18 @@ def configure(env, env_mono):
if not mono_dll_name:
raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
- copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
+ copy_file(mono_bin_path, '#bin', mono_dll_name + '.dll')
else:
is_apple = (sys.platform == 'darwin' or "osxcross" in env)
sharedlib_ext = '.dylib' if is_apple else '.so'
- mono_root = ''
+ mono_root = mono_prefix
mono_lib_path = ''
+ mono_so_name = ''
- if bits == '32':
- if os.getenv('MONO32_PREFIX'):
- mono_root = os.getenv('MONO32_PREFIX')
- else:
- if os.getenv('MONO64_PREFIX'):
- mono_root = os.getenv('MONO64_PREFIX')
+ if not mono_root and is_android:
+ raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter")
if not mono_root and is_apple:
# Try with some known directories under OSX
@@ -142,7 +154,8 @@ def configure(env, env_mono):
if not mono_root and mono_static:
mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
if not mono_root:
- raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
+ raise RuntimeError("Building with mono_static=yes, but failed to find the mono prefix with pkg-config; " + \
+ "specify one manually with the 'mono_prefix' SCons parameter")
if mono_root:
print('Found Mono root directory: ' + mono_root)
@@ -174,6 +187,8 @@ def configure(env, env_mono):
if is_apple:
env.Append(LIBS=['iconv', 'pthread'])
+ elif is_android:
+ env.Append(LIBS=['m', 'dl'])
else:
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
@@ -183,7 +198,7 @@ def configure(env, env_mono):
if not mono_so_name:
raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
- copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+ copy_file(mono_lib_path, '#bin', 'lib' + mono_so_name + sharedlib_ext)
else:
assert not mono_static
@@ -196,9 +211,6 @@ def configure(env, env_mono):
env.ParseConfig('pkg-config monosgen-2 --libs')
env_mono.ParseConfig('pkg-config monosgen-2 --cflags')
- mono_lib_path = ''
- mono_so_name = ''
-
tmpenv = Environment()
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
@@ -213,11 +225,13 @@ def configure(env, env_mono):
if not mono_so_name:
raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
- copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+ if not mono_static:
+ libs_output_dir = get_android_out_dir(env) if is_android else '#bin'
+ copy_file(mono_lib_path, libs_output_dir, 'lib' + mono_so_name + sharedlib_ext)
env.Append(LINKFLAGS='-rdynamic')
- if not tools_enabled:
+ if not tools_enabled and not is_android:
if not mono_root:
mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
@@ -241,7 +255,7 @@ def make_template_dir(env, mono_root):
template_dir_name = ''
- if platform in ['windows', 'osx', 'x11']:
+ if platform in ['windows', 'osx', 'x11', 'android']:
template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
else:
assert False
@@ -256,12 +270,13 @@ def make_template_dir(env, mono_root):
# Copy etc/mono/
- template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
- copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
+ if platform != 'android':
+ template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
+ copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
# Copy the required shared libraries
- copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform'])
+ copy_mono_shared_libs(env, mono_root, template_mono_root_dir)
def copy_mono_root_files(env, mono_root):
@@ -285,7 +300,7 @@ def copy_mono_root_files(env, mono_root):
# Copy the required shared libraries
- copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform'])
+ copy_mono_shared_libs(env, mono_root, editor_mono_root_dir)
# Copy framework assemblies
@@ -332,39 +347,55 @@ def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0'))
copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0'))
copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5'))
- copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
+ if os.path.isdir(os.path.join(mono_etc_dir, 'mconfig')):
+ copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
for file in glob(os.path.join(mono_etc_dir, '*')):
if os.path.isfile(file):
copy(file, target_mono_config_dir)
-def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform):
+def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
from shutil import copy
+ def copy_if_exists(src, dst):
+ if os.path.isfile(src):
+ copy(src, dst)
+
+ platform = env['platform']
+
if platform == 'windows':
target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin')
if not os.path.isdir(target_mono_bin_dir):
os.makedirs(target_mono_bin_dir)
- copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll'))
+ copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), target_mono_bin_dir)
else:
- target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib')
+ target_mono_lib_dir = get_android_out_dir(env) if platform == 'android' else os.path.join(target_mono_root_dir, 'lib')
if not os.path.isdir(target_mono_lib_dir):
os.makedirs(target_mono_lib_dir)
if platform == 'osx':
- copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib'))
- elif platform == 'x11':
- copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so'))
- copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so'))
+ # TODO: Make sure nothing is missing
+ copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), target_mono_lib_dir)
+ elif platform == 'x11' or platform == 'android':
+ lib_file_names = [lib_name + '.so' for lib_name in [
+ 'libmono-btls-shared', 'libmono-ee-interp', 'libmono-native', 'libMonoPosixHelper',
+ 'libmono-profiler-aot', 'libmono-profiler-coverage', 'libmono-profiler-log', 'libMonoSupportW'
+ ]]
+
+ for lib_file_name in lib_file_names:
+ copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir)
def configure_for_mono_version(env, mono_version):
if mono_version is None:
- raise RuntimeError('Mono JIT compiler version not found')
+ if os.getenv('MONO_VERSION'):
+ mono_version = os.getenv('MONO_VERSION')
+ else:
+ raise RuntimeError("Mono JIT compiler version not found; specify one manually with the 'MONO_VERSION' environment variable")
print('Found Mono JIT compiler version: ' + str(mono_version))
if mono_version >= LooseVersion('5.12.0'):
env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
diff --git a/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff
new file mode 100644
index 0000000000..05f8dcadcc
--- /dev/null
+++ b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff
@@ -0,0 +1,70 @@
+diff --git a/libgc/include/private/gcconfig.h b/libgc/include/private/gcconfig.h
+index e2bdf13ac3e..f962200ba4e 100644
+--- a/libgc/include/private/gcconfig.h
++++ b/libgc/include/private/gcconfig.h
+@@ -2255,6 +2255,14 @@
+ # define GETPAGESIZE() getpagesize()
+ # endif
+
++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
++ || defined(ARM32) || defined(I386) /* but not x32 */)
++ /* tkill() exists only on arm32/mips(32)/x86. */
++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
++# define USE_TKILL_ON_ANDROID
++#endif
++
+ # if defined(SUNOS5) || defined(DRSNX) || defined(UTS4)
+ /* OS has SVR4 generic features. Probably others also qualify. */
+ # define SVR4
+diff --git a/libgc/pthread_stop_world.c b/libgc/pthread_stop_world.c
+index f93ce26b562..4a49a6d578c 100644
+--- a/libgc/pthread_stop_world.c
++++ b/libgc/pthread_stop_world.c
+@@ -336,7 +336,7 @@ void GC_push_all_stacks()
+ pthread_t GC_stopping_thread;
+ int GC_stopping_pid;
+
+-#ifdef HOST_ANDROID
++#ifdef USE_TKILL_ON_ANDROID
+ static
+ int android_thread_kill(pid_t tid, int sig)
+ {
+diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c
+index ad9b8823f8f..3542b32b540 100644
+--- a/mono/metadata/threads.c
++++ b/mono/metadata/threads.c
+@@ -77,8 +77,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
+ #include <zircon/syscalls.h>
+ #endif
+
+-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
+-#define USE_TKILL_ON_ANDROID 1
++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
++ || defined(ARM32) || defined(I386) /* but not x32 */)
++ /* tkill() exists only on arm32/mips(32)/x86. */
++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
++# define USE_TKILL_ON_ANDROID
+ #endif
+
+ #ifdef HOST_ANDROID
+diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c
+index 3e4bf93de5f..79c9f731fe7 100644
+--- a/mono/utils/mono-threads-posix.c
++++ b/mono/utils/mono-threads-posix.c
+@@ -31,8 +31,12 @@
+
+ #include <errno.h>
+
+-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
+-#define USE_TKILL_ON_ANDROID 1
++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
++ || defined(ARM32) || defined(I386) /* but not x32 */)
++ /* tkill() exists only on arm32/mips(32)/x86. */
++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
++# define USE_TKILL_ON_ANDROID
+ #endif
+
+ #ifdef USE_TKILL_ON_ANDROID
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 3b2e96765e..9adf4ee6e5 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -8,6 +8,16 @@ def configure(env):
env.use_ptrcall = True
env.add_module_version_string('mono')
+ from SCons.Script import BoolVariable, PathVariable, Variables
+
+ envvars = Variables()
+ envvars.Add(PathVariable('mono_prefix', 'Path to the mono installation directory for the target platform and architecture', '', PathVariable.PathAccept))
+ envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
+ envvars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
+ envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False))
+ envvars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
+ envvars.Update(env)
+
def get_doc_classes():
return [
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index e735e0f741..4a1fb8e5ed 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -380,7 +380,6 @@ public:
virtual bool supports_builtin_mode() const;
/* TODO? */ virtual int find_function(const String &p_function, const String &p_code) const { return -1; }
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
- /* TODO? */ Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, String &r_call_hint) { return ERR_UNAVAILABLE; }
virtual String _get_indentation() const;
/* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {}
/* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {}
diff --git a/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs
index e45dd2025b..44a43f0ddd 100644
--- a/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs
+++ b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Runtime.CompilerServices;
namespace GodotSharpTools.Editor
@@ -62,7 +63,7 @@ namespace GodotSharpTools.Editor
{
// OSX export templates are contained in a zip, so we place
// our custom template inside it and let Godot do the rest.
- return !featureSet.Contains("OSX");
+ return !featureSet.Any(f => new[] {"OSX", "Android"}.Contains(f));
}
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index cd7774e7a1..2d618f7891 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2536,13 +2536,8 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
switch (p_val.get_type()) {
case Variant::NIL:
- if (ClassDB::class_exists(r_iarg.type.cname)) {
- // Object type
- r_iarg.default_argument = "null";
- } else {
- // Variant
- r_iarg.default_argument = "null";
- }
+ // Either Object type or Variant
+ r_iarg.default_argument = "null";
break;
// Atomic types
case Variant::BOOL:
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index ae5b939b26..126178125f 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -125,11 +125,21 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name,
project_dll_src_path, &scripts_assembly, /* refonly: */ true);
- ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
+ ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name);
ERR_FAIL_COND(!load_success);
Vector<String> search_dirs;
- GDMonoAssembly::fill_search_dirs(search_dirs, build_config);
+ String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
+ String android_bcl_dir = templates_dir.plus_file("android-bcl");
+
+ String custom_lib_dir;
+
+ if (p_features.find("Android") && DirAccess::exists(android_bcl_dir)) {
+ custom_lib_dir = android_bcl_dir;
+ }
+
+ GDMonoAssembly::fill_search_dirs(search_dirs, build_config, custom_lib_dir);
+
Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies);
ERR_FAIL_COND(depend_error != OK);
}
@@ -152,7 +162,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
int i = 0;
for (const Set<String>::Element *E = p_features.front(); E; E = E->next()) {
MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get());
- mono_array_set(features, MonoString *, i, boxed);
+ mono_array_setref(features, i, boxed);
i++;
}
@@ -234,8 +244,10 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c
r_dependencies.insert(ref_name, ref_assembly->get_path());
Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
- if (err != OK)
- return err;
+ if (err != OK) {
+ ERR_EXPLAIN("Cannot load one of the dependencies for the assembly: " + ref_name);
+ ERR_FAIL_V(err);
+ }
}
return OK;
diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs
index 8fb8730b88..2d8c63fe7f 100644
--- a/modules/mono/glue/Managed/Files/Mathf.cs
+++ b/modules/mono/glue/Managed/Files/Mathf.cs
@@ -210,6 +210,11 @@ namespace Godot
return a < b ? a : b;
}
+ public static real_t MoveToward(real_t from, real_t to, real_t delta)
+ {
+ return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta;
+ }
+
public static int NearestPo2(int value)
{
value--;
diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs
index bb1950e1a8..a7f26283a7 100644
--- a/modules/mono/glue/Managed/Files/Vector2.cs
+++ b/modules/mono/glue/Managed/Files/Vector2.cs
@@ -186,6 +186,14 @@ namespace Godot
return res;
}
+ public Vector2 MoveToward(Vector2 to, real_t delta)
+ {
+ var v = this;
+ var vd = to - v;
+ var len = vd.Length();
+ return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
+ }
+
public Vector2 Normalized()
{
var v = this;
diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs
index 283cb6341a..16803ae55c 100644
--- a/modules/mono/glue/Managed/Files/Vector3.cs
+++ b/modules/mono/glue/Managed/Files/Vector3.cs
@@ -190,6 +190,14 @@ namespace Godot
);
}
+ public Vector3 MoveToward(Vector3 to, real_t delta)
+ {
+ var v = this;
+ var vd = to - v;
+ var len = vd.Length();
+ return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
+ }
+
public Axis MaxAxis()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index 7385014a53..75b2dfce9a 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -166,7 +166,7 @@ MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
int i = 0;
for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get().name);
- mono_array_set(result, MonoString *, i, boxed);
+ mono_array_setref(result, i, boxed);
i++;
}
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 19e49d29f9..7699e0d0cd 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -251,17 +251,19 @@ void GDMono::initialize() {
String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
#ifdef TOOLS_ENABLED
- if (DirAccess::exists(bundled_assembly_rootdir) && DirAccess::exists(bundled_config_dir)) {
+ if (DirAccess::exists(bundled_assembly_rootdir)) {
assembly_rootdir = bundled_assembly_rootdir;
+ }
+
+ if (DirAccess::exists(bundled_config_dir)) {
config_dir = bundled_config_dir;
}
#ifdef WINDOWS_ENABLED
if (assembly_rootdir.empty() || config_dir.empty()) {
+ ERR_PRINT("Cannot find Mono in the registry");
// Assertion: if they are not set, then they weren't found in the registry
CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0);
-
- ERR_PRINT("Cannot find Mono in the registry");
}
#endif // WINDOWS_ENABLED
@@ -807,6 +809,8 @@ Error GDMono::_unload_scripts_domain() {
mono_gc_collect(mono_gc_max_generation());
+ GDMonoUtils::clear_godot_api_cache();
+
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
core_api_assembly = NULL;
@@ -926,6 +930,7 @@ Error GDMono::reload_scripts_domain() {
Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
CRASH_COND(p_domain == NULL);
+ CRASH_COND(p_domain == SCRIPTS_DOMAIN); // Should use _unload_scripts_domain() instead
String domain_name = mono_domain_get_friendly_name(p_domain);
@@ -1078,8 +1083,6 @@ GDMono::~GDMono() {
}
assemblies.clear();
- GDMonoUtils::clear_cache();
-
print_verbose("Mono: Runtime cleanup...");
mono_jit_cleanup(root_domain);
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 8fec28b186..f1f0015ac9 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -46,11 +46,17 @@ bool GDMonoAssembly::in_preload = false;
Vector<String> GDMonoAssembly::search_dirs;
-void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config) {
+void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
- const char *rootdir = mono_assembly_getrootdir();
- if (rootdir) {
- String framework_dir = String::utf8(rootdir).plus_file("mono").plus_file("4.5");
+ String framework_dir;
+
+ if (!p_custom_bcl_dir.empty()) {
+ framework_dir = p_custom_bcl_dir;
+ } else if (mono_assembly_getrootdir()) {
+ framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5");
+ }
+
+ if (!framework_dir.empty()) {
r_search_dirs.push_back(framework_dir);
r_search_dirs.push_back(framework_dir.plus_file("Facades"));
}
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 32432af37d..39749dfc1d 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -122,7 +122,7 @@ public:
GDMonoClass *get_object_derived_class(const StringName &p_class);
- static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String());
+ static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index c462b8f71d..87157ed233 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -1019,7 +1019,7 @@ MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) {
for (int i = 0; i < p_array.size(); i++) {
MonoString *boxed = mono_string_from_godot(r[i]);
- mono_array_set(ret, MonoString *, i, boxed);
+ mono_array_setref(ret, i, boxed);
}
return ret;
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index f290c6c8ac..968b316a3e 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -109,7 +109,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
for (int i = 0; i < params_count; i++) {
MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
- mono_array_set(params, MonoObject *, i, boxed_param);
+ mono_array_setref(params, i, boxed_param);
}
MonoException *exc = NULL;
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index 5842e26241..f1da00638f 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -142,7 +142,7 @@ bool GDMonoProperty::has_setter() {
void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
MonoMethod *prop_method = mono_property_get_set_method(mono_property);
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
- mono_array_set(params, MonoObject *, 0, p_value);
+ mono_array_setref(params, 0, p_value);
MonoException *exc = NULL;
GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc);
if (exc) {
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 413c8cba85..1b32f1126e 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -50,6 +50,7 @@ MonoCache mono_cache;
#define CACHE_AND_CHECK(m_var, m_val) \
{ \
+ CRASH_COND(m_var != NULL); \
m_var = m_val; \
if (!m_var) { \
ERR_EXPLAIN("Mono Cache: Member " #m_var " is null"); \
@@ -65,7 +66,9 @@ MonoCache mono_cache;
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val)
#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val)
-void MonoCache::clear_members() {
+void MonoCache::clear_corlib_cache() {
+
+ corlib_cache_updated = false;
class_MonoObject = NULL;
class_bool = NULL;
@@ -93,6 +96,11 @@ void MonoCache::clear_members() {
#endif
class_KeyNotFoundException = NULL;
+}
+
+void MonoCache::clear_godot_api_cache() {
+
+ godot_api_cache_updated = false;
rawclass_Dictionary = NULL;
@@ -176,12 +184,6 @@ void MonoCache::clear_members() {
task_scheduler_handle = Ref<MonoGCHandle>();
}
-void MonoCache::cleanup() {
-
- corlib_cache_updated = false;
- godot_api_cache_updated = false;
-}
-
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
#define GODOT_API_NS_CLAS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class))
@@ -281,13 +283,10 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
-
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, (GenericIDictionaryIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2));
@@ -310,11 +309,6 @@ void update_godot_api_cache() {
mono_cache.godot_api_cache_updated = true;
}
-void clear_cache() {
- mono_cache.cleanup();
- mono_cache.clear_members();
-}
-
MonoObject *unmanaged_get_managed(Object *unmanaged) {
if (!unmanaged)
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index ee239be959..00e1ffdd31 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -218,14 +218,12 @@ struct MonoCache {
bool corlib_cache_updated;
bool godot_api_cache_updated;
- void clear_members();
- void cleanup();
+ void clear_corlib_cache();
+ void clear_godot_api_cache();
MonoCache() {
- corlib_cache_updated = false;
- godot_api_cache_updated = false;
-
- clear_members();
+ clear_corlib_cache();
+ clear_godot_api_cache();
}
};
@@ -233,7 +231,13 @@ extern MonoCache mono_cache;
void update_corlib_cache();
void update_godot_api_cache();
-void clear_cache();
+
+inline void clear_corlib_cache() {
+ mono_cache.clear_corlib_cache();
+}
+inline void clear_godot_api_cache() {
+ mono_cache.clear_godot_api_cache();
+}
_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 5d37e8212f..0e1739b754 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -95,7 +95,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
for (int i = 0; i < signal_argc; i++) {
MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
- mono_array_set(signal_args, MonoObject *, i, boxed);
+ mono_array_setref(signal_args, i, boxed);
}
MonoException *exc = NULL;
diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp
index fda82295de..70d0f770d8 100644
--- a/modules/opus/audio_stream_opus.cpp
+++ b/modules/opus/audio_stream_opus.cpp
@@ -313,7 +313,7 @@ int AudioStreamPlaybackOpus::mix(int16_t *p_buffer, int p_frames) {
bool ok = op_pcm_seek(opus_file, (loop_restart_time * osrate) + pre_skip) == 0;
if (!ok) {
playing = false;
- ERR_PRINT("loop restart time rejected")
+ ERR_PRINT("Loop restart time rejected");
}
frames_mixed = (loop_restart_time * osrate) + pre_skip;
diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp
index eadc11fcee..9f30806925 100644
--- a/modules/recast/navigation_mesh_editor_plugin.cpp
+++ b/modules/recast/navigation_mesh_editor_plugin.cpp
@@ -67,9 +67,7 @@ void NavigationMeshEditor::_bake_pressed() {
EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
EditorNavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node);
- if (node) {
- node->update_gizmo();
- }
+ node->update_gizmo();
}
void NavigationMeshEditor::_clear_pressed() {
diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp
index 0cac07e3e7..14467dc5c7 100644
--- a/modules/recast/navigation_mesh_generator.cpp
+++ b/modules/recast/navigation_mesh_generator.cpp
@@ -45,6 +45,10 @@
#include "scene/resources/shape.h"
#include "scene/resources/sphere_shape.h"
+#ifdef MODULE_CSG_ENABLED
+#include "modules/csg/csg_shape.h"
+#endif
+
EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::singleton = NULL;
void EditorNavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
@@ -134,6 +138,20 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran
}
}
+#ifdef MODULE_CSG_ENABLED
+ if (Object::cast_to<CSGShape>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
+
+ CSGShape *csg_shape = Object::cast_to<CSGShape>(p_node);
+ Array meshes = csg_shape->get_meshes();
+ if (!meshes.empty()) {
+ Ref<Mesh> mesh = meshes[1];
+ if (mesh.is_valid()) {
+ _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_verticies, p_indices);
+ }
+ }
+ }
+#endif
+
if (Object::cast_to<StaticBody>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES) {
StaticBody *static_body = Object::cast_to<StaticBody>(p_node);
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index bd84a28c84..a9340b1498 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -122,13 +122,13 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
}
if (idxG == -1) {
- ERR_PRINT("TinyEXR: G channel not found.")
+ ERR_PRINT("TinyEXR: G channel not found.");
// @todo { free exr_image }
return ERR_FILE_CORRUPT;
}
if (idxB == -1) {
- ERR_PRINT("TinyEXR: B channel not found.")
+ ERR_PRINT("TinyEXR: B channel not found.");
// @todo { free exr_image }
return ERR_FILE_CORRUPT;
}
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
index 74d5f29c02..21e8a38c16 100644
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
@@ -102,114 +102,117 @@
</constant>
<constant name="MATH_RANGE_LERP" value="28" enum="BuiltinFunc">
</constant>
- <constant name="MATH_DECTIME" value="29" enum="BuiltinFunc">
+ <constant name="MATH_MOVE_TOWARD" value="29" enum="BuiltinFunc">
+ Moves the number toward a value, based on the third input.
+ </constant>
+ <constant name="MATH_DECTIME" value="30" enum="BuiltinFunc">
Return the result of 'value' decreased by 'step' * 'amount'.
</constant>
- <constant name="MATH_RANDOMIZE" value="30" enum="BuiltinFunc">
+ <constant name="MATH_RANDOMIZE" value="31" enum="BuiltinFunc">
Randomize the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time.
</constant>
- <constant name="MATH_RAND" value="31" enum="BuiltinFunc">
+ <constant name="MATH_RAND" value="32" enum="BuiltinFunc">
Return a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use it with the remainder function.
</constant>
- <constant name="MATH_RANDF" value="32" enum="BuiltinFunc">
+ <constant name="MATH_RANDF" value="33" enum="BuiltinFunc">
Return a random floating-point value between 0 and 1. To obtain a random value between 0 to N, you can use it with multiplication.
</constant>
- <constant name="MATH_RANDOM" value="33" enum="BuiltinFunc">
+ <constant name="MATH_RANDOM" value="34" enum="BuiltinFunc">
Return a random floating-point value between the two inputs.
</constant>
- <constant name="MATH_SEED" value="34" enum="BuiltinFunc">
+ <constant name="MATH_SEED" value="35" enum="BuiltinFunc">
Set the seed for the random number generator.
</constant>
- <constant name="MATH_RANDSEED" value="35" enum="BuiltinFunc">
+ <constant name="MATH_RANDSEED" value="36" enum="BuiltinFunc">
Return a random value from the given seed, along with the new seed.
</constant>
- <constant name="MATH_DEG2RAD" value="36" enum="BuiltinFunc">
+ <constant name="MATH_DEG2RAD" value="37" enum="BuiltinFunc">
Convert the input from degrees to radians.
</constant>
- <constant name="MATH_RAD2DEG" value="37" enum="BuiltinFunc">
+ <constant name="MATH_RAD2DEG" value="38" enum="BuiltinFunc">
Convert the input from radians to degrees.
</constant>
- <constant name="MATH_LINEAR2DB" value="38" enum="BuiltinFunc">
+ <constant name="MATH_LINEAR2DB" value="39" enum="BuiltinFunc">
Convert the input from linear volume to decibel volume.
</constant>
- <constant name="MATH_DB2LINEAR" value="39" enum="BuiltinFunc">
+ <constant name="MATH_DB2LINEAR" value="40" enum="BuiltinFunc">
Convert the input from decibel volume to linear volume.
</constant>
- <constant name="MATH_POLAR2CARTESIAN" value="40" enum="BuiltinFunc">
+ <constant name="MATH_POLAR2CARTESIAN" value="41" enum="BuiltinFunc">
Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (x and y axis).
</constant>
- <constant name="MATH_CARTESIAN2POLAR" value="41" enum="BuiltinFunc">
+ <constant name="MATH_CARTESIAN2POLAR" value="42" enum="BuiltinFunc">
Converts a 2D point expressed in the cartesian coordinate system (x and y axis) to the polar coordinate system (a distance from the origin and an angle).
</constant>
- <constant name="MATH_WRAP" value="42" enum="BuiltinFunc">
+ <constant name="MATH_WRAP" value="43" enum="BuiltinFunc">
</constant>
- <constant name="MATH_WRAPF" value="43" enum="BuiltinFunc">
+ <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc">
</constant>
- <constant name="LOGIC_MAX" value="44" enum="BuiltinFunc">
+ <constant name="LOGIC_MAX" value="45" enum="BuiltinFunc">
Return the greater of the two numbers, also known as their maximum.
</constant>
- <constant name="LOGIC_MIN" value="45" enum="BuiltinFunc">
+ <constant name="LOGIC_MIN" value="46" enum="BuiltinFunc">
Return the lesser of the two numbers, also known as their minimum.
</constant>
- <constant name="LOGIC_CLAMP" value="46" enum="BuiltinFunc">
+ <constant name="LOGIC_CLAMP" value="47" enum="BuiltinFunc">
Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code].
</constant>
- <constant name="LOGIC_NEAREST_PO2" value="47" enum="BuiltinFunc">
+ <constant name="LOGIC_NEAREST_PO2" value="48" enum="BuiltinFunc">
Return the nearest power of 2 to the input.
</constant>
- <constant name="OBJ_WEAKREF" value="48" enum="BuiltinFunc">
+ <constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc">
Create a [WeakRef] from the input.
</constant>
- <constant name="FUNC_FUNCREF" value="49" enum="BuiltinFunc">
+ <constant name="FUNC_FUNCREF" value="50" enum="BuiltinFunc">
Create a [FuncRef] from the input.
</constant>
- <constant name="TYPE_CONVERT" value="50" enum="BuiltinFunc">
+ <constant name="TYPE_CONVERT" value="51" enum="BuiltinFunc">
Convert between types.
</constant>
- <constant name="TYPE_OF" value="51" enum="BuiltinFunc">
+ <constant name="TYPE_OF" value="52" enum="BuiltinFunc">
Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned.
</constant>
- <constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc">
+ <constant name="TYPE_EXISTS" value="53" enum="BuiltinFunc">
Checks if a type is registered in the [ClassDB].
</constant>
- <constant name="TEXT_CHAR" value="53" enum="BuiltinFunc">
+ <constant name="TEXT_CHAR" value="54" enum="BuiltinFunc">
Return a character with the given ascii value.
</constant>
- <constant name="TEXT_STR" value="54" enum="BuiltinFunc">
+ <constant name="TEXT_STR" value="55" enum="BuiltinFunc">
Convert the input to a string.
</constant>
- <constant name="TEXT_PRINT" value="55" enum="BuiltinFunc">
+ <constant name="TEXT_PRINT" value="56" enum="BuiltinFunc">
Print the given string to the output window.
</constant>
- <constant name="TEXT_PRINTERR" value="56" enum="BuiltinFunc">
+ <constant name="TEXT_PRINTERR" value="57" enum="BuiltinFunc">
Print the given string to the standard error output.
</constant>
- <constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc">
+ <constant name="TEXT_PRINTRAW" value="58" enum="BuiltinFunc">
Print the given string to the standard output, without adding a newline.
</constant>
- <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc">
+ <constant name="VAR_TO_STR" value="59" enum="BuiltinFunc">
Serialize a [Variant] to a string.
</constant>
- <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc">
+ <constant name="STR_TO_VAR" value="60" enum="BuiltinFunc">
Deserialize a [Variant] from a string serialized using [code]VAR_TO_STR[/code].
</constant>
- <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc">
+ <constant name="VAR_TO_BYTES" value="61" enum="BuiltinFunc">
Serialize a [Variant] to a [PoolByteArray].
</constant>
- <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc">
+ <constant name="BYTES_TO_VAR" value="62" enum="BuiltinFunc">
Deserialize a [Variant] from a [PoolByteArray] serialized using [code]VAR_TO_BYTES[/code].
</constant>
- <constant name="COLORN" value="62" enum="BuiltinFunc">
+ <constant name="COLORN" value="63" enum="BuiltinFunc">
Return the [Color] with the given name and alpha ranging from 0 to 1. Note: names are defined in color_names.inc.
</constant>
- <constant name="MATH_SMOOTHSTEP" value="63" enum="BuiltinFunc">
+ <constant name="MATH_SMOOTHSTEP" value="64" enum="BuiltinFunc">
Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [code]MATH_LERP[/code], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula:
[codeblock]
var t = clamp((weight - from) / (to - from), 0.0, 1.0)
return t * t * (3.0 - 2.0 * t)
[/codeblock]
</constant>
- <constant name="FUNC_MAX" value="64" enum="BuiltinFunc">
+ <constant name="FUNC_MAX" value="65" enum="BuiltinFunc">
The maximum value the [member function] property can have.
</constant>
</constants>
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index d207656705..75b79f8929 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -68,6 +68,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"lerp",
"inverse_lerp",
"range_lerp",
+ "move_toward",
"dectime",
"randomize",
"randi",
@@ -206,6 +207,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case MATH_LERP:
case MATH_INVERSE_LERP:
case MATH_SMOOTHSTEP:
+ case MATH_MOVE_TOWARD:
case MATH_DECTIME:
case MATH_WRAP:
case MATH_WRAPF:
@@ -347,6 +349,14 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
else
return PropertyInfo(Variant::REAL, "weight");
} break;
+ case MATH_MOVE_TOWARD: {
+ if (p_idx == 0)
+ return PropertyInfo(Variant::REAL, "from");
+ else if (p_idx == 1)
+ return PropertyInfo(Variant::REAL, "to");
+ else
+ return PropertyInfo(Variant::REAL, "delta");
+ } break;
case MATH_DECTIME: {
if (p_idx == 0)
return PropertyInfo(Variant::REAL, "value");
@@ -580,6 +590,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
case MATH_INVERSE_LERP:
case MATH_RANGE_LERP:
case MATH_SMOOTHSTEP:
+ case MATH_MOVE_TOWARD:
case MATH_DECTIME: {
t = Variant::REAL;
@@ -916,6 +927,13 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
VALIDATE_ARG_NUM(2);
*r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
} break;
+ case VisualScriptBuiltinFunc::MATH_MOVE_TOWARD: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *r_return = Math::move_toward((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
+ } break;
case VisualScriptBuiltinFunc::MATH_DECTIME: {
VALIDATE_ARG_NUM(0);
@@ -1363,6 +1381,7 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(MATH_LERP);
BIND_ENUM_CONSTANT(MATH_INVERSE_LERP);
BIND_ENUM_CONSTANT(MATH_RANGE_LERP);
+ BIND_ENUM_CONSTANT(MATH_MOVE_TOWARD);
BIND_ENUM_CONSTANT(MATH_DECTIME);
BIND_ENUM_CONSTANT(MATH_RANDOMIZE);
BIND_ENUM_CONSTANT(MATH_RAND);
@@ -1453,6 +1472,7 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/smoothstep", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SMOOTHSTEP>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/move_toward", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_MOVE_TOWARD>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index 50854c16b1..3e452cd94f 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -67,6 +67,7 @@ public:
MATH_LERP,
MATH_INVERSE_LERP,
MATH_RANGE_LERP,
+ MATH_MOVE_TOWARD,
MATH_DECTIME,
MATH_RANDOMIZE,
MATH_RAND,
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index aad6e95845..f84f2c90cd 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -2111,6 +2111,9 @@ void VisualScriptEditor::clear_executing_line() {
void VisualScriptEditor::trim_trailing_whitespace() {
}
+void VisualScriptEditor::insert_final_newline() {
+}
+
void VisualScriptEditor::convert_indent_to_spaces() {
}
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 3d3a49f672..6072e77342 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -266,6 +266,7 @@ public:
virtual void set_executing_line(int p_line);
virtual void clear_executing_line();
virtual void trim_trailing_whitespace();
+ virtual void insert_final_newline();
virtual void convert_indent_to_spaces();
virtual void convert_indent_to_tabs();
virtual void ensure_focus();
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 692705e411..e652abbe6a 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -143,8 +143,7 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) {
bool ok = ov_time_seek(&vf, loop_restart_time) == 0;
if (!ok) {
playing = false;
- //ERR_EXPLAIN("loop restart time rejected");
- ERR_PRINT("loop restart time rejected")
+ ERR_PRINT("Loop restart time rejected");
}
frames_mixed = stream_srate * loop_restart_time;
diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp
index 6485c95360..3670edc9ea 100644
--- a/modules/webm/video_stream_webm.cpp
+++ b/modules/webm/video_stream_webm.cpp
@@ -413,10 +413,11 @@ void VideoStreamPlaybackWebm::delete_pointers() {
if (audio_frame)
memdelete(audio_frame);
- for (int i = 0; i < video_frames_capacity; ++i)
- memdelete(video_frames[i]);
- if (video_frames)
+ if (video_frames) {
+ for (int i = 0; i < video_frames_capacity; ++i)
+ memdelete(video_frames[i]);
memfree(video_frames);
+ }
if (video)
memdelete(video);
diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py
index 2e3a18ad0e..48b4c33c5d 100644
--- a/modules/webrtc/config.py
+++ b/modules/webrtc/config.py
@@ -7,7 +7,8 @@ def configure(env):
def get_doc_classes():
return [
"WebRTCPeerConnection",
- "WebRTCDataChannel"
+ "WebRTCDataChannel",
+ "WebRTCMultiplayer"
]
def get_doc_path():
diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
index dcc14d4ddb..7cce97244d 100644
--- a/modules/webrtc/doc_classes/WebRTCDataChannel.xml
+++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
@@ -11,85 +11,106 @@
<return type="void">
</return>
<description>
+ Closes this data channel, notifying the other peer.
</description>
</method>
<method name="get_id" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the id assigned to this channel during creation (or auto-assigned during negotiation).
+ If the channel is not negotiated out-of-band the id will only be available after the connection is established (will return [code]65535[/code] until then).
</description>
</method>
<method name="get_label" qualifiers="const">
<return type="String">
</return>
<description>
+ Returns the label assigned to this channel during creation.
</description>
</method>
<method name="get_max_packet_life_time" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the [code]maxPacketLifeTime[/code] value assigned to this channel during creation.
+ Will be [code]65535[/code] if not specified.
</description>
</method>
<method name="get_max_retransmits" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the [code]maxRetransmits[/code] value assigned to this channel during creation.
+ Will be [code]65535[/code] if not specified.
</description>
</method>
<method name="get_protocol" qualifiers="const">
<return type="String">
</return>
<description>
+ Returns the sub-protocol assigned to this channel during creation. An empty string if not specified.
</description>
</method>
<method name="get_ready_state" qualifiers="const">
<return type="int" enum="WebRTCDataChannel.ChannelState">
</return>
<description>
+ Returns the current state of this channel, see [enum WebRTCDataChannel.ChannelState].
</description>
</method>
<method name="is_negotiated" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if this channel was created with out-of-band configuration.
</description>
</method>
<method name="is_ordered" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if this channel was created with ordering enabled (default).
</description>
</method>
<method name="poll">
<return type="int" enum="Error">
</return>
<description>
+ Reserved, but not used for now.
</description>
</method>
<method name="was_string_packet" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the last received packet was transferred as text. See [member write_mode].
</description>
</method>
</methods>
<members>
<member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCDataChannel.WriteMode">
+ The transfer mode to use when sending outgoing packet. Either text or binary.
</member>
</members>
<constants>
<constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
+ Tells the channel to send data over this channel as text. An external peer (non-Godot) would receive this as a string.
</constant>
<constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
+ Tells the channel to send data over this channel as binary. An external peer (non-Godot) would receive this as array buffer or blob.
</constant>
<constant name="STATE_CONNECTING" value="0" enum="ChannelState">
+ The channel was created, but it's still trying to connect.
</constant>
<constant name="STATE_OPEN" value="1" enum="ChannelState">
+ The channel is currently open, and data can flow over it.
</constant>
<constant name="STATE_CLOSING" value="2" enum="ChannelState">
+ The channel is being closed, no new messages will be accepted, but those already in queue will be flushed.
</constant>
<constant name="STATE_CLOSED" value="3" enum="ChannelState">
+ The channel was closed, or connection failed.
</constant>
</constants>
</class>
diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml
new file mode 100644
index 0000000000..2b0622fffa
--- /dev/null
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebRTCMultiplayer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.2">
+ <brief_description>
+ A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI].
+ </brief_description>
+ <description>
+ This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.network_peer].
+ You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections.
+ [signal NetworkedMultiplayerPeer.connection_succeeded] and [signal NetworkedMultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [NetworkedMultiplayerPeer].
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_peer">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="peer" type="WebRTCPeerConnection">
+ </argument>
+ <argument index="1" name="peer_id" type="int">
+ </argument>
+ <argument index="2" name="unreliable_lifetime" type="int" default="1">
+ </argument>
+ <description>
+ Add a new peer to the mesh with the given [code]peer_id[/code]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW].
+ Three channels will be created for reliable, unreliable, and ordered transport. The value of [code]unreliable_lifetime[/code] will be passed to the [code]maxPacketLifetime[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]).
+ </description>
+ </method>
+ <method name="close">
+ <return type="void">
+ </return>
+ <description>
+ Close all the add peer connections and channels, freeing all resources.
+ </description>
+ </method>
+ <method name="get_peer">
+ <return type="Dictionary">
+ </return>
+ <argument index="0" name="peer_id" type="int">
+ </argument>
+ <description>
+ Return a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open).
+ </description>
+ </method>
+ <method name="get_peers">
+ <return type="Dictionary">
+ </return>
+ <description>
+ Returns a dictionary which keys are the peer ids and values the peer representation as in [method get_peer]
+ </description>
+ </method>
+ <method name="has_peer">
+ <return type="bool">
+ </return>
+ <argument index="0" name="peer_id" type="int">
+ </argument>
+ <description>
+ Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though).
+ </description>
+ </method>
+ <method name="initialize">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="peer_id" type="int">
+ </argument>
+ <argument index="1" name="server_compatibility" type="bool" default="false">
+ </argument>
+ <description>
+ Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647).
+ If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant NetworkedMultiplayerPeer.CONNECTION_CONNECTED] and [signal NetworkedMultiplayerPeer.connection_succeeded] will not be emitted.
+ If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal NetworkedMultiplayerPeer.peer_connected] signals until a peer with id [constant NetworkedMultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal NetworkedMultiplayerPeer.connection_succeeded]. After that the signal [signal NetworkedMultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal NetworkedMultiplayerPeer.server_disconnected] will be emitted and state will become [constant NetworkedMultiplayerPeer.CONNECTION_CONNECTED].
+ </description>
+ </method>
+ <method name="remove_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="peer_id" type="int">
+ </argument>
+ <description>
+ Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal NetworkedMultiplayerPeer.peer_connected] was emitted for it, then [signal NetworkedMultiplayerPeer.peer_disconnected] will be emitted.
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 8b14c60deb..ae709877f4 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -1,8 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebRTCPeerConnection" inherits="Reference" category="Core" version="3.2">
<brief_description>
+ Interface to a WebRTC peer connection.
</brief_description>
<description>
+ A WebRTC connection between the local computer and a remote peer. Provides an interface to connect, maintain and monitor the connection.
+ Setting up a WebRTC connection between two peers from now on) may not seem a trivial task, but it can be broken down into 3 main steps:
+ - The peer that wants to initiate the connection ([code]A[/code] from now on) creates an offer and send it to the other peer ([code]B[/code] from now on).
+ - [code]B[/code] receives the offer, generate and answer, and sends it to [code]B[/code]).
+ - [code]A[/code] and [code]B[/code] then generates and exchange ICE candidates with each other.
+ After these steps, the connection should become connected. Keep on reading or look into the tutorial for more information.
</description>
<tutorials>
</tutorials>
@@ -17,12 +24,14 @@
<argument index="2" name="name" type="String">
</argument>
<description>
+ Add an ice candidate generated by a remote peer (and received over the signaling server). See [signal ice_candidate_created].
</description>
</method>
<method name="close">
<return type="void">
</return>
<description>
+ Close the peer connection and all data channels associated with it. Note, you cannot reuse this object for a new connection unless you call [method initialize].
</description>
</method>
<method name="create_data_channel">
@@ -35,18 +44,38 @@
}">
</argument>
<description>
+ Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW].
+ There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code].
+ Valid [code]options[/code] are:
+ [codeblock]
+ {
+ "negotiated": true, # When set to true (default off), means the channel is negotiated out of band. "id" must be set too. data_channel_received will not be called.
+ "id": 1, # When "negotiated" is true this value must also be set to the same value on both peer.
+
+ # Only one of maxRetransmits and maxPacketLifeTime can be specified, not both. They make the channel unreliable (but also better at real time).
+ "maxRetransmits": 1, # Specify the maximum number of attempt the peer will make to retransmits packets if they are not acknowledged.
+ "maxPacketLifeTime": 100, # Specify the maximum amount of time before giving up retransmitions of unacknowledged packets (in milliseconds).
+ "ordered": true, # When in unreliable mode (i.e. either "maxRetransmits" or "maxPacketLifetime" is set), "ordered" (true by default) specify if packet ordering is to be enforced.
+
+ "protocol": "my-custom-protocol", # A custom sub-protocol string for this channel.
+ }
+ [/codeblock]
+ NOTE: You must keep a reference to channels created this way, or it will be closed.
</description>
</method>
<method name="create_offer">
<return type="int" enum="Error">
</return>
<description>
+ Creates a new SDP offer to start a WebRTC connection with a remote peer. At least one [WebRTCDataChannel] must have been created before calling this method.
+ If this functions returns [code]OK[/code], [signal session_description_created] will be called when the session is ready to be sent.
</description>
</method>
<method name="get_connection_state" qualifiers="const">
<return type="int" enum="WebRTCPeerConnection.ConnectionState">
</return>
<description>
+ Returns the connection state. See [enum ConnectionState].
</description>
</method>
<method name="initialize">
@@ -57,12 +86,29 @@
}">
</argument>
<description>
+ Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection.
+ Valid [code]options[/code] are:
+ [codeblock]
+ {
+ "iceServers": [
+ {
+ "urls": [ "stun:stun.example.com:3478" ], # One or more STUN servers.
+ },
+ {
+ "urls": [ "turn:turn.example.com:3478" ], # One or more TURN servers.
+ "username": "a_username", # Optional username for the TURN server.
+ "credentials": "a_password", # Optional password for the TURN server.
+ }
+ ]
+ }
+ [/codeblock]
</description>
</method>
<method name="poll">
<return type="int" enum="Error">
</return>
<description>
+ Call this method frequently (e.g. in [method Node._process] or [method Node._physics_process]) to properly receive signals.
</description>
</method>
<method name="set_local_description">
@@ -73,6 +119,8 @@
<argument index="1" name="sdp" type="String">
</argument>
<description>
+ Sets the SDP description of the local peer. This should be called in response to [signal session_description_created].
+ If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created].
</description>
</method>
<method name="set_remote_description">
@@ -83,6 +131,9 @@
<argument index="1" name="sdp" type="String">
</argument>
<description>
+ Sets the SDP description of the remote peer. This should be called with the values generated by a remote peer and received over the signaling server.
+ If [code]type[/code] is [code]offer[/code] the peer will emit [signal session_description_created] with the appropriate answer.
+ If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created].
</description>
</method>
</methods>
@@ -91,6 +142,8 @@
<argument index="0" name="channel" type="Object">
</argument>
<description>
+ Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default).
+ The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel]
</description>
</signal>
<signal name="ice_candidate_created">
@@ -101,6 +154,7 @@
<argument index="2" name="name" type="String">
</argument>
<description>
+ Emitted when a new ICE candidate has been created. The three parameters are meant to be passed to the remote peer over the signaling server.
</description>
</signal>
<signal name="session_description_created">
@@ -109,21 +163,28 @@
<argument index="1" name="sdp" type="String">
</argument>
<description>
+ Emitted after a successful call to [method create_offer] or [method set_remote_description] (when it generates an answer). The parameters are meant to be passed to [method set_local_description] on this object, and sent to the remote peer over the signaling server.
</description>
</signal>
</signals>
<constants>
<constant name="STATE_NEW" value="0" enum="ConnectionState">
+ The connection is new, data channels and an offer can be created in this state.
</constant>
<constant name="STATE_CONNECTING" value="1" enum="ConnectionState">
+ The peer is connecting, ICE is in progress, non of the transports has failed.
</constant>
<constant name="STATE_CONNECTED" value="2" enum="ConnectionState">
+ The peer is connected, all ICE transports are connected.
</constant>
<constant name="STATE_DISCONNECTED" value="3" enum="ConnectionState">
+ At least one ICE transport is disconnected.
</constant>
<constant name="STATE_FAILED" value="4" enum="ConnectionState">
+ One or more of the ICE transports failed.
</constant>
<constant name="STATE_CLOSED" value="5" enum="ConnectionState">
+ The peer connection is closed (after calling [method close] for example).
</constant>
</constants>
</class>
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 44e072cc89..58b68d926b 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -40,6 +40,7 @@
#include "webrtc_data_channel_gdnative.h"
#include "webrtc_peer_connection_gdnative.h"
#endif
+#include "webrtc_multiplayer.h"
void register_webrtc_types() {
#ifdef JAVASCRIPT_ENABLED
@@ -54,6 +55,7 @@ void register_webrtc_types() {
ClassDB::register_class<WebRTCDataChannelGDNative>();
#endif
ClassDB::register_virtual_class<WebRTCDataChannel>();
+ ClassDB::register_class<WebRTCMultiplayer>();
}
void unregister_webrtc_types() {}
diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp
index 2e7c64aa72..069918cc9c 100644
--- a/modules/webrtc/webrtc_data_channel_js.cpp
+++ b/modules/webrtc/webrtc_data_channel_js.cpp
@@ -68,7 +68,7 @@ void WebRTCDataChannelJS::_on_error() {
}
void WebRTCDataChannelJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
- if (in_buffer.space_left() < p_size + 5) {
+ if (in_buffer.space_left() < (int)(p_size + 5)) {
ERR_EXPLAIN("Buffer full! Dropping data");
ERR_FAIL();
}
@@ -205,30 +205,45 @@ String WebRTCDataChannelJS::get_label() const {
}
/* clang-format off */
-#define _JS_GET(PROP) \
+#define _JS_GET(PROP, DEF) \
EM_ASM_INT({ \
var dict = Module.IDHandler.get($0); \
if (!dict || !dict["channel"]) { \
- return 0; \
- }; \
- return dict["channel"].PROP; \
+ return DEF; \
+ } \
+ var out = dict["channel"].PROP; \
+ return out === null ? DEF : out; \
}, _js_id)
/* clang-format on */
bool WebRTCDataChannelJS::is_ordered() const {
- return _JS_GET(ordered);
+ return _JS_GET(ordered, true);
}
int WebRTCDataChannelJS::get_id() const {
- return _JS_GET(id);
+ return _JS_GET(id, 65535);
}
int WebRTCDataChannelJS::get_max_packet_life_time() const {
- return _JS_GET(maxPacketLifeTime);
+ // Can't use macro, webkit workaround.
+ /* clang-format off */
+ return EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict || !dict["channel"]) {
+ return 65535;
+ }
+ if (dict["channel"].maxRetransmitTime !== undefined) {
+ // Guess someone didn't appreciate the standardization process.
+ return dict["channel"].maxRetransmitTime;
+ }
+ var out = dict["channel"].maxPacketLifeTime;
+ return out === null ? 65535 : out;
+ }, _js_id);
+ /* clang-format on */
}
int WebRTCDataChannelJS::get_max_retransmits() const {
- return _JS_GET(maxRetransmits);
+ return _JS_GET(maxRetransmits, 65535);
}
String WebRTCDataChannelJS::get_protocol() const {
@@ -236,7 +251,7 @@ String WebRTCDataChannelJS::get_protocol() const {
}
bool WebRTCDataChannelJS::is_negotiated() const {
- return _JS_GET(negotiated);
+ return _JS_GET(negotiated, false);
}
WebRTCDataChannelJS::WebRTCDataChannelJS() {
diff --git a/modules/webrtc/webrtc_multiplayer.cpp b/modules/webrtc/webrtc_multiplayer.cpp
new file mode 100644
index 0000000000..17dafff93a
--- /dev/null
+++ b/modules/webrtc/webrtc_multiplayer.cpp
@@ -0,0 +1,384 @@
+/*************************************************************************/
+/* webrtc_multiplayer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 "webrtc_multiplayer.h"
+
+#include "core/io/marshalls.h"
+#include "core/os/os.h"
+
+void WebRTCMultiplayer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility"), &WebRTCMultiplayer::initialize, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayer::add_peer, DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayer::remove_peer);
+ ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayer::has_peer);
+ ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayer::get_peer);
+ ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayer::get_peers);
+ ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayer::close);
+}
+
+void WebRTCMultiplayer::set_transfer_mode(TransferMode p_mode) {
+ transfer_mode = p_mode;
+}
+
+NetworkedMultiplayerPeer::TransferMode WebRTCMultiplayer::get_transfer_mode() const {
+ return transfer_mode;
+}
+
+void WebRTCMultiplayer::set_target_peer(int p_peer_id) {
+ target_peer = p_peer_id;
+}
+
+/* Returns the ID of the NetworkedMultiplayerPeer who sent the most recent packet: */
+int WebRTCMultiplayer::get_packet_peer() const {
+ return next_packet_peer;
+}
+
+bool WebRTCMultiplayer::is_server() const {
+ return unique_id == TARGET_PEER_SERVER;
+}
+
+void WebRTCMultiplayer::poll() {
+ if (peer_map.size() == 0)
+ return;
+
+ List<int> remove;
+ List<int> add;
+ for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) {
+ Ref<ConnectedPeer> peer = E->get();
+ peer->connection->poll();
+ // Check peer state
+ switch (peer->connection->get_connection_state()) {
+ case WebRTCPeerConnection::STATE_NEW:
+ case WebRTCPeerConnection::STATE_CONNECTING:
+ // Go to next peer, not ready yet.
+ continue;
+ case WebRTCPeerConnection::STATE_CONNECTED:
+ // Good to go, go ahead and check channel state.
+ break;
+ default:
+ // Peer is closed or in error state. Got to next peer.
+ remove.push_back(E->key());
+ continue;
+ }
+ // Check channels state
+ int ready = 0;
+ for (List<Ref<WebRTCDataChannel> >::Element *C = peer->channels.front(); C && C->get().is_valid(); C = C->next()) {
+ Ref<WebRTCDataChannel> ch = C->get();
+ switch (ch->get_ready_state()) {
+ case WebRTCDataChannel::STATE_CONNECTING:
+ continue;
+ case WebRTCDataChannel::STATE_OPEN:
+ ready++;
+ continue;
+ default:
+ // Channel was closed or in error state, remove peer id.
+ remove.push_back(E->key());
+ }
+ // We got a closed channel break out, the peer will be removed.
+ break;
+ }
+ // This peer has newly connected, and all channels are now open.
+ if (ready == peer->channels.size() && !peer->connected) {
+ peer->connected = true;
+ add.push_back(E->key());
+ }
+ }
+ // Remove disconnected peers
+ for (List<int>::Element *E = remove.front(); E; E = E->next()) {
+ remove_peer(E->get());
+ if (next_packet_peer == E->get())
+ next_packet_peer = 0;
+ }
+ // Signal newly connected peers
+ for (List<int>::Element *E = add.front(); E; E = E->next()) {
+ // Already connected to server: simply notify new peer.
+ // NOTE: Mesh is always connected.
+ if (connection_status == CONNECTION_CONNECTED)
+ emit_signal("peer_connected", E->get());
+
+ // Server emulation mode suppresses peer_conencted until server connects.
+ if (server_compat && E->get() == TARGET_PEER_SERVER) {
+ // Server connected.
+ connection_status = CONNECTION_CONNECTED;
+ emit_signal("peer_connected", TARGET_PEER_SERVER);
+ emit_signal("connection_succeeded");
+ // Notify of all previously connected peers
+ for (Map<int, Ref<ConnectedPeer> >::Element *F = peer_map.front(); F; F = F->next()) {
+ if (F->key() != 1 && F->get()->connected)
+ emit_signal("peer_connected", F->key());
+ }
+ break; // Because we already notified of all newly added peers.
+ }
+ }
+ // Fetch next packet
+ if (next_packet_peer == 0)
+ _find_next_peer();
+}
+
+void WebRTCMultiplayer::_find_next_peer() {
+ Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.find(next_packet_peer);
+ if (E) E = E->next();
+ // After last.
+ while (E) {
+ for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) {
+ if (F->get()->get_available_packet_count()) {
+ next_packet_peer = E->key();
+ return;
+ }
+ }
+ E = E->next();
+ }
+ E = peer_map.front();
+ // Before last
+ while (E) {
+ for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) {
+ if (F->get()->get_available_packet_count()) {
+ next_packet_peer = E->key();
+ return;
+ }
+ }
+ if (E->key() == (int)next_packet_peer)
+ break;
+ E = E->next();
+ }
+ // No packet found
+ next_packet_peer = 0;
+}
+
+void WebRTCMultiplayer::set_refuse_new_connections(bool p_enable) {
+ refuse_connections = p_enable;
+}
+
+bool WebRTCMultiplayer::is_refusing_new_connections() const {
+ return refuse_connections;
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus WebRTCMultiplayer::get_connection_status() const {
+ return connection_status;
+}
+
+Error WebRTCMultiplayer::initialize(int p_self_id, bool p_server_compat) {
+ ERR_FAIL_COND_V(p_self_id < 0 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
+ unique_id = p_self_id;
+ server_compat = p_server_compat;
+
+ // Mesh and server are always connected
+ if (!server_compat || p_self_id == 1)
+ connection_status = CONNECTION_CONNECTED;
+ else
+ connection_status = CONNECTION_CONNECTING;
+ return OK;
+}
+
+int WebRTCMultiplayer::get_unique_id() const {
+ ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1);
+ return unique_id;
+}
+
+void WebRTCMultiplayer::_peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict) {
+ Array channels;
+ for (List<Ref<WebRTCDataChannel> >::Element *F = p_connected_peer->channels.front(); F; F = F->next()) {
+ channels.push_back(F->get());
+ }
+ r_dict["connection"] = p_connected_peer->connection;
+ r_dict["connected"] = p_connected_peer->connected;
+ r_dict["channels"] = channels;
+}
+
+bool WebRTCMultiplayer::has_peer(int p_peer_id) {
+ return peer_map.has(p_peer_id);
+}
+
+Dictionary WebRTCMultiplayer::get_peer(int p_peer_id) {
+ ERR_FAIL_COND_V(!peer_map.has(p_peer_id), Dictionary());
+ Dictionary out;
+ _peer_to_dict(peer_map[p_peer_id], out);
+ return out;
+}
+
+Dictionary WebRTCMultiplayer::get_peers() {
+ Dictionary out;
+ for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) {
+ Dictionary d;
+ _peer_to_dict(E->get(), d);
+ out[E->key()] = d;
+ }
+ return out;
+}
+
+Error WebRTCMultiplayer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) {
+ ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(refuse_connections, ERR_UNAUTHORIZED);
+ // Peer must be valid, and in new state (to create data channels)
+ ERR_FAIL_COND_V(!p_peer.is_valid(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_peer->get_connection_state() != WebRTCPeerConnection::STATE_NEW, ERR_INVALID_PARAMETER);
+
+ Ref<ConnectedPeer> peer = memnew(ConnectedPeer);
+ peer->connection = p_peer;
+
+ // Initialize data channels
+ Dictionary cfg;
+ cfg["negotiated"] = true;
+ cfg["ordered"] = true;
+
+ cfg["id"] = 1;
+ peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg);
+ ERR_FAIL_COND_V(!peer->channels[CH_RELIABLE].is_valid(), FAILED);
+
+ cfg["id"] = 2;
+ cfg["maxPacketLifetime"] = p_unreliable_lifetime;
+ peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg);
+ ERR_FAIL_COND_V(!peer->channels[CH_ORDERED].is_valid(), FAILED);
+
+ cfg["id"] = 3;
+ cfg["ordered"] = false;
+ peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg);
+ ERR_FAIL_COND_V(!peer->channels[CH_UNRELIABLE].is_valid(), FAILED);
+
+ peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map
+
+ return OK;
+}
+
+void WebRTCMultiplayer::remove_peer(int p_peer_id) {
+ ERR_FAIL_COND(!peer_map.has(p_peer_id));
+ Ref<ConnectedPeer> peer = peer_map[p_peer_id];
+ peer_map.erase(p_peer_id);
+ if (peer->connected) {
+ peer->connected = false;
+ emit_signal("peer_disconnected", p_peer_id);
+ if (server_compat && p_peer_id == TARGET_PEER_SERVER) {
+ emit_signal("server_disconnected");
+ connection_status = CONNECTION_DISCONNECTED;
+ }
+ }
+}
+
+Error WebRTCMultiplayer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ // Peer not available
+ if (next_packet_peer == 0 || !peer_map.has(next_packet_peer)) {
+ _find_next_peer();
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
+ for (List<Ref<WebRTCDataChannel> >::Element *E = peer_map[next_packet_peer]->channels.front(); E; E = E->next()) {
+ if (E->get()->get_available_packet_count()) {
+ Error err = E->get()->get_packet(r_buffer, r_buffer_size);
+ _find_next_peer();
+ return err;
+ }
+ }
+ // Channels for that peer were empty. Bug?
+ _find_next_peer();
+ ERR_FAIL_V(ERR_BUG);
+}
+
+Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED);
+
+ int ch = CH_RELIABLE;
+ switch (transfer_mode) {
+ case TRANSFER_MODE_RELIABLE:
+ ch = CH_RELIABLE;
+ break;
+ case TRANSFER_MODE_UNRELIABLE_ORDERED:
+ ch = CH_ORDERED;
+ break;
+ case TRANSFER_MODE_UNRELIABLE:
+ ch = CH_UNRELIABLE;
+ break;
+ }
+
+ Map<int, Ref<ConnectedPeer> >::Element *E = NULL;
+
+ if (target_peer > 0) {
+
+ E = peer_map.find(target_peer);
+ if (!E) {
+ ERR_EXPLAIN("Invalid Target Peer: " + itos(target_peer));
+ ERR_FAIL_V(ERR_INVALID_PARAMETER);
+ }
+ ERR_FAIL_COND_V(E->value()->channels.size() <= ch, ERR_BUG);
+ ERR_FAIL_COND_V(!E->value()->channels[ch].is_valid(), ERR_BUG);
+ return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
+
+ } else {
+ int exclude = -target_peer;
+
+ for (Map<int, Ref<ConnectedPeer> >::Element *F = peer_map.front(); F; F = F->next()) {
+
+ // Exclude packet. If target_peer == 0 then don't exclude any packets
+ if (target_peer != 0 && F->key() == exclude)
+ continue;
+
+ ERR_CONTINUE(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid());
+ F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
+ }
+ }
+ return OK;
+}
+
+int WebRTCMultiplayer::get_available_packet_count() const {
+ if (next_packet_peer == 0)
+ return 0; // To be sure next call to get_packet works if size > 0 .
+ int size = 0;
+ for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) {
+ for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) {
+ size += F->get()->get_available_packet_count();
+ }
+ }
+ return size;
+}
+
+int WebRTCMultiplayer::get_max_packet_size() const {
+ return 1200;
+}
+
+void WebRTCMultiplayer::close() {
+ peer_map.clear();
+ unique_id = 0;
+ next_packet_peer = 0;
+ target_peer = 0;
+ connection_status = CONNECTION_DISCONNECTED;
+}
+
+WebRTCMultiplayer::WebRTCMultiplayer() {
+ unique_id = 0;
+ next_packet_peer = 0;
+ target_peer = 0;
+ transfer_mode = TRANSFER_MODE_RELIABLE;
+ refuse_connections = false;
+ connection_status = CONNECTION_DISCONNECTED;
+ server_compat = false;
+}
+
+WebRTCMultiplayer::~WebRTCMultiplayer() {
+ close();
+}
diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h
new file mode 100644
index 0000000000..82bbfd4f68
--- /dev/null
+++ b/modules/webrtc/webrtc_multiplayer.h
@@ -0,0 +1,116 @@
+/*************************************************************************/
+/* webrtc_multiplayer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 WEBRTC_MULTIPLAYER_H
+#define WEBRTC_MULTIPLAYER_H
+
+#include "core/io/networked_multiplayer_peer.h"
+#include "webrtc_peer_connection.h"
+
+class WebRTCMultiplayer : public NetworkedMultiplayerPeer {
+
+ GDCLASS(WebRTCMultiplayer, NetworkedMultiplayerPeer);
+
+protected:
+ static void _bind_methods();
+
+private:
+ enum {
+ CH_RELIABLE = 0,
+ CH_ORDERED = 1,
+ CH_UNRELIABLE = 2,
+ CH_RESERVED_MAX = 3
+ };
+
+ class ConnectedPeer : public Reference {
+
+ public:
+ Ref<WebRTCPeerConnection> connection;
+ List<Ref<WebRTCDataChannel> > channels;
+ bool connected;
+
+ ConnectedPeer() {
+ connected = false;
+ for (int i = 0; i < CH_RESERVED_MAX; i++)
+ channels.push_front(Ref<WebRTCDataChannel>());
+ }
+ };
+
+ uint32_t unique_id;
+ int target_peer;
+ int client_count;
+ bool refuse_connections;
+ ConnectionStatus connection_status;
+ TransferMode transfer_mode;
+ int next_packet_peer;
+ bool server_compat;
+
+ Map<int, Ref<ConnectedPeer> > peer_map;
+
+ void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
+ void _find_next_peer();
+
+public:
+ WebRTCMultiplayer();
+ ~WebRTCMultiplayer();
+
+ Error initialize(int p_self_id, bool p_server_compat = false);
+ Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1);
+ void remove_peer(int p_peer_id);
+ bool has_peer(int p_peer_id);
+ Dictionary get_peer(int p_peer_id);
+ Dictionary get_peers();
+ void close();
+
+ // PacketPeer
+ Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet
+ Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ int get_available_packet_count() const;
+ int get_max_packet_size() const;
+
+ // NetworkedMultiplayerPeer
+ void set_transfer_mode(TransferMode p_mode);
+ TransferMode get_transfer_mode() const;
+ void set_target_peer(int p_peer_id);
+
+ int get_unique_id() const;
+ int get_packet_peer() const;
+
+ bool is_server() const;
+
+ void poll();
+
+ void set_refuse_new_connections(bool p_enable);
+ bool is_refusing_new_connections() const;
+
+ ConnectionStatus get_connection_status() const;
+};
+
+#endif
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index b987b3aebd..30267aa968 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -790,7 +790,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
if (tname == "manifest" && attrname == "versionName") {
if (attr_value == 0xFFFFFFFF) {
- WARN_PRINT("Version name in a resource, should be plaintext")
+ WARN_PRINT("Version name in a resource, should be plain text");
} else
string_table.write[attr_value] = version_name;
}
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index f8076dfd2d..ebc319e57d 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -176,6 +176,9 @@ Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int
input = memnew(InputDefault);
input->set_fallback_mapping("Default Android Gamepad");
+ ///@TODO implement a subclass for Android and instantiate that instead
+ camera_server = memnew(CameraServer);
+
//power_manager = memnew(PowerAndroid);
return OK;
@@ -193,6 +196,9 @@ void OS_Android::delete_main_loop() {
}
void OS_Android::finalize() {
+
+ memdelete(camera_server);
+
memdelete(input);
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index 4dbc96f4da..e74d4cfd43 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -39,6 +39,7 @@
#include "main/input_default.h"
//#include "power_android.h"
#include "servers/audio_server.h"
+#include "servers/camera_server.h"
#include "servers/visual/rasterizer.h"
class GodotJavaWrapper;
@@ -77,6 +78,8 @@ private:
VisualServer *visual_server;
+ CameraServer *camera_server;
+
mutable String data_dir_cache;
//AudioDriverAndroid audio_driver_android;
diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp
index 438b50053f..9c07535c85 100644
--- a/platform/haiku/os_haiku.cpp
+++ b/platform/haiku/os_haiku.cpp
@@ -133,6 +133,8 @@ Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p
window->Show();
visual_server->init();
+ camera_server = memnew(CameraServer);
+
AudioDriverManager::initialize(p_audio_driver);
return OK;
@@ -148,6 +150,8 @@ void OS_Haiku::finalize() {
visual_server->finish();
memdelete(visual_server);
+ memdelete(camera_server);
+
memdelete(input);
#if defined(OPENGL_ENABLED)
diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h
index e1d4cf8d87..70d78a1978 100644
--- a/platform/haiku/os_haiku.h
+++ b/platform/haiku/os_haiku.h
@@ -38,6 +38,7 @@
#include "haiku_direct_window.h"
#include "main/input_default.h"
#include "servers/audio_server.h"
+#include "servers/camera_server.h"
#include "servers/visual_server.h"
class OS_Haiku : public OS_Unix {
@@ -49,6 +50,7 @@ private:
VisualServer *visual_server;
VideoMode current_video_mode;
int video_driver_index;
+ CameraServer *camera_server;
#ifdef MEDIA_KIT_ENABLED
AudioDriverMediaKit driver_media_kit;
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index fa1b124561..85ba56165b 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -14,6 +14,7 @@ iphone_lib = [
'in_app_store.mm',
'icloud.mm',
'ios.mm',
+ 'camera_ios.mm',
]
env_ios = env.Clone()
diff --git a/platform/iphone/camera_ios.h b/platform/iphone/camera_ios.h
new file mode 100644
index 0000000000..e5d62af65d
--- /dev/null
+++ b/platform/iphone/camera_ios.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* camera_ios.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 CAMERAIOS_H
+#define CAMERAIOS_H
+
+///@TODO this is a near duplicate of CameraOSX, we should find a way to combine those to minimise code duplication!!!!
+// If you fix something here, make sure you fix it there as wel!
+
+#include "servers/camera_server.h"
+
+class CameraIOS : public CameraServer {
+public:
+ CameraIOS();
+ ~CameraIOS();
+
+ void update_feeds();
+};
+
+#endif /* CAMERAIOS_H */ \ No newline at end of file
diff --git a/platform/iphone/camera_ios.mm b/platform/iphone/camera_ios.mm
new file mode 100644
index 0000000000..3994e9366a
--- /dev/null
+++ b/platform/iphone/camera_ios.mm
@@ -0,0 +1,429 @@
+/*************************************************************************/
+/* camera_ios.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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. */
+/*************************************************************************/
+
+///@TODO this is a near duplicate of CameraOSX, we should find a way to combine those to minimise code duplication!!!!
+// If you fix something here, make sure you fix it there as wel!
+
+#include "camera_ios.h"
+#include "servers/camera/camera_feed.h"
+
+#import <AVFoundation/AVFoundation.h>
+#import <UIKit/UIKit.h>
+
+//////////////////////////////////////////////////////////////////////////
+// MyCaptureSession - This is a little helper class so we can capture our frames
+
+@interface MyCaptureSession : AVCaptureSession <AVCaptureVideoDataOutputSampleBufferDelegate> {
+ Ref<CameraFeed> feed;
+ size_t width[2];
+ size_t height[2];
+ PoolVector<uint8_t> img_data[2];
+
+ AVCaptureDeviceInput *input;
+ AVCaptureVideoDataOutput *output;
+}
+
+@end
+
+@implementation MyCaptureSession
+
+- (id)initForFeed:(Ref<CameraFeed>)p_feed andDevice:(AVCaptureDevice *)p_device {
+ if (self = [super init]) {
+ NSError *error;
+ feed = p_feed;
+ width[0] = 0;
+ height[0] = 0;
+ width[1] = 0;
+ height[1] = 0;
+
+ // prepare our device
+ [p_device lockForConfiguration:&error];
+
+ [p_device setFocusMode:AVCaptureFocusModeLocked];
+ [p_device setExposureMode:AVCaptureExposureModeLocked];
+ [p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked];
+
+ [p_device unlockForConfiguration];
+
+ [self beginConfiguration];
+
+ // setup our capture
+ self.sessionPreset = AVCaptureSessionPreset1280x720;
+
+ input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error];
+ if (!input) {
+ print_line("Couldn't get input device for camera");
+ } else {
+ [self addInput:input];
+ }
+
+ output = [AVCaptureVideoDataOutput new];
+ if (!output) {
+ print_line("Couldn't get output device for camera");
+ } else {
+ NSDictionary *settings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) };
+ output.videoSettings = settings;
+
+ // discard if the data output queue is blocked (as we process the still image)
+ [output setAlwaysDiscardsLateVideoFrames:YES];
+
+ // now set ourselves as the delegate to receive new frames. Note that we're doing this on the main thread at the moment, we may need to change this..
+ [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
+
+ [self addOutput:output];
+ }
+
+ [self commitConfiguration];
+
+ // kick off our session..
+ [self startRunning];
+ };
+ return self;
+}
+
+- (void)cleanup {
+ // stop running
+ [self stopRunning];
+
+ // cleanup
+ [self beginConfiguration];
+
+ if (input) {
+ [self removeInput:input];
+ // don't release this
+ input = nil;
+ }
+
+ if (output) {
+ [self removeOutput:output];
+ [output setSampleBufferDelegate:nil queue:NULL];
+ [output release];
+ output = nil;
+ }
+
+ [self commitConfiguration];
+}
+
+- (void)dealloc {
+ // bye bye
+ [super dealloc];
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
+ // This gets called every time our camera has a new image for us to process.
+ // May need to investigate in a way to throttle this if we get more images then we're rendering frames..
+
+ // For now, version 1, we're just doing the bare minimum to make this work...
+
+ CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+ // int width = CVPixelBufferGetWidth(pixelBuffer);
+ // int height = CVPixelBufferGetHeight(pixelBuffer);
+
+ // It says that we need to lock this on the documentation pages but it's not in the samples
+ // need to lock our base address so we can access our pixel buffers, better safe then sorry?
+ CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
+
+ // get our buffers
+ unsigned char *dataY = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
+ unsigned char *dataCbCr = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
+ if (dataY == NULL) {
+ print_line("Couldn't access Y pixel buffer data");
+ } else if (dataCbCr == NULL) {
+ print_line("Couldn't access CbCr pixel buffer data");
+ } else {
+ UIDeviceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
+ Ref<Image> img[2];
+
+ {
+ // do Y
+ int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
+ int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
+ int _bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
+
+ if ((width[0] != new_width) || (height[0] != new_height)) {
+ // printf("Camera Y plane %i, %i - %i\n", new_width, new_height, bytes_per_row);
+
+ width[0] = new_width;
+ height[0] = new_height;
+ img_data[0].resize(new_width * new_height);
+ }
+
+ PoolVector<uint8_t>::Write w = img_data[0].write();
+ memcpy(w.ptr(), dataY, new_width * new_height);
+
+ img[0].instance();
+ img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
+ }
+
+ {
+ // do CbCr
+ int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
+ int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
+ int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
+
+ if ((width[1] != new_width) || (height[1] != new_height)) {
+ // printf("Camera CbCr plane %i, %i - %i\n", new_width, new_height, bytes_per_row);
+
+ width[1] = new_width;
+ height[1] = new_height;
+ img_data[1].resize(2 * new_width * new_height);
+ }
+
+ PoolVector<uint8_t>::Write w = img_data[1].write();
+ memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height);
+
+ ///TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion
+ img[1].instance();
+ img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]);
+ }
+
+ // set our texture...
+ feed->set_YCbCr_imgs(img[0], img[1]);
+
+ // update our matrix to match the orientation, note, before changing anything
+ // here, be aware that the project orientation settings must match your xcode
+ // settings or this will go wrong!
+ Transform2D display_transform;
+ switch (orientation) {
+ case UIInterfaceOrientationPortrait: {
+ display_transform = Transform2D(0.0, -1.0, -1.0, 0.0, 1.0, 1.0);
+ } break;
+ case UIInterfaceOrientationLandscapeRight: {
+ display_transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
+ } break;
+ case UIInterfaceOrientationLandscapeLeft: {
+ display_transform = Transform2D(-1.0, 0.0, 0.0, 1.0, 1.0, 0.0);
+ } break;
+ default: {
+ display_transform = Transform2D(0.0, 1.0, 1.0, 0.0, 0.0, 0.0);
+ } break;
+ }
+
+ //TODO: this is correct for the camera on the back, I have a feeling this needs to be inversed for the camera on the front!
+ feed->set_transform(display_transform);
+ }
+
+ // and unlock
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
+}
+
+@end
+
+//////////////////////////////////////////////////////////////////////////
+// CameraFeedIOS - Subclass for camera feeds in iOS
+
+class CameraFeedIOS : public CameraFeed {
+private:
+ bool is_arkit; // if true this feed is updated through ARKit (should only have one and not yet implemented)
+ AVCaptureDevice *device;
+ MyCaptureSession *capture_session;
+
+public:
+ bool get_is_arkit() const;
+ AVCaptureDevice *get_device() const;
+
+ CameraFeedIOS();
+ ~CameraFeedIOS();
+
+ void set_device(AVCaptureDevice *p_device);
+
+ bool activate_feed();
+ void deactivate_feed();
+};
+
+bool CameraFeedIOS::get_is_arkit() const {
+ return is_arkit;
+};
+
+AVCaptureDevice *CameraFeedIOS::get_device() const {
+ return device;
+};
+
+CameraFeedIOS::CameraFeedIOS() {
+ capture_session = NULL;
+ device = NULL;
+ transform = Transform2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); /* should re-orientate this based on device orientation */
+};
+
+void CameraFeedIOS::set_device(AVCaptureDevice *p_device) {
+ device = p_device;
+ if (device == NULL) {
+ ///@TODO finish this!
+ is_arkit = true;
+ name = "ARKit";
+ position = CameraFeed::FEED_BACK;
+ } else {
+ is_arkit = false;
+ [device retain];
+
+ // get some info
+ NSString *device_name = p_device.localizedName;
+ name = device_name.UTF8String;
+ position = CameraFeed::FEED_UNSPECIFIED;
+ if ([p_device position] == AVCaptureDevicePositionBack) {
+ position = CameraFeed::FEED_BACK;
+ } else if ([p_device position] == AVCaptureDevicePositionFront) {
+ position = CameraFeed::FEED_FRONT;
+ };
+ };
+};
+
+CameraFeedIOS::~CameraFeedIOS() {
+ if (capture_session != NULL) {
+ [capture_session release];
+ capture_session = NULL;
+ };
+
+ if (device != NULL) {
+ [device release];
+ device = NULL;
+ };
+};
+
+bool CameraFeedIOS::activate_feed() {
+ if (is_arkit) {
+ ///@TODO to implement;
+ } else {
+ if (capture_session) {
+ // already recording!
+ } else {
+ // start camera capture
+ capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
+ };
+ };
+
+ return true;
+};
+
+void CameraFeedIOS::deactivate_feed() {
+ // end camera capture if we have one
+ if (capture_session) {
+ [capture_session cleanup];
+ [capture_session release];
+ capture_session = NULL;
+ };
+};
+
+//////////////////////////////////////////////////////////////////////////
+// MyDeviceNotifications - This is a little helper class gets notifications
+// when devices are connected/disconnected
+
+@interface MyDeviceNotifications : NSObject {
+ CameraIOS *camera_server;
+}
+
+@end
+
+@implementation MyDeviceNotifications
+
+- (void)devices_changed:(NSNotification *)notification {
+ camera_server->update_feeds();
+}
+
+- (id)initForServer:(CameraIOS *)p_server {
+ if (self = [super init]) {
+ camera_server = p_server;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasConnectedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasDisconnectedNotification object:nil];
+ };
+ return self;
+}
+
+- (void)dealloc {
+ // remove notifications
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil];
+
+ [super dealloc];
+}
+
+@end
+
+MyDeviceNotifications *device_notifications = nil;
+
+//////////////////////////////////////////////////////////////////////////
+// CameraIOS - Subclass for our camera server on iPhone
+
+void CameraIOS::update_feeds() {
+ // this way of doing things is deprecated but still works,
+ // rewrite to using AVCaptureDeviceDiscoverySession
+
+ AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeBuiltInTelephotoCamera, AVCaptureDeviceTypeBuiltInDualCamera, AVCaptureDeviceTypeBuiltInTrueDepthCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
+
+ // remove devices that are gone..
+ for (int i = feeds.size() - 1; i >= 0; i--) {
+ Ref<CameraFeedIOS> feed = (Ref<CameraFeedIOS>)feeds[i];
+
+ if (feed->get_is_arkit()) {
+ // ignore, this is our arkit entry
+ } else if (![session.devices containsObject:feed->get_device()]) {
+ // remove it from our array, this will also destroy it ;)
+ remove_feed(feed);
+ };
+ };
+
+ // add new devices..
+ for (AVCaptureDevice *device in session.devices) {
+ bool found = false;
+ for (int i = 0; i < feeds.size() && !found; i++) {
+ Ref<CameraFeedIOS> feed = (Ref<CameraFeedIOS>)feeds[i];
+ if (feed->get_device() == device) {
+ found = true;
+ };
+ };
+
+ if (!found) {
+ Ref<CameraFeedIOS> newfeed;
+ newfeed.instance();
+ newfeed->set_device(device);
+ add_feed(newfeed);
+ };
+ };
+};
+
+CameraIOS::CameraIOS() {
+ [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
+ completionHandler:^(BOOL granted) {
+ if (granted) {
+ // Find available cameras we have at this time
+ update_feeds();
+
+ // should only have one of these....
+ device_notifications = [[MyDeviceNotifications alloc] initForServer:this];
+ } else {
+ print_line("No access to cameras!");
+ }
+ }];
+};
+
+CameraIOS::~CameraIOS() {
+ [device_notifications release];
+};
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index d9f710e456..daf4f405fd 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -144,6 +144,7 @@ def configure(env):
'-framework', 'CoreAudio',
'-framework', 'CoreGraphics',
'-framework', 'CoreMedia',
+ '-framework', 'CoreVideo',
'-framework', 'CoreMotion',
'-framework', 'Foundation',
'-framework', 'GameController',
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index ba405ab7ae..7ca83320d0 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -914,7 +914,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
};
DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir);
- ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE)
+ ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE);
print_line("Unzipping...");
FileAccess *src_f = NULL;
diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm
index e32618e8f6..c60db3d661 100644
--- a/platform/iphone/icloud.mm
+++ b/platform/iphone/icloud.mm
@@ -138,7 +138,7 @@ Variant nsobject_to_variant(NSObject *object) {
//this is a type that icloud supports...but how did you submit it in the first place?
//I guess this is a type that *might* show up, if you were, say, trying to make your game
//compatible with existing cloud data written by another engine's version of your game
- WARN_PRINT("NSDate unsupported, returning null Variant")
+ WARN_PRINT("NSDate unsupported, returning null Variant");
return Variant();
} else if ([object isKindOfClass:[NSNull class]] or object == nil) {
return Variant();
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index 6a65cadf09..f5fce66059 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -167,6 +167,8 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p
input = memnew(InputDefault);
+ camera_server = memnew(CameraIOS);
+
#ifdef GAME_CENTER_ENABLED
game_center = memnew(GameCenter);
Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
@@ -361,6 +363,11 @@ void OSIPhone::finalize() {
if (main_loop) // should not happen?
memdelete(main_loop);
+ if (camera_server) {
+ memdelete(camera_server);
+ camera_server = NULL;
+ }
+
visual_server->finish();
memdelete(visual_server);
// memdelete(rasterizer);
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 017125209c..c16c29a858 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -37,6 +37,7 @@
#include "drivers/coreaudio/audio_driver_coreaudio.h"
#include "drivers/unix/os_unix.h"
+#include "camera_ios.h"
#include "game_center.h"
#include "icloud.h"
#include "in_app_store.h"
@@ -60,6 +61,8 @@ private:
AudioDriverCoreAudio audio_driver;
+ CameraServer *camera_server;
+
#ifdef GAME_CENTER_ENABLED
GameCenter *game_center;
#endif
diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp
index 11104007e2..163826f828 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -99,7 +99,7 @@ Error AudioDriverJavaScript::init() {
return FAILED;
}
- if (!internal_buffer || memarr_len(internal_buffer) != buffer_length * channel_count) {
+ if (!internal_buffer || (int)memarr_len(internal_buffer) != buffer_length * channel_count) {
if (internal_buffer)
memdelete_arr(internal_buffer);
internal_buffer = memnew_arr(float, buffer_length *channel_count);
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index c69e6f0cb8..d96ffc3a55 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -70,6 +70,20 @@ static bool is_canvas_focused() {
/* clang-format on */
}
+static Point2 correct_canvas_position(int x, int y) {
+ int canvas_width;
+ int canvas_height;
+ emscripten_get_canvas_element_size(NULL, &canvas_width, &canvas_height);
+
+ double element_width;
+ double element_height;
+ emscripten_get_element_css_size(NULL, &element_width, &element_height);
+
+ x = (int)(canvas_width / element_width * x);
+ y = (int)(canvas_height / element_height * y);
+ return Point2(x, y);
+}
+
static bool cursor_inside_canvas = true;
EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
@@ -285,7 +299,7 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM
Ref<InputEventMouseButton> ev;
ev.instance();
ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
- ev->set_position(Point2(p_event->canvasX, p_event->canvasY));
+ ev->set_position(correct_canvas_position(p_event->canvasX, p_event->canvasY));
ev->set_global_position(ev->get_position());
dom2godot_mod(p_event, ev);
switch (p_event->button) {
@@ -349,7 +363,7 @@ EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMous
OS_JavaScript *os = get_singleton();
int input_mask = os->input->get_mouse_button_mask();
- Point2 pos = Point2(p_event->canvasX, p_event->canvasY);
+ Point2 pos = correct_canvas_position(p_event->canvasX, p_event->canvasY);
// For motion outside the canvas, only read mouse movement if dragging
// started inside the canvas; imitating desktop app behaviour.
if (!cursor_inside_canvas && !input_mask)
@@ -666,7 +680,7 @@ EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTo
if (!touch.isChanged)
continue;
ev->set_index(touch.identifier);
- ev->set_position(Point2(touch.canvasX, touch.canvasY));
+ ev->set_position(correct_canvas_position(touch.canvasX, touch.canvasY));
os->touches[i] = ev->get_position();
ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
@@ -691,7 +705,7 @@ EM_BOOL OS_JavaScript::touchmove_callback(int p_event_type, const EmscriptenTouc
if (!touch.isChanged)
continue;
ev->set_index(touch.identifier);
- ev->set_position(Point2(touch.canvasX, touch.canvasY));
+ ev->set_position(correct_canvas_position(touch.canvasX, touch.canvasY));
Point2 &prev = os->touches[i];
ev->set_relative(ev->get_position() - prev);
prev = ev->get_position();
@@ -795,6 +809,47 @@ const char *OS_JavaScript::get_audio_driver_name(int p_driver) const {
return "JavaScript";
}
+// Clipboard
+extern "C" EMSCRIPTEN_KEEPALIVE void update_clipboard(const char *p_text) {
+ // Only call set_clipboard from OS (sets local clipboard)
+ OS::get_singleton()->OS::set_clipboard(p_text);
+}
+
+void OS_JavaScript::set_clipboard(const String &p_text) {
+ OS::set_clipboard(p_text);
+ /* clang-format off */
+ int err = EM_ASM_INT({
+ var text = UTF8ToString($0);
+ if (!navigator.clipboard || !navigator.clipboard.writeText)
+ return 1;
+ navigator.clipboard.writeText(text).catch(e => {
+ // Setting OS clipboard is only possible from an input callback.
+ console.error("Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:", e);
+ });
+ return 0;
+ }, p_text.utf8().get_data());
+ /* clang-format on */
+ ERR_EXPLAIN("Clipboard API is not supported.");
+ ERR_FAIL_COND(err);
+}
+
+String OS_JavaScript::get_clipboard() const {
+ /* clang-format off */
+ EM_ASM({
+ try {
+ navigator.clipboard.readText().then(function (result) {
+ ccall('update_clipboard', 'void', ['string'], [result]);
+ }).catch(function (e) {
+ // Fail graciously.
+ });
+ } catch (e) {
+ // Fail graciously.
+ }
+ });
+ /* clang-format on */
+ return this->OS::get_clipboard();
+}
+
// Lifecycle
int OS_JavaScript::get_current_video_driver() const {
return video_driver_index;
@@ -901,6 +956,8 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
VisualServer *visual_server = memnew(VisualServerRaster());
input = memnew(InputDefault);
+ camera_server = memnew(CameraServer);
+
EMSCRIPTEN_RESULT result;
#define EM_CHECK(ev) \
if (result != EMSCRIPTEN_RESULT_SUCCESS) \
@@ -939,6 +996,11 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
(['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, index) {
Module.canvas.addEventListener(event, send_notification.bind(null, notifications[index]));
});
+ // Clipboard
+ const update_clipboard = cwrap('update_clipboard', null, ['string']);
+ window.addEventListener('paste', function(evt) {
+ update_clipboard(evt.clipboardData.getData('text'));
+ }, true);
},
MainLoop::NOTIFICATION_WM_MOUSE_ENTER,
MainLoop::NOTIFICATION_WM_MOUSE_EXIT,
@@ -1030,6 +1092,7 @@ void OS_JavaScript::delete_main_loop() {
void OS_JavaScript::finalize() {
+ memdelete(camera_server);
memdelete(input);
}
@@ -1098,7 +1161,7 @@ void OS_JavaScript::set_icon(const Ref<Image> &p_icon) {
Ref<Image> icon = p_icon;
if (icon->is_compressed()) {
icon = icon->duplicate();
- ERR_FAIL_COND(icon->decompress() != OK)
+ ERR_FAIL_COND(icon->decompress() != OK);
}
if (icon->get_format() != Image::FORMAT_RGBA8) {
if (icon == p_icon)
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index a0c7c31f2d..9635465c0d 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -35,6 +35,7 @@
#include "drivers/unix/os_unix.h"
#include "main/input_default.h"
#include "servers/audio_server.h"
+#include "servers/camera_server.h"
#include "servers/visual/rasterizer.h"
#include <emscripten/html5.h>
@@ -65,6 +66,8 @@ class OS_JavaScript : public OS_Unix {
int64_t sync_wait_time;
int64_t last_sync_check_time;
+ CameraServer *camera_server;
+
static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
@@ -133,6 +136,9 @@ public:
virtual int get_audio_driver_count() const;
virtual const char *get_audio_driver_name(int p_driver) const;
+ virtual void set_clipboard(const String &p_text);
+ virtual String get_clipboard() const;
+
virtual MainLoop *get_main_loop() const;
void run_async();
bool main_loop_iterate();
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index e15b4339a7..9620863b96 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -13,6 +13,7 @@ files = [
'dir_access_osx.mm',
'joypad_osx.cpp',
'power_osx.cpp',
+ 'camera_osx.mm',
]
prog = env.add_program('#bin/godot', files)
diff --git a/platform/osx/camera_osx.h b/platform/osx/camera_osx.h
new file mode 100644
index 0000000000..ed7a1f0a73
--- /dev/null
+++ b/platform/osx/camera_osx.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* camera_osx.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 CAMERAOSX_H
+#define CAMERAOSX_H
+
+///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimise code duplication!!!!
+// If you fix something here, make sure you fix it there as wel!
+
+#include "servers/camera_server.h"
+
+class CameraOSX : public CameraServer {
+public:
+ CameraOSX();
+ ~CameraOSX();
+
+ void update_feeds();
+};
+
+#endif /* CAMERAOSX_H */ \ No newline at end of file
diff --git a/platform/osx/camera_osx.mm b/platform/osx/camera_osx.mm
new file mode 100644
index 0000000000..34486c58b1
--- /dev/null
+++ b/platform/osx/camera_osx.mm
@@ -0,0 +1,362 @@
+/*************************************************************************/
+/* camera_osx.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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. */
+/*************************************************************************/
+
+///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimise code duplication!!!!
+// If you fix something here, make sure you fix it there as wel!
+
+#include "camera_osx.h"
+#include "servers/camera/camera_feed.h"
+#import <AVFoundation/AVFoundation.h>
+
+//////////////////////////////////////////////////////////////////////////
+// MyCaptureSession - This is a little helper class so we can capture our frames
+
+@interface MyCaptureSession : AVCaptureSession <AVCaptureVideoDataOutputSampleBufferDelegate> {
+ Ref<CameraFeed> feed;
+ size_t width[2];
+ size_t height[2];
+ PoolVector<uint8_t> img_data[2];
+
+ AVCaptureDeviceInput *input;
+ AVCaptureVideoDataOutput *output;
+}
+
+@end
+
+@implementation MyCaptureSession
+
+- (id)initForFeed:(Ref<CameraFeed>)p_feed andDevice:(AVCaptureDevice *)p_device {
+ if (self = [super init]) {
+ NSError *error;
+ feed = p_feed;
+ width[0] = 0;
+ height[0] = 0;
+ width[1] = 0;
+ height[1] = 0;
+
+ [self beginConfiguration];
+
+ input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error];
+ if (!input) {
+ print_line("Couldn't get input device for camera");
+ } else {
+ [self addInput:input];
+ }
+
+ output = [AVCaptureVideoDataOutput new];
+ if (!output) {
+ print_line("Couldn't get output device for camera");
+ } else {
+ NSDictionary *settings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) };
+ output.videoSettings = settings;
+
+ // discard if the data output queue is blocked (as we process the still image)
+ [output setAlwaysDiscardsLateVideoFrames:YES];
+
+ // now set ourselves as the delegate to receive new frames.
+ [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
+
+ // this takes ownership
+ [self addOutput:output];
+ }
+
+ [self commitConfiguration];
+
+ // kick off our session..
+ [self startRunning];
+ };
+ return self;
+}
+
+- (void)cleanup {
+ // stop running
+ [self stopRunning];
+
+ // cleanup
+ [self beginConfiguration];
+
+ // remove input
+ if (input) {
+ [self removeInput:input];
+ // don't release this
+ input = NULL;
+ }
+
+ // free up our output
+ if (output) {
+ [self removeOutput:output];
+ [output setSampleBufferDelegate:nil queue:NULL];
+ [output release];
+ output = NULL;
+ }
+
+ [self commitConfiguration];
+}
+
+- (void)dealloc {
+ // bye bye
+ [super dealloc];
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
+ // This gets called every time our camera has a new image for us to process.
+ // May need to investigate in a way to throttle this if we get more images then we're rendering frames..
+
+ // For now, version 1, we're just doing the bare minimum to make this work...
+ CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+ // int _width = CVPixelBufferGetWidth(pixelBuffer);
+ // int _height = CVPixelBufferGetHeight(pixelBuffer);
+
+ // It says that we need to lock this on the documentation pages but it's not in the samples
+ // need to lock our base address so we can access our pixel buffers, better safe then sorry?
+ CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
+
+ // get our buffers
+ unsigned char *dataY = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
+ unsigned char *dataCbCr = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
+ if (dataY == NULL) {
+ print_line("Couldn't access Y pixel buffer data");
+ } else if (dataCbCr == NULL) {
+ print_line("Couldn't access CbCr pixel buffer data");
+ } else {
+ Ref<Image> img[2];
+
+ {
+ // do Y
+ int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
+ int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
+
+ if ((width[0] != new_width) || (height[0] != new_height)) {
+ width[0] = new_width;
+ height[0] = new_height;
+ img_data[0].resize(new_width * new_height);
+ }
+
+ PoolVector<uint8_t>::Write w = img_data[0].write();
+ memcpy(w.ptr(), dataY, new_width * new_height);
+
+ img[0].instance();
+ img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
+ }
+
+ {
+ // do CbCr
+ int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
+ int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
+
+ if ((width[1] != new_width) || (height[1] != new_height)) {
+ width[1] = new_width;
+ height[1] = new_height;
+ img_data[1].resize(2 * new_width * new_height);
+ }
+
+ PoolVector<uint8_t>::Write w = img_data[1].write();
+ memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height);
+
+ ///TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion
+ img[1].instance();
+ img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]);
+ }
+
+ // set our texture...
+ feed->set_YCbCr_imgs(img[0], img[1]);
+ }
+
+ // and unlock
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
+}
+
+@end
+
+//////////////////////////////////////////////////////////////////////////
+// CameraFeedOSX - Subclass for camera feeds in OSX
+
+class CameraFeedOSX : public CameraFeed {
+private:
+ AVCaptureDevice *device;
+ MyCaptureSession *capture_session;
+
+public:
+ AVCaptureDevice *get_device() const;
+
+ CameraFeedOSX();
+ ~CameraFeedOSX();
+
+ void set_device(AVCaptureDevice *p_device);
+
+ bool activate_feed();
+ void deactivate_feed();
+};
+
+AVCaptureDevice *CameraFeedOSX::get_device() const {
+ return device;
+};
+
+CameraFeedOSX::CameraFeedOSX() {
+ device = NULL;
+ capture_session = NULL;
+};
+
+void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
+ device = p_device;
+ [device retain];
+
+ // get some info
+ NSString *device_name = p_device.localizedName;
+ name = device_name.UTF8String;
+ position = CameraFeed::FEED_UNSPECIFIED;
+ if ([p_device position] == AVCaptureDevicePositionBack) {
+ position = CameraFeed::FEED_BACK;
+ } else if ([p_device position] == AVCaptureDevicePositionFront) {
+ position = CameraFeed::FEED_FRONT;
+ };
+};
+
+CameraFeedOSX::~CameraFeedOSX() {
+ if (capture_session != NULL) {
+ [capture_session release];
+ capture_session = NULL;
+ };
+
+ if (device != NULL) {
+ [device release];
+ device = NULL;
+ };
+};
+
+bool CameraFeedOSX::activate_feed() {
+ if (capture_session) {
+ // already recording!
+ } else {
+ // start camera capture
+ capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
+ };
+
+ return true;
+};
+
+void CameraFeedOSX::deactivate_feed() {
+ // end camera capture if we have one
+ if (capture_session) {
+ [capture_session cleanup];
+ [capture_session release];
+ capture_session = NULL;
+ };
+};
+
+//////////////////////////////////////////////////////////////////////////
+// MyDeviceNotifications - This is a little helper class gets notifications
+// when devices are connected/disconnected
+
+@interface MyDeviceNotifications : NSObject {
+ CameraOSX *camera_server;
+}
+
+@end
+
+@implementation MyDeviceNotifications
+
+- (void)devices_changed:(NSNotification *)notification {
+ camera_server->update_feeds();
+}
+
+- (id)initForServer:(CameraOSX *)p_server {
+ if (self = [super init]) {
+ camera_server = p_server;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasConnectedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasDisconnectedNotification object:nil];
+ };
+ return self;
+}
+
+- (void)dealloc {
+ // remove notifications
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil];
+
+ [super dealloc];
+}
+
+@end
+
+MyDeviceNotifications *device_notifications = nil;
+
+//////////////////////////////////////////////////////////////////////////
+// CameraOSX - Subclass for our camera server on OSX
+
+void CameraOSX::update_feeds() {
+ NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
+
+ // remove devices that are gone..
+ for (int i = feeds.size() - 1; i >= 0; i--) {
+ Ref<CameraFeedOSX> feed = (Ref<CameraFeedOSX>)feeds[i];
+
+ if (![devices containsObject:feed->get_device()]) {
+ // remove it from our array, this will also destroy it ;)
+ remove_feed(feed);
+ };
+ };
+
+ // add new devices..
+ for (AVCaptureDevice *device in devices) {
+ bool found = false;
+ for (int i = 0; i < feeds.size() && !found; i++) {
+ Ref<CameraFeedOSX> feed = (Ref<CameraFeedOSX>)feeds[i];
+ if (feed->get_device() == device) {
+ found = true;
+ };
+ };
+
+ if (!found) {
+ Ref<CameraFeedOSX> newfeed;
+ newfeed.instance();
+ newfeed->set_device(device);
+
+ // assume display camera so inverse
+ Transform2D transform = Transform2D(-1.0, 0.0, 0.0, -1.0, 1.0, 1.0);
+ newfeed->set_transform(transform);
+
+ add_feed(newfeed);
+ };
+ };
+};
+
+CameraOSX::CameraOSX() {
+ // Find available cameras we have at this time
+ update_feeds();
+
+ // should only have one of these....
+ device_notifications = [[MyDeviceNotifications alloc] initForServer:this];
+};
+
+CameraOSX::~CameraOSX() {
+ [device_notifications release];
+};
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 4c88f91d13..2175797dec 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -128,7 +128,7 @@ def configure(env):
env.Prepend(CPPPATH=['#platform/osx'])
env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED', '-DCOREMIDI_ENABLED'])
- env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'CoreVideo'])
+ env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'AVFoundation', '-framework', 'CoreMedia', '-framework', 'CoreVideo'])
env.Append(LIBS=['pthread'])
env.Append(CCFLAGS=['-mmacosx-version-min=10.9'])
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 212966af11..09a9c0be4d 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -31,6 +31,7 @@
#ifndef OS_OSX_H
#define OS_OSX_H
+#include "camera_osx.h"
#include "core/os/input.h"
#include "crash_handler_osx.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
@@ -60,6 +61,7 @@ public:
unsigned int osx_state;
bool pressed;
bool echo;
+ bool raw;
uint32_t scancode;
uint32_t unicode;
};
@@ -72,6 +74,8 @@ public:
//Rasterizer *rasterizer;
VisualServer *visual_server;
+ CameraServer *camera_server;
+
List<String> args;
MainLoop *main_loop;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 113c6636f0..3ee6537180 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -392,7 +392,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
@interface GodotContentView : NSView <NSTextInputClient> {
NSTrackingArea *trackingArea;
NSMutableAttributedString *markedText;
- bool imeMode;
+ bool imeInputEventInProgress;
}
- (void)cancelComposition;
- (BOOL)wantsUpdateLayer;
@@ -418,7 +418,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
- (id)init {
self = [super init];
trackingArea = nil;
- imeMode = false;
+ imeInputEventInProgress = false;
[self updateTrackingAreas];
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
markedText = [[NSMutableAttributedString alloc] init];
@@ -452,7 +452,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
[markedText initWithString:aString];
}
if (OS_OSX::singleton->im_active) {
- imeMode = true;
+ imeInputEventInProgress = true;
OS_OSX::singleton->im_text.parse_utf8([[markedText mutableString] UTF8String]);
OS_OSX::singleton->im_selection = Point2(selectedRange.location, selectedRange.length);
@@ -467,7 +467,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
}
- (void)unmarkText {
- imeMode = false;
+ imeInputEventInProgress = false;
[[markedText mutableString] setString:@""];
if (OS_OSX::singleton->im_active) {
OS_OSX::singleton->im_text = String();
@@ -540,6 +540,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
ke.osx_state = [event modifierFlags];
ke.pressed = true;
ke.echo = false;
+ ke.raw = false; // IME input event
ke.scancode = 0;
ke.unicode = codepoint;
@@ -1001,7 +1002,7 @@ static const _KeyCodeMap _keycodes[55] = {
{ '/', KEY_SLASH }
};
-static int remapKey(unsigned int key) {
+static int remapKey(unsigned int key, unsigned int state) {
if (isNumpadKey(key))
return translateKey(key);
@@ -1023,7 +1024,7 @@ static int remapKey(unsigned int key) {
OSStatus err = UCKeyTranslate(keyboardLayout,
key,
kUCKeyActionDisplay,
- 0,
+ (state >> 8) & 0xFF,
LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit,
&keysDown,
@@ -1045,29 +1046,52 @@ static int remapKey(unsigned int key) {
- (void)keyDown:(NSEvent *)event {
- //disable raw input in IME mode
- if (!imeMode) {
- OS_OSX::KeyEvent ke;
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
+ NSString *characters = [event characters];
+ NSUInteger length = [characters length];
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = [event isARepeat];
- ke.scancode = remapKey([event keyCode]);
- ke.unicode = 0;
+ if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
+ // Fallback unicode character handler used if IME is not active
+ for (NSUInteger i = 0; i < length; i++) {
+ OS_OSX::KeyEvent ke;
- push_to_key_event_buffer(ke);
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = [event isARepeat];
+ ke.scancode = remapKey([event keyCode], [event modifierFlags]);
+ ke.raw = true;
+ ke.unicode = [characters characterAtIndex:i];
+
+ push_to_key_event_buffer(ke);
+ }
+ } else {
+ OS_OSX::KeyEvent ke;
+
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = [event isARepeat];
+ ke.scancode = remapKey([event keyCode], [event modifierFlags]);
+ ke.raw = false;
+ ke.unicode = 0;
+
+ push_to_key_event_buffer(ke);
+ }
}
- if (OS_OSX::singleton->im_active == true)
+ // Pass events to IME handler
+ if (OS_OSX::singleton->im_active)
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
- (void)flagsChanged:(NSEvent *)event {
- if (!imeMode) {
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
OS_OSX::KeyEvent ke;
ke.echo = false;
+ ke.raw = true;
int key = [event keyCode];
int mod = [event modifierFlags];
@@ -1105,7 +1129,7 @@ static int remapKey(unsigned int key) {
}
ke.osx_state = mod;
- ke.scancode = remapKey(key);
+ ke.scancode = remapKey(key, mod);
ke.unicode = 0;
push_to_key_event_buffer(ke);
@@ -1114,17 +1138,37 @@ static int remapKey(unsigned int key) {
- (void)keyUp:(NSEvent *)event {
- if (!imeMode) {
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
+ NSString *characters = [event characters];
+ NSUInteger length = [characters length];
- OS_OSX::KeyEvent ke;
+ // Fallback unicode character handler used if IME is not active
+ if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
+ for (NSUInteger i = 0; i < length; i++) {
+ OS_OSX::KeyEvent ke;
- ke.osx_state = [event modifierFlags];
- ke.pressed = false;
- ke.echo = false;
- ke.scancode = remapKey([event keyCode]);
- ke.unicode = 0;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = false;
+ ke.echo = [event isARepeat];
+ ke.scancode = remapKey([event keyCode], [event modifierFlags]);
+ ke.raw = true;
+ ke.unicode = [characters characterAtIndex:i];
- push_to_key_event_buffer(ke);
+ push_to_key_event_buffer(ke);
+ }
+ } else {
+ OS_OSX::KeyEvent ke;
+
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = false;
+ ke.echo = [event isARepeat];
+ ke.scancode = remapKey([event keyCode], [event modifierFlags]);
+ ke.raw = true;
+ ke.unicode = 0;
+
+ push_to_key_event_buffer(ke);
+ }
}
}
@@ -1498,6 +1542,8 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
visual_server->init();
AudioDriverManager::initialize(p_audio_driver);
+ camera_server = memnew(CameraOSX);
+
input = memnew(InputDefault);
joypad_osx = memnew(JoypadOSX);
@@ -1507,7 +1553,7 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
restore_rect = Rect2(get_window_position(), get_window_size());
- if (p_desired.layered_splash) {
+ if (p_desired.layered) {
set_window_per_pixel_transparency_enabled(true);
}
return OK;
@@ -1529,6 +1575,11 @@ void OS_OSX::finalize() {
delete_main_loop();
+ if (camera_server) {
+ memdelete(camera_server);
+ camera_server = NULL;
+ }
+
memdelete(joypad_osx);
memdelete(input);
@@ -2612,30 +2663,44 @@ void OS_OSX::process_key_events() {
const KeyEvent &ke = key_event_buffer[i];
- if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) {
+ if (ke.raw) {
+ // Non IME input - no composite characters, pass events as is
k.instance();
get_key_modifier_state(ke.osx_state, k);
k->set_pressed(ke.pressed);
k->set_echo(ke.echo);
- k->set_scancode(0);
+ k->set_scancode(ke.scancode);
k->set_unicode(ke.unicode);
push_input(k);
- }
- if (ke.scancode != 0) {
- k.instance();
+ } else {
+ // IME input
+ if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) {
+ k.instance();
- get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_scancode(ke.scancode);
+ get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_scancode(0);
+ k->set_unicode(ke.unicode);
- if (i + 1 < key_event_pos && key_event_buffer[i + 1].scancode == 0) {
- k->set_unicode(key_event_buffer[i + 1].unicode);
+ push_input(k);
}
+ if (ke.scancode != 0) {
+ k.instance();
- push_input(k);
+ get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_scancode(ke.scancode);
+
+ if (i + 1 < key_event_pos && key_event_buffer[i + 1].scancode == 0) {
+ k->set_unicode(key_event_buffer[i + 1].unicode);
+ }
+
+ push_input(k);
+ }
}
}
diff --git a/platform/server/detect.py b/platform/server/detect.py
index 08c2eb6aaf..a5648d8d9d 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -145,12 +145,12 @@ def configure(env):
env.ParseConfig('pkg-config libpng --cflags --libs')
if not env['builtin_bullet']:
- # We need at least version 2.88
+ # We need at least version 2.89
import subprocess
bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip()
- if str(bullet_version) < "2.88":
+ if str(bullet_version) < "2.89":
# Abort as system bullet was requested but too old
- print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88"))
+ print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.89"))
sys.exit(255)
env.ParseConfig('pkg-config bullet --cflags --libs')
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index cdcad33f6d..ec43a4c26f 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -241,7 +241,6 @@ void AppxPackager::make_block_map() {
tmp_file->close();
memdelete(tmp_file);
- tmp_file = NULL;
}
String AppxPackager::content_type(String p_extension) {
@@ -291,7 +290,6 @@ void AppxPackager::make_content_types() {
tmp_file->close();
memdelete(tmp_file);
- tmp_file = NULL;
}
Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) {
@@ -606,7 +604,6 @@ void AppxPackager::finish() {
blockmap_file->close();
memdelete(blockmap_file);
- blockmap_file = NULL;
// Add content types
EditorNode::progress_task_step("export", "Setting content types...", 5);
@@ -622,7 +619,6 @@ void AppxPackager::finish() {
types_file->close();
memdelete(types_file);
- types_file = NULL;
// Pre-process central directory before signing
for (int i = 0; i < file_metadata.size(); i++) {
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index f9d22481dd..9d9be44ce5 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -302,6 +302,10 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
}
visual_server->init();
+
+ ///@TODO implement a subclass for UWP and instantiate that instead
+ camera_server = memnew(CameraServer);
+
input = memnew(InputDefault);
joypad = ref new JoypadUWP(input);
@@ -400,6 +404,8 @@ void OS_UWP::finalize() {
memdelete(input);
+ memdelete(camera_server);
+
joypad = nullptr;
}
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 1bb68bc75d..b7a7248f19 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -41,6 +41,7 @@
#include "main/input_default.h"
#include "power_uwp.h"
#include "servers/audio_server.h"
+#include "servers/camera_server.h"
#include "servers/visual/rasterizer.h"
#include "servers/visual_server.h"
@@ -95,6 +96,8 @@ private:
VisualServer *visual_server;
int pressrc;
+ CameraServer *camera_server;
+
ContextEGL_UWP *gl_context;
Windows::UI::Core::CoreWindow ^ window;
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 892d734734..8426ccbb89 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -8,6 +8,7 @@ import platform_windows_builders
common_win = [
"godot_windows.cpp",
+ "camera_win.cpp",
"context_gl_windows.cpp",
"crash_handler_windows.cpp",
"os_windows.cpp",
diff --git a/platform/windows/camera_win.cpp b/platform/windows/camera_win.cpp
new file mode 100644
index 0000000000..6eff2749f2
--- /dev/null
+++ b/platform/windows/camera_win.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* camera_win.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 "camera_win.h"
+
+///@TODO sorry guys, I got about 80% through implementing this using DirectShow only to find out Microsoft deprecated half the API and its replacement is as confusing as they could make it
+// Joey suggested looking into libuvc which offers a more direct route to webcams over USB and this is very promissing but it wouldn't compile on windows for me...
+// I've gutted the classes I implemented DirectShow in just to have a skeleton for someone to work on, mail me for more details or if you want a copy....
+
+//////////////////////////////////////////////////////////////////////////
+// CameraFeedWindows - Subclass for our camera feed on windows
+
+/// @TODO need to implement this
+
+class CameraFeedWindows : public CameraFeed {
+private:
+protected:
+public:
+ CameraFeedWindows();
+ virtual ~CameraFeedWindows();
+
+ bool activate_feed();
+ void deactivate_feed();
+};
+
+CameraFeedWindows::CameraFeedWindows(){
+ ///@TODO implement this, should store information about our available camera
+};
+
+CameraFeedWindows::~CameraFeedWindows() {
+ // make sure we stop recording if we are!
+ if (is_active()) {
+ deactivate_feed();
+ };
+
+ ///@TODO free up anything used by this
+};
+
+bool CameraFeedWindows::activate_feed() {
+ ///@TODO this should activate our camera and start the process of capturing frames
+
+ return true;
+};
+
+///@TODO we should probably have a callback method here that is being called by the camera API which provides frames and call back into the CameraServer to update our texture
+
+void CameraFeedWindows::deactivate_feed(){
+ ///@TODO this should deactivate our camera and stop the process of capturing frames
+};
+
+//////////////////////////////////////////////////////////////////////////
+// CameraWindows - Subclass for our camera server on windows
+
+void CameraWindows::add_active_cameras(){
+ ///@TODO scan through any active cameras and create CameraFeedWindows objects for them
+};
+
+CameraWindows::CameraWindows() {
+ // Find cameras active right now
+ add_active_cameras();
+
+ // need to add something that will react to devices being connected/removed...
+};
+
+CameraWindows::~CameraWindows(){
+
+};
diff --git a/platform/windows/camera_win.h b/platform/windows/camera_win.h
new file mode 100644
index 0000000000..61d22ac6b1
--- /dev/null
+++ b/platform/windows/camera_win.h
@@ -0,0 +1,46 @@
+/*************************************************************************/
+/* camera_win.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 CAMERAWIN_H
+#define CAMERAWIN_H
+
+#include "servers/camera/camera_feed.h"
+#include "servers/camera_server.h"
+
+class CameraWindows : public CameraServer {
+private:
+ void add_active_cameras();
+
+public:
+ CameraWindows();
+ ~CameraWindows();
+};
+
+#endif /* CAMERAWIN_H */
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp
index 141ab96370..4a72d07adc 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -138,8 +138,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
EditorExportPlatformPC::get_export_options(r_options);
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
diff --git a/platform/windows/godot_res.rc b/platform/windows/godot_res.rc
index f2dca10d55..1fa8957f15 100644
--- a/platform/windows/godot_res.rc
+++ b/platform/windows/godot_res.rc
@@ -21,7 +21,7 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Godot Engine"
- VALUE "FileDescription", VERSION_NAME " Editor"
+ VALUE "FileDescription", VERSION_NAME
VALUE "FileVersion", VERSION_NUMBER
VALUE "ProductName", VERSION_NAME
VALUE "Licence", "MIT"
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 5a399cdf90..432060f4fe 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -103,17 +103,17 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
PRAWINPUTDEVICELIST dev_list = NULL;
unsigned int dev_list_count = 0;
- if (GetRawInputDeviceList(NULL, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == -1) {
+ if (GetRawInputDeviceList(NULL, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
return false;
}
dev_list = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count);
if (!dev_list) return false;
- if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == -1) {
+ if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
free(dev_list);
return false;
}
- for (int i = 0; i < dev_list_count; i++) {
+ for (unsigned int i = 0; i < dev_list_count; i++) {
RID_DEVICE_INFO rdi;
char dev_name[128];
@@ -334,9 +334,9 @@ void JoypadWindows::process_joypads() {
if (joy.state.dwPacketNumber != joy.last_packet) {
int button_mask = XINPUT_GAMEPAD_DPAD_UP;
- for (int i = 0; i <= 16; i++) {
+ for (int j = 0; j <= 16; i++) {
- input->joy_button(joy.id, i, joy.state.Gamepad.wButtons & button_mask);
+ input->joy_button(joy.id, j, joy.state.Gamepad.wButtons & button_mask);
button_mask = button_mask * 2;
}
@@ -406,7 +406,7 @@ void JoypadWindows::process_joypads() {
// on mingw, these constants are not constants
int count = 6;
- int axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ };
+ unsigned int axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ };
int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz };
for (int j = 0; j < joy->joy_axis.size(); j++) {
@@ -426,7 +426,11 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
int dpad_val = 0;
- if (p_dpad == -1) {
+ // Should be -1 when centered, but according to docs:
+ // "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows:
+ // BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
+ // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
+ if (LOWORD(p_dpad) == 0xFFFF) {
dpad_val = InputDefault::HAT_MASK_CENTER;
}
if (p_dpad == 0) {
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 6df2ad4821..db56bf5c7e 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -94,6 +94,7 @@ static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPR
return TRUE;
}
+#ifdef DEBUG_ENABLED
static String format_error_message(DWORD id) {
LPWSTR messageBuffer = NULL;
@@ -106,6 +107,7 @@ static String format_error_message(DWORD id) {
return msg;
}
+#endif // DEBUG_ENABLED
extern HINSTANCE godot_hinstance;
@@ -555,6 +557,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
break;
}
}
+ FALLTHROUGH;
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
@@ -583,7 +586,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
case WM_MBUTTONDOWN: {
mb->set_pressed(true);
mb->set_button_index(3);
-
} break;
case WM_MBUTTONUP: {
mb->set_pressed(false);
@@ -598,19 +600,16 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
mb->set_button_index(2);
} break;
case WM_LBUTTONDBLCLK: {
-
mb->set_pressed(true);
mb->set_button_index(1);
mb->set_doubleclick(true);
} break;
case WM_RBUTTONDBLCLK: {
-
mb->set_pressed(true);
mb->set_button_index(2);
mb->set_doubleclick(true);
} break;
case WM_MBUTTONDBLCLK: {
-
mb->set_pressed(true);
mb->set_button_index(3);
mb->set_doubleclick(true);
@@ -1358,6 +1357,8 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
power_manager = memnew(PowerWindows);
+ camera_server = memnew(CameraWindows);
+
AudioDriverManager::initialize(p_audio_driver);
TRACKMOUSEEVENT tme;
@@ -1381,7 +1382,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
SetFocus(hWnd); // Sets Keyboard Focus To
}
- if (p_desired.layered_splash) {
+ if (p_desired.layered) {
set_window_per_pixel_transparency_enabled(true);
}
@@ -1517,6 +1518,7 @@ void OS_Windows::finalize() {
memdelete(joypad);
memdelete(input);
+ memdelete(camera_server);
touch_state.clear();
visual_server->finish();
@@ -1816,11 +1818,11 @@ void OS_Windows::set_window_size(const Size2 p_size) {
// Don't let the mouse leave the window when resizing to a smaller resolution
if (mouse_mode == MOUSE_MODE_CONFINED) {
- RECT rect;
- GetClientRect(hWnd, &rect);
- ClientToScreen(hWnd, (POINT *)&rect.left);
- ClientToScreen(hWnd, (POINT *)&rect.right);
- ClipCursor(&rect);
+ RECT crect;
+ GetClientRect(hWnd, &crect);
+ ClientToScreen(hWnd, (POINT *)&crect.left);
+ ClientToScreen(hWnd, (POINT *)&crect.right);
+ ClipCursor(&crect);
}
}
void OS_Windows::set_window_fullscreen(bool p_enabled) {
@@ -2193,6 +2195,8 @@ uint64_t OS_Windows::get_unix_time() const {
FILETIME fep;
SystemTimeToFileTime(&ep, &fep);
+ // FIXME: dereferencing type-punned pointer will break strict-aliasing rules (GCC warning)
+ // https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime#remarks
return (*(uint64_t *)&ft - *(uint64_t *)&fep) / 10000000;
};
@@ -2378,7 +2382,7 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap
}
// Finally, create the icon
- ICONINFO iconinfo = { 0 };
+ ICONINFO iconinfo;
iconinfo.fIcon = FALSE;
iconinfo.xHotspot = p_hotspot.x;
iconinfo.yHotspot = p_hotspot.y;
@@ -2531,9 +2535,9 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
if (p_blocking) {
- DWORD ret = WaitForSingleObject(pi.pi.hProcess, INFINITE);
+ DWORD ret2 = WaitForSingleObject(pi.pi.hProcess, INFINITE);
if (r_exitcode)
- *r_exitcode = ret;
+ *r_exitcode = ret2;
CloseHandle(pi.pi.hProcess);
CloseHandle(pi.pi.hThread);
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 0aacbcb9ff..b20d847baa 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -31,6 +31,7 @@
#ifndef OS_WINDOWS_H
#define OS_WINDOWS_H
+#include "camera_win.h"
#include "context_gl_windows.h"
#include "core/os/input.h"
#include "core/os/os.h"
@@ -108,6 +109,7 @@ class OS_Windows : public OS {
ContextGL_Windows *gl_context;
#endif
VisualServer *visual_server;
+ CameraWindows *camera_server;
int pressrc;
HDC hDC; // Private GDI Device Context
HINSTANCE hInstance; // Holds The Instance Of The Application
diff --git a/platform/windows/power_windows.cpp b/platform/windows/power_windows.cpp
index b96ae51132..0efd88c216 100644
--- a/platform/windows/power_windows.cpp
+++ b/platform/windows/power_windows.cpp
@@ -89,7 +89,7 @@ bool PowerWindows::GetPowerInfo_Windows() {
if (pct != 255) { /* 255 == unknown */
percent_left = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
}
- if (secs != 0xFFFFFFFF) { /* ((DWORD)-1) == unknown */
+ if (secs != (int)0xFFFFFFFF) { /* ((DWORD)-1) == unknown */
nsecs_left = secs;
}
}
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 7def419103..adbdafb07e 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -45,7 +45,7 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er
int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list);
if (len <= 0)
return;
- if (len >= BUFFER_SIZE)
+ if ((unsigned int)len >= BUFFER_SIZE)
len = BUFFER_SIZE; // Output is too big, will be truncated
buf[len] = 0;
@@ -154,4 +154,4 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file
WindowsTerminalLogger::~WindowsTerminalLogger() {}
-#endif \ No newline at end of file
+#endif
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index 933ee6b72e..a502308eee 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -219,12 +219,12 @@ def configure(env):
env.ParseConfig('pkg-config libpng --cflags --libs')
if not env['builtin_bullet']:
- # We need at least version 2.88
+ # We need at least version 2.89
import subprocess
bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip()
- if str(bullet_version) < "2.88":
+ if str(bullet_version) < "2.89":
# Abort as system bullet was requested but too old
- print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88"))
+ print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.89"))
sys.exit(255)
env.ParseConfig('pkg-config bullet --cflags --libs')
diff --git a/platform/x11/joypad_linux.cpp b/platform/x11/joypad_linux.cpp
index c4dd8fe0e0..3e9e8033e8 100644
--- a/platform/x11/joypad_linux.cpp
+++ b/platform/x11/joypad_linux.cpp
@@ -444,10 +444,10 @@ InputDefault::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int
jx.min = -1;
if (p_value < 0) {
jx.value = (float)-p_value / min;
+ } else {
+ jx.value = (float)p_value / max;
}
- jx.value = (float)p_value / max;
- }
- if (min == 0) {
+ } else if (min == 0) {
jx.min = 0;
jx.value = 0.0f + (float)p_value / max;
}
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index f034b2389b..146cd8bb66 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -583,6 +583,9 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
AudioDriverManager::initialize(p_audio_driver);
+ ///@TODO implement a subclass for Linux and instantiate that instead
+ camera_server = memnew(CameraServer);
+
input = memnew(InputDefault);
window_has_focus = true; // Set focus to true at init
@@ -593,7 +596,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
power_manager = memnew(PowerX11);
- if (p_desired.layered_splash) {
+ if (p_desired.layered) {
set_window_per_pixel_transparency_enabled(true);
}
@@ -783,6 +786,8 @@ void OS_X11::finalize() {
memdelete(input);
+ memdelete(camera_server);
+
visual_server->finish();
memdelete(visual_server);
//memdelete(rasterizer);
@@ -1017,12 +1022,12 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) {
XFree(xsh);
}
- if (!p_enabled && !get_borderless_window()) {
- // put decorations back if the window wasn't suppoesed to be borderless
+ if (!p_enabled) {
+ // put back or remove decorations according to the last set borderless state
Hints hints;
Atom property;
hints.flags = 2;
- hints.decorations = 1;
+ hints.decorations = current_videomode.borderless_window ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
}
@@ -1531,7 +1536,7 @@ bool OS_X11::is_window_always_on_top() const {
void OS_X11::set_borderless_window(bool p_borderless) {
- if (current_videomode.borderless_window == p_borderless)
+ if (get_borderless_window() == p_borderless)
return;
if (!p_borderless && layered_window)
@@ -1551,7 +1556,24 @@ void OS_X11::set_borderless_window(bool p_borderless) {
}
bool OS_X11::get_borderless_window() {
- return current_videomode.borderless_window;
+
+ bool borderless = current_videomode.borderless_window;
+ Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ if (prop != None) {
+
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = NULL;
+ if (XGetWindowProperty(x11_display, x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
+ if (data && (format == 32) && (len >= 5)) {
+ borderless = !((Hints *)data)->decorations;
+ }
+ XFree(data);
+ }
+ }
+ return borderless;
}
void OS_X11::request_attention() {
@@ -2378,7 +2400,7 @@ void OS_X11::process_xevents() {
Vector<String> files = String((char *)p.data).split("\n", false);
for (int i = 0; i < files.size(); i++) {
- files.write[i] = files[i].replace("file://", "").http_unescape().strip_escapes();
+ files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges();
}
main_loop->drop_files(files);
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index ad35cdb4f9..eca617bd90 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -42,6 +42,7 @@
#include "main/input_default.h"
#include "power_x11.h"
#include "servers/audio_server.h"
+#include "servers/camera_server.h"
#include "servers/visual/rasterizer.h"
#include "servers/visual_server.h"
//#include "servers/visual/visual_server_wrap_mt.h"
@@ -146,6 +147,8 @@ class OS_X11 : public OS_Unix {
void get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
void flush_mouse_motion();
+ CameraServer *camera_server;
+
MouseMode mouse_mode;
Point2i center;
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 25ad6bd5c9..5bf70e12b7 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -646,6 +646,7 @@ void AnimatedSprite::_reset_timeout() {
void AnimatedSprite::set_animation(const StringName &p_animation) {
ERR_EXPLAIN(vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND(frames == NULL);
ERR_FAIL_COND(frames->get_animation_names().find(p_animation) == -1);
if (animation == p_animation)
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 11846654c5..a0d74dd283 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -29,10 +29,11 @@
/*************************************************************************/
#include "camera_2d.h"
+
+#include "core/engine.h"
#include "core/math/math_funcs.h"
#include "scene/scene_string_names.h"
#include "servers/visual_server.h"
-#include <editor/editor_node.h>
void Camera2D::_update_scroll() {
@@ -44,15 +45,16 @@ void Camera2D::_update_scroll() {
return;
}
+ if (!viewport)
+ return;
+
if (current) {
ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
Transform2D xform = get_camera_transform();
- if (viewport) {
- viewport->set_canvas_transform(xform);
- }
+ viewport->set_canvas_transform(xform);
Size2 screen_size = viewport->get_visible_rect().size;
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index ff27e0a29a..536a05dceb 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -29,8 +29,9 @@
/*************************************************************************/
#include "cpu_particles_2d.h"
-#include "particles_2d.h"
+
#include "scene/2d/canvas_item.h"
+#include "scene/2d/particles_2d.h"
#include "scene/resources/particles_material.h"
#include "servers/visual_server.h"
@@ -324,9 +325,9 @@ void CPUParticles2D::set_param_curve(Parameter p_param, const Ref<Curve> &p_curv
case PARAM_ANGULAR_VELOCITY: {
_adjust_curve_range(p_curve, -360, 360);
} break;
- /*case PARAM_ORBIT_VELOCITY: {
+ case PARAM_ORBIT_VELOCITY: {
_adjust_curve_range(p_curve, -500, 500);
- } break;*/
+ } break;
case PARAM_LINEAR_ACCEL: {
_adjust_curve_range(p_curve, -200, 200);
} break;
@@ -489,12 +490,6 @@ void CPUParticles2D::_validate_property(PropertyInfo &property) const {
if (property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
property.usage = 0;
}
-
- /*
- if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
- property.usage = 0;
- }
- */
}
static uint32_t idhash(uint32_t x) {
@@ -545,6 +540,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
velocity_xform[2] = Vector2();
}
+ float system_phase = time / lifetime;
+
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
@@ -552,21 +549,26 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (!emitting && !p.active)
continue;
- float restart_time = (float(i) / float(pcount)) * lifetime;
float local_delta = p_delta;
+ // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle.
+ // While we use time in tests later on, for randomness we use the phase as done in the
+ // original shader code, and we later multiply by lifetime to get the time.
+ float restart_phase = float(i) / float(pcount);
+
if (randomness_ratio > 0.0) {
uint32_t seed = cycle;
- if (restart_time >= time) {
+ if (restart_phase >= system_phase) {
seed -= uint32_t(1);
}
seed *= uint32_t(pcount);
seed += uint32_t(i);
float random = float(idhash(seed) % uint32_t(65536)) / 65536.0;
- restart_time += randomness_ratio * random * 1.0 / float(pcount);
+ restart_phase += randomness_ratio * random * 1.0 / float(pcount);
}
- restart_time *= (1.0 - explosiveness_ratio);
+ restart_phase *= (1.0 - explosiveness_ratio);
+ float restart_time = restart_phase * lifetime;
bool restart = false;
if (time > prev_time) {
@@ -688,16 +690,12 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]);
}
- /*
- float tex_orbit_velocity = 0.0;
-
- if (flags[FLAG_DISABLE_Z]) {
- if (curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY].is_valid()) {
- tex_orbit_velocity = curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY]->interpolate(p.custom[1]);
- }
+ float tex_orbit_velocity = 0.0;
+ if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
+ tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]);
}
-*/
+
float tex_angular_velocity = 0.0;
if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]);
@@ -748,22 +746,19 @@ void CPUParticles2D::_particles_process(float p_delta) {
force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector2();
//apply tangential acceleration;
Vector2 yx = Vector2(diff.y, diff.x);
- force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2();
+ force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2();
//apply attractor forces
p.velocity += force * local_delta;
//orbit velocity
-#if 0
- if (flags[FLAG_DISABLE_Z]) {
-
- float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);
- if (orbit_amount != 0.0) {
- float ang = orbit_amount * DELTA * pi * 2.0;
- mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));
- TRANSFORM[3].xy -= diff.xy;
- TRANSFORM[3].xy += rot * diff.xy;
- }
+ float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]);
+ if (orbit_amount != 0.0) {
+ float ang = orbit_amount * local_delta * Math_PI * 2.0;
+ // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix,
+ // but we use -ang here to reproduce its behavior.
+ Transform2D rot = Transform2D(-ang, Vector2());
+ p.transform[2] -= diff;
+ p.transform[2] += rot.basis_xform(diff);
}
-#endif
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
p.velocity = p.velocity.normalized() * tex_linear_velocity;
}
@@ -1174,15 +1169,16 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
ADD_GROUP("Time", "");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "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");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "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, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_GROUP("Drawing", "");
+ // No visibility_rect property contrarily to Particles2D, it's updated automatically.
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
@@ -1263,12 +1259,10 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY);
- /*
ADD_GROUP("Orbit Velocity", "orbit_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY);
-*/
ADD_GROUP("Linear Accel", "linear_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
@@ -1324,6 +1318,8 @@ void CPUParticles2D::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_MAX);
BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
+ BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); // Unused, but exposed for consistency with 3D.
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); // Unused, but exposed for consistency with 3D.
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
@@ -1362,7 +1358,7 @@ CPUParticles2D::CPUParticles2D() {
set_spread(45);
set_flatness(0);
set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1);
- //set_param(PARAM_ORBIT_VELOCITY, 0);
+ set_param(PARAM_ORBIT_VELOCITY, 0);
set_param(PARAM_LINEAR_ACCEL, 0);
set_param(PARAM_ANGULAR_VELOCITY, 0);
set_param(PARAM_RADIAL_ACCEL, 0);
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 23d2586331..79444407ee 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -68,6 +68,8 @@ public:
enum Flags {
FLAG_ALIGN_Y_TO_VELOCITY,
+ FLAG_ROTATE_Y, // Unused, but exposed for consistency with 3D.
+ FLAG_DISABLE_Z, // Unused, but exposed for consistency with 3D.
FLAG_MAX
};
@@ -116,7 +118,7 @@ private:
const Particle *particles;
bool operator()(int p_a, int p_b) const {
- return particles[p_a].time < particles[p_b].time;
+ return particles[p_a].time > particles[p_b].time;
}
};
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index ba06b3ebff..5ba184b324 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -105,6 +105,7 @@ void Line2D::set_point_position(int i, Vector2 pos) {
}
Vector2 Line2D::get_point_position(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), Vector2());
return _points.get(i);
}
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index b382ca7b33..bcd4bca940 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -50,6 +50,8 @@ void MeshInstance2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MeshInstance2D::set_normal_map);
ClassDB::bind_method(D_METHOD("get_normal_map"), &MeshInstance2D::get_normal_map);
+ ADD_SIGNAL(MethodInfo("texture_changed"));
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp
new file mode 100644
index 0000000000..ca75302163
--- /dev/null
+++ b/scene/2d/multimesh_instance_2d.cpp
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* multimesh_instance_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 "multimesh_instance_2d.h"
+
+void MultiMeshInstance2D::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_DRAW) {
+ if (multimesh.is_valid()) {
+ draw_multimesh(multimesh, texture, normal_map);
+ }
+ }
+}
+
+void MultiMeshInstance2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_multimesh", "multimesh"), &MultiMeshInstance2D::set_multimesh);
+ ClassDB::bind_method(D_METHOD("get_multimesh"), &MultiMeshInstance2D::get_multimesh);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &MultiMeshInstance2D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &MultiMeshInstance2D::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MultiMeshInstance2D::set_normal_map);
+ ClassDB::bind_method(D_METHOD("get_normal_map"), &MultiMeshInstance2D::get_normal_map);
+
+ ADD_SIGNAL(MethodInfo("texture_changed"));
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
+}
+
+void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) {
+
+ multimesh = p_multimesh;
+ update();
+}
+
+Ref<MultiMesh> MultiMeshInstance2D::get_multimesh() const {
+
+ return multimesh;
+}
+
+void MultiMeshInstance2D::set_texture(const Ref<Texture> &p_texture) {
+
+ if (p_texture == texture)
+ return;
+ texture = p_texture;
+ update();
+ emit_signal("texture_changed");
+ _change_notify("texture");
+}
+
+Ref<Texture> MultiMeshInstance2D::get_texture() const {
+
+ return texture;
+}
+
+void MultiMeshInstance2D::set_normal_map(const Ref<Texture> &p_texture) {
+
+ normal_map = p_texture;
+ update();
+}
+
+Ref<Texture> MultiMeshInstance2D::get_normal_map() const {
+
+ return normal_map;
+}
+
+Rect2 MultiMeshInstance2D::_edit_get_rect() const {
+
+ if (multimesh.is_valid()) {
+ AABB aabb = multimesh->get_aabb();
+ return Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
+ }
+
+ return Node2D::_edit_get_rect();
+}
+
+MultiMeshInstance2D::MultiMeshInstance2D() {
+}
+
+MultiMeshInstance2D::~MultiMeshInstance2D() {
+}
diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h
new file mode 100644
index 0000000000..3795497183
--- /dev/null
+++ b/scene/2d/multimesh_instance_2d.h
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* multimesh_instance_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 MULTIMESH_INSTANCE_2D_H
+#define MULTIMESH_INSTANCE_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/multimesh.h"
+
+class MultiMeshInstance2D : public Node2D {
+ GDCLASS(MultiMeshInstance2D, Node2D);
+
+ Ref<MultiMesh> multimesh;
+
+ Ref<Texture> texture;
+ Ref<Texture> normal_map;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_multimesh(const Ref<MultiMesh> &p_multimesh);
+ Ref<MultiMesh> get_multimesh() const;
+
+ void set_texture(const Ref<Texture> &p_texture);
+ Ref<Texture> get_texture() const;
+
+ void set_normal_map(const Ref<Texture> &p_texture);
+ Ref<Texture> get_normal_map() const;
+
+ virtual Rect2 _edit_get_rect() const;
+
+ MultiMeshInstance2D();
+ ~MultiMeshInstance2D();
+};
+
+#endif // MULTIMESH_INSTANCE_2D_H
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 578c9aa5f9..2bd9e5e2a2 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -203,8 +203,8 @@ void StaticBody2D::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
@@ -217,8 +217,8 @@ void StaticBody2D::set_friction(real_t p_friction) {
real_t StaticBody2D::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 1;
@@ -233,8 +233,8 @@ void StaticBody2D::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -247,8 +247,8 @@ void StaticBody2D::set_bounce(real_t p_bounce) {
real_t StaticBody2D::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 0;
@@ -630,8 +630,8 @@ void RigidBody2D::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
@@ -643,8 +643,8 @@ void RigidBody2D::set_friction(real_t p_friction) {
}
real_t RigidBody2D::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 1;
@@ -659,8 +659,8 @@ void RigidBody2D::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -672,8 +672,8 @@ void RigidBody2D::set_bounce(real_t p_bounce) {
}
real_t RigidBody2D::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 0;
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index bed6f8a816..f0c46a5fb7 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -33,15 +33,19 @@
#include "core/engine.h"
#include "scene/resources/texture.h"
+const float DEFAULT_GIZMO_EXTENTS = 10.0;
+
void Position2D::_draw_cross() {
- draw_line(Point2(-10, 0), Point2(+10, 0), Color(1, 0.5, 0.5));
- draw_line(Point2(0, -10), Point2(0, +10), Color(0.5, 1, 0.5));
+ float extents = get_gizmo_extents();
+ draw_line(Point2(-extents, 0), Point2(+extents, 0), Color(1, 0.5, 0.5));
+ draw_line(Point2(0, -extents), Point2(0, +extents), Color(0.5, 1, 0.5));
}
Rect2 Position2D::_edit_get_rect() const {
- return Rect2(Point2(-10, -10), Size2(20, 20));
+ float extents = get_gizmo_extents();
+ return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2));
}
bool Position2D::_edit_use_rect() const {
@@ -66,5 +70,31 @@ void Position2D::_notification(int p_what) {
}
}
+void Position2D::set_gizmo_extents(float p_extents) {
+ if (p_extents == DEFAULT_GIZMO_EXTENTS) {
+ set_meta("_gizmo_extents_", Variant());
+ } else {
+ set_meta("_gizmo_extents_", p_extents);
+ }
+
+ update();
+}
+
+float Position2D::get_gizmo_extents() const {
+ if (has_meta("_gizmo_extents_")) {
+ return get_meta("_gizmo_extents_");
+ } else {
+ return DEFAULT_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);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_gizmo_extents", "_get_gizmo_extents");
+}
+
Position2D::Position2D() {
}
diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h
index c95315fea3..ec73c02d2b 100644
--- a/scene/2d/position_2d.h
+++ b/scene/2d/position_2d.h
@@ -41,10 +41,15 @@ class Position2D : public Node2D {
protected:
void _notification(int p_what);
+ static void _bind_methods();
public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+
+ void set_gizmo_extents(float p_extents);
+ float get_gizmo_extents() const;
+
Position2D();
};
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 86be8762d4..b3b6f3175d 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -220,8 +220,8 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
xform.elements[1].y = -xform.elements[1].y;
offset.y = s.y - offset.y;
}
-
- offset += cell_size / 2 - s / 2;
+ /* For a future CheckBox to Center Texture:
+ offset += cell_size / 2 - s / 2; */
xform.elements[2] += offset;
}
@@ -372,10 +372,11 @@ void TileMap::update_dirty_quadrants() {
if (c.transpose) {
SWAP(tile_ofs.x, tile_ofs.y);
+ /* For a future CheckBox to Center Texture:
rect.position.x += cell_size.x / 2 - rect.size.y / 2;
rect.position.y += cell_size.y / 2 - rect.size.x / 2;
} else {
- rect.position += cell_size / 2 - rect.size / 2;
+ rect.position += cell_size / 2 - rect.size / 2; */
}
if (c.flip_h) {
@@ -868,8 +869,17 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) {
_make_quadrant_dirty(Q);
} else if (tile_set->tile_get_tile_mode(id) == TileSet::SINGLE_TILE) {
+
E->get().autotile_coord_x = 0;
E->get().autotile_coord_y = 0;
+ } else if (tile_set->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) {
+
+ if (tile_set->autotile_get_bitmask(id, Vector2(p_x, p_y)) == TileSet::BIND_CENTER) {
+ Vector2 coord = tile_set->atlastile_get_subtile_by_priority(id, this, Vector2(p_x, p_y));
+
+ E->get().autotile_coord_x = (int)coord.x;
+ E->get().autotile_coord_y = (int)coord.y;
+ }
}
}
}
diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp
index 4e88948ce2..95eec41fb2 100644
--- a/scene/3d/arvr_nodes.cpp
+++ b/scene/3d/arvr_nodes.cpp
@@ -390,7 +390,7 @@ String ARVRController::get_configuration_warning() const {
};
ARVRController::ARVRController() {
- controller_id = 0;
+ controller_id = 1;
is_active = true;
button_states = 0;
};
@@ -530,7 +530,7 @@ Ref<Mesh> ARVRAnchor::get_mesh() const {
}
ARVRAnchor::ARVRAnchor() {
- anchor_id = 0;
+ anchor_id = 1;
is_active = true;
};
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index a00f2173c0..c7d6919a2b 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -869,6 +869,11 @@ void ClippedCamera::clear_exceptions() {
exclude.clear();
}
+float ClippedCamera::get_clip_offset() const {
+
+ return clip_offset;
+}
+
void ClippedCamera::set_clip_to_areas(bool p_clip) {
clip_to_areas = p_clip;
@@ -912,6 +917,8 @@ void ClippedCamera::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_clip_to_areas", "enable"), &ClippedCamera::set_clip_to_areas);
ClassDB::bind_method(D_METHOD("is_clip_to_areas_enabled"), &ClippedCamera::is_clip_to_areas_enabled);
+ ClassDB::bind_method(D_METHOD("get_clip_offset"), &ClippedCamera::get_clip_offset);
+
ClassDB::bind_method(D_METHOD("set_clip_to_bodies", "enable"), &ClippedCamera::set_clip_to_bodies);
ClassDB::bind_method(D_METHOD("is_clip_to_bodies_enabled"), &ClippedCamera::is_clip_to_bodies_enabled);
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index 1cd729199d..c0a4f77435 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -234,6 +234,8 @@ public:
void remove_exception(const Object *p_object);
void clear_exceptions();
+ float get_clip_offset() const;
+
ClippedCamera();
~ClippedCamera();
};
diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp
index 3190e1e0b2..219ea56681 100644
--- a/scene/3d/collision_shape.cpp
+++ b/scene/3d/collision_shape.cpp
@@ -228,7 +228,9 @@ void CollisionShape::_update_debug_shape() {
void CollisionShape::_shape_changed() {
// If this is a heightfield shape our center may have changed
- _update_in_shape_owner(true);
+ if (parent) {
+ _update_in_shape_owner(true);
+ }
if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) {
debug_shape_dirty = true;
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
index 138c446fea..b42649b35a 100644
--- a/scene/3d/cpu_particles.cpp
+++ b/scene/3d/cpu_particles.cpp
@@ -302,9 +302,9 @@ void CPUParticles::set_param_curve(Parameter p_param, const Ref<Curve> &p_curve)
case PARAM_ANGULAR_VELOCITY: {
_adjust_curve_range(p_curve, -360, 360);
} break;
- /*case PARAM_ORBIT_VELOCITY: {
+ case PARAM_ORBIT_VELOCITY: {
_adjust_curve_range(p_curve, -500, 500);
- } break;*/
+ } break;
case PARAM_LINEAR_ACCEL: {
_adjust_curve_range(p_curve, -200, 200);
} break;
@@ -461,11 +461,10 @@ void CPUParticles::_validate_property(PropertyInfo &property) const {
if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
property.usage = 0;
}
- /*
+
if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
property.usage = 0;
}
- */
}
static uint32_t idhash(uint32_t x) {
@@ -515,6 +514,8 @@ void CPUParticles::_particles_process(float p_delta) {
velocity_xform = emission_xform.basis;
}
+ float system_phase = time / lifetime;
+
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
@@ -522,21 +523,26 @@ void CPUParticles::_particles_process(float p_delta) {
if (!emitting && !p.active)
continue;
- float restart_time = (float(i) / float(pcount)) * lifetime;
float local_delta = p_delta;
+ // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle.
+ // While we use time in tests later on, for randomness we use the phase as done in the
+ // original shader code, and we later multiply by lifetime to get the time.
+ float restart_phase = float(i) / float(pcount);
+
if (randomness_ratio > 0.0) {
uint32_t seed = cycle;
- if (restart_time >= time) {
+ if (restart_phase >= system_phase) {
seed -= uint32_t(1);
}
seed *= uint32_t(pcount);
seed += uint32_t(i);
float random = float(idhash(seed) % uint32_t(65536)) / 65536.0;
- restart_time += randomness_ratio * random * 1.0 / float(pcount);
+ restart_phase += randomness_ratio * random * 1.0 / float(pcount);
}
- restart_time *= (1.0 - explosiveness_ratio);
+ restart_phase *= (1.0 - explosiveness_ratio);
+ float restart_time = restart_phase * lifetime;
bool restart = false;
if (time > prev_time) {
@@ -691,16 +697,14 @@ void CPUParticles::_particles_process(float p_delta) {
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]);
}
- /*
- float tex_orbit_velocity = 0.0;
+ float tex_orbit_velocity = 0.0;
if (flags[FLAG_DISABLE_Z]) {
-
- if (curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY].is_valid()) {
- tex_orbit_velocity = curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY]->interpolate(p.custom[1]);
+ if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
+ tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]);
}
}
-*/
+
float tex_angular_velocity = 0.0;
if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]);
@@ -754,8 +758,9 @@ void CPUParticles::_particles_process(float p_delta) {
//apply tangential acceleration;
if (flags[FLAG_DISABLE_Z]) {
- Vector3 yx = Vector3(diff.y, 0, diff.x);
- force += yx.length() > 0.0 ? (yx * Vector3(-1.0, 0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3();
+ Vector2 yx = Vector2(diff.y, diff.x);
+ Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized();
+ force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3();
} else {
Vector3 crossDiff = diff.normalized().cross(gravity.normalized());
@@ -764,18 +769,18 @@ void CPUParticles::_particles_process(float p_delta) {
//apply attractor forces
p.velocity += force * local_delta;
//orbit velocity
-#if 0
if (flags[FLAG_DISABLE_Z]) {
-
- float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);
+ float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]);
if (orbit_amount != 0.0) {
- float ang = orbit_amount * DELTA * pi * 2.0;
- mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));
- TRANSFORM[3].xy -= diff.xy;
- TRANSFORM[3].xy += rot * diff.xy;
+ float ang = orbit_amount * local_delta * Math_PI * 2.0;
+ // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix,
+ // but we use -ang here to reproduce its behavior.
+ Transform2D rot = Transform2D(-ang, Vector2());
+ Vector2 rotv = rot.basis_xform(Vector2(diff.x, diff.y));
+ p.transform.origin -= Vector3(diff.x, diff.y, 0);
+ p.transform.origin += Vector3(rotv.x, rotv.y, 0);
}
}
-#endif
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
p.velocity = p.velocity.normalized() * tex_linear_velocity;
}
@@ -1171,7 +1176,7 @@ void CPUParticles::convert_from_particles(Node *p_particles) {
CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY);
CONVERT_PARAM(PARAM_ANGULAR_VELOCITY);
- // CONVERT_PARAM(PARAM_ORBIT_VELOCITY);
+ CONVERT_PARAM(PARAM_ORBIT_VELOCITY);
CONVERT_PARAM(PARAM_LINEAR_ACCEL);
CONVERT_PARAM(PARAM_RADIAL_ACCEL);
CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL);
@@ -1314,12 +1319,10 @@ void CPUParticles::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY);
- /*
ADD_GROUP("Orbit Velocity", "orbit_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY);
-*/
ADD_GROUP("Linear Accel", "linear_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
@@ -1362,7 +1365,7 @@ void CPUParticles::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
- //BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
+ BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_LINEAR_ACCEL);
BIND_ENUM_CONSTANT(PARAM_RADIAL_ACCEL);
BIND_ENUM_CONSTANT(PARAM_TANGENTIAL_ACCEL);
@@ -1376,6 +1379,7 @@ void CPUParticles::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
BIND_ENUM_CONSTANT(FLAG_ROTATE_Y);
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_Z);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
@@ -1414,7 +1418,7 @@ CPUParticles::CPUParticles() {
set_spread(45);
set_flatness(0);
set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1);
- //set_param(PARAM_ORBIT_VELOCITY, 0);
+ set_param(PARAM_ORBIT_VELOCITY, 0);
set_param(PARAM_LINEAR_ACCEL, 0);
set_param(PARAM_RADIAL_ACCEL, 0);
set_param(PARAM_TANGENTIAL_ACCEL, 0);
diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h
index b863a3cb3f..6a989251f1 100644
--- a/scene/3d/cpu_particles.h
+++ b/scene/3d/cpu_particles.h
@@ -53,7 +53,7 @@ public:
PARAM_INITIAL_LINEAR_VELOCITY,
PARAM_ANGULAR_VELOCITY,
- //PARAM_ORBIT_VELOCITY,
+ PARAM_ORBIT_VELOCITY,
PARAM_LINEAR_ACCEL,
PARAM_RADIAL_ACCEL,
PARAM_TANGENTIAL_ACCEL,
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 57af951110..3624e04434 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -192,8 +192,8 @@ void StaticBody::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
@@ -206,8 +206,8 @@ void StaticBody::set_friction(real_t p_friction) {
real_t StaticBody::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 1;
@@ -222,8 +222,8 @@ void StaticBody::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -236,8 +236,8 @@ void StaticBody::set_bounce(real_t p_bounce) {
real_t StaticBody::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 0;
@@ -636,8 +636,8 @@ void RigidBody::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
@@ -649,8 +649,8 @@ void RigidBody::set_friction(real_t p_friction) {
}
real_t RigidBody::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 1;
@@ -665,8 +665,8 @@ void RigidBody::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -677,8 +677,8 @@ void RigidBody::set_bounce(real_t p_bounce) {
physics_material_override->set_bounce(p_bounce);
}
real_t RigidBody::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
+ WARN_DEPRECATED;
if (physics_material_override.is_null()) {
return 0;
}
diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h
index 0e7d9be505..3b924e0454 100644
--- a/scene/3d/visual_instance.h
+++ b/scene/3d/visual_instance.h
@@ -89,6 +89,7 @@ class GeometryInstance : public VisualInstance {
public:
enum Flags {
FLAG_USE_BAKED_LIGHT = VS::INSTANCE_FLAG_USE_BAKED_LIGHT,
+ FLAG_DRAW_NEXT_FRAME_IF_VISIBLE = VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE,
FLAG_MAX = VS::INSTANCE_FLAG_MAX,
};
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index e9b38ae990..20a09696e1 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -1049,7 +1049,7 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node
return CONNECTION_ERROR_NO_INPUT;
}
- if (!nodes.has(p_input_node)) {
+ if (p_input_node == p_output_node) {
return CONNECTION_ERROR_SAME_NODE;
}
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 8247728b9d..54f0fdc26a 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -316,7 +316,7 @@ String AnimationNode::get_caption() const {
void AnimationNode::add_input(const String &p_name) {
//root nodes can't add inputs
- ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != NULL)
+ ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != NULL);
Input input;
ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1);
input.name = p_name;
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 3cc90c2ad6..5c3e123ac3 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -404,7 +404,7 @@ void AnimationTreePlayer::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
ERR_EXPLAIN("AnimationTreePlayer has been deprecated. Use AnimationTree instead.");
- WARN_DEPRECATED
+ WARN_DEPRECATED;
if (!processing) {
//make sure that a previous process state was not saved
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 23998183b8..c70e58564f 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -34,10 +34,14 @@
void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8, const Variant &p_arg9, const Variant &p_arg10) {
+ // Add a new pending command and reference it
pending_commands.push_back(PendingCommand());
PendingCommand &cmd = pending_commands.back()->get();
+ // Update the command with the target key
cmd.key = p_key;
+
+ // Determine command argument count
int &count = cmd.args;
if (p_arg10.get_type() != Variant::NIL)
count = 10;
@@ -59,6 +63,9 @@ void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const
count = 2;
else if (p_arg1.get_type() != Variant::NIL)
count = 1;
+
+ // Add the specified arguments to the command
+ // TODO: Make this a switch statement?
if (count > 0)
cmd.arg[0] = p_arg1;
if (count > 1)
@@ -83,10 +90,14 @@ void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const
void Tween::_process_pending_commands() {
+ // For each pending command...
for (List<PendingCommand>::Element *E = pending_commands.front(); E; E = E->next()) {
+ // Get the command
PendingCommand &cmd = E->get();
Variant::CallError err;
+
+ // Grab all of the arguments for the command
Variant *arg[10] = {
&cmd.arg[0],
&cmd.arg[1],
@@ -99,16 +110,20 @@ void Tween::_process_pending_commands() {
&cmd.arg[8],
&cmd.arg[9],
};
+
+ // Execute the command (and retrieve any errors)
this->call(cmd.key, (const Variant **)arg, cmd.args, err);
}
+
+ // Clear the pending commands
pending_commands.clear();
}
bool Tween::_set(const StringName &p_name, const Variant &p_value) {
+ // Set the correct attribute based on the given name
String name = p_name;
-
- if (name == "playback/speed" || name == "speed") { //bw compatibility
+ if (name == "playback/speed" || name == "speed") { // Backwards compatibility
set_speed_scale(p_value);
} else if (name == "playback/active") {
@@ -122,69 +137,78 @@ bool Tween::_set(const StringName &p_name, const Variant &p_value) {
bool Tween::_get(const StringName &p_name, Variant &r_ret) const {
+ // Get the correct attribute based on the given name
String name = p_name;
-
- if (name == "playback/speed") { //bw compatibility
-
+ if (name == "playback/speed") { // Backwards compatibility
r_ret = speed_scale;
- } else if (name == "playback/active") {
+ } else if (name == "playback/active") {
r_ret = is_active();
- } else if (name == "playback/repeat") {
+ } else if (name == "playback/repeat") {
r_ret = is_repeat();
}
-
return true;
}
void Tween::_get_property_list(List<PropertyInfo> *p_list) const {
-
+ // Add the property info for the Tween object
p_list->push_back(PropertyInfo(Variant::BOOL, "playback/active", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::BOOL, "playback/repeat", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::REAL, "playback/speed", PROPERTY_HINT_RANGE, "-64,64,0.01"));
}
void Tween::_notification(int p_what) {
-
+ // What notification did we receive?
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
-
+ // Are we not already active?
if (!is_active()) {
- //make sure that a previous process state was not saved
- //only process if "processing" is set
+ // Make sure that a previous process state was not saved
+ // Only process if "processing" is set
set_physics_process_internal(false);
set_process_internal(false);
}
} break;
- case NOTIFICATION_READY: {
+ case NOTIFICATION_READY: {
+ // Do nothing
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
+ // Are we processing during physics time?
if (tween_process_mode == TWEEN_PROCESS_PHYSICS)
+ // Do nothing since we aren't aligned with physics when we should be
break;
+ // Should we update?
if (is_active())
+ // Update the tweens
_tween_process(get_process_delta_time());
} break;
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ // Are we processing during 'regular' time?
if (tween_process_mode == TWEEN_PROCESS_IDLE)
+ // Do nothing since we whould only process during idle time
break;
+ // Should we update?
if (is_active())
+ // Update the tweens
_tween_process(get_physics_process_delta_time());
} break;
- case NOTIFICATION_EXIT_TREE: {
+ case NOTIFICATION_EXIT_TREE: {
+ // We've left the tree. Stop all tweens
stop_all();
} break;
}
}
void Tween::_bind_methods() {
-
+ // Bind getters and setters
ClassDB::bind_method(D_METHOD("is_active"), &Tween::is_active);
ClassDB::bind_method(D_METHOD("set_active", "active"), &Tween::set_active);
@@ -197,6 +221,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tween_process_mode", "mode"), &Tween::set_tween_process_mode);
ClassDB::bind_method(D_METHOD("get_tween_process_mode"), &Tween::get_tween_process_mode);
+ // Bind the various Tween control methods
ClassDB::bind_method(D_METHOD("start"), &Tween::start);
ClassDB::bind_method(D_METHOD("reset", "object", "key"), &Tween::reset, DEFVAL(""));
ClassDB::bind_method(D_METHOD("reset_all"), &Tween::reset_all);
@@ -211,6 +236,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("tell"), &Tween::tell);
ClassDB::bind_method(D_METHOD("get_runtime"), &Tween::get_runtime);
+ // Bind interpolation and follow methods
ClassDB::bind_method(D_METHOD("interpolate_property", "object", "property", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::interpolate_property, DEFVAL(0));
ClassDB::bind_method(D_METHOD("interpolate_method", "object", "method", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::interpolate_method, DEFVAL(0));
ClassDB::bind_method(D_METHOD("interpolate_callback", "object", "duration", "callback", "arg1", "arg2", "arg3", "arg4", "arg5"), &Tween::interpolate_callback, DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Variant()));
@@ -220,18 +246,22 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("targeting_property", "object", "property", "initial", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_property, DEFVAL(0));
ClassDB::bind_method(D_METHOD("targeting_method", "object", "method", "initial", "initial_method", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_method, DEFVAL(0));
+ // Add the Tween signals
ADD_SIGNAL(MethodInfo("tween_started", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key")));
ADD_SIGNAL(MethodInfo("tween_step", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"), PropertyInfo(Variant::REAL, "elapsed"), PropertyInfo(Variant::OBJECT, "value")));
ADD_SIGNAL(MethodInfo("tween_completed", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key")));
ADD_SIGNAL(MethodInfo("tween_all_completed"));
+ // Add the properties and tie them to the getters and setters
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "repeat"), "set_repeat", "is_repeat");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_tween_process_mode", "get_tween_process_mode");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
+ // Bind Idle vs Physics process
BIND_ENUM_CONSTANT(TWEEN_PROCESS_PHYSICS);
BIND_ENUM_CONSTANT(TWEEN_PROCESS_IDLE);
+ // Bind the Transition type constants
BIND_ENUM_CONSTANT(TRANS_LINEAR);
BIND_ENUM_CONSTANT(TRANS_SINE);
BIND_ENUM_CONSTANT(TRANS_QUINT);
@@ -244,6 +274,7 @@ void Tween::_bind_methods() {
BIND_ENUM_CONSTANT(TRANS_BOUNCE);
BIND_ENUM_CONSTANT(TRANS_BACK);
+ // Bind the easing constants
BIND_ENUM_CONSTANT(EASE_IN);
BIND_ENUM_CONSTANT(EASE_OUT);
BIND_ENUM_CONSTANT(EASE_IN_OUT);
@@ -252,27 +283,30 @@ void Tween::_bind_methods() {
Variant &Tween::_get_initial_val(InterpolateData &p_data) {
+ // What type of data are we interpolating?
switch (p_data.type) {
case INTER_PROPERTY:
case INTER_METHOD:
case FOLLOW_PROPERTY:
case FOLLOW_METHOD:
+ // Simply use the given initial value
return p_data.initial_val;
case TARGETING_PROPERTY:
case TARGETING_METHOD: {
-
+ // Get the object that is being targeted
Object *object = ObjectDB::get_instance(p_data.target_id);
ERR_FAIL_COND_V(object == NULL, p_data.initial_val);
+ // Are we targeting a property or a method?
static Variant initial_val;
if (p_data.type == TARGETING_PROPERTY) {
-
+ // Get the property from the target object
bool valid = false;
initial_val = object->get_indexed(p_data.target_key, &valid);
ERR_FAIL_COND_V(!valid, p_data.initial_val);
} else {
-
+ // Call the method and get the initial value from it
Variant::CallError error;
initial_val = object->call(p_data.target_key[0], NULL, 0, error);
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val);
@@ -281,64 +315,75 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) {
}
case INTER_CALLBACK:
+ // Callback does not have a special initial value
break;
}
+ // If we've made it here, just return the delta value as the initial value
return p_data.delta_val;
}
Variant &Tween::_get_delta_val(InterpolateData &p_data) {
+ // What kind of data are we interpolating?
switch (p_data.type) {
case INTER_PROPERTY:
case INTER_METHOD:
+ // Simply return the given delta value
return p_data.delta_val;
case FOLLOW_PROPERTY:
case FOLLOW_METHOD: {
-
+ // We're following an object, so grab that instance
Object *target = ObjectDB::get_instance(p_data.target_id);
ERR_FAIL_COND_V(target == NULL, p_data.initial_val);
+ // We want to figure out the final value
Variant final_val;
-
if (p_data.type == FOLLOW_PROPERTY) {
-
+ // Read the property as-is
bool valid = false;
final_val = target->get_indexed(p_data.target_key, &valid);
ERR_FAIL_COND_V(!valid, p_data.initial_val);
} else {
-
+ // We're looking at a method. Call the method on the target object
Variant::CallError error;
final_val = target->call(p_data.target_key[0], NULL, 0, error);
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val);
}
- // convert INT to REAL is better for interpolaters
+ // If we're looking at an INT value, instead convert it to a REAL
+ // This is better for interpolation
if (final_val.get_type() == Variant::INT) final_val = final_val.operator real_t();
+
+ // Calculate the delta based on the initial value and the final value
_calc_delta_val(p_data.initial_val, final_val, p_data.delta_val);
return p_data.delta_val;
}
case TARGETING_PROPERTY:
case TARGETING_METHOD: {
-
+ // Grab the initial value from the data to calculate delta
Variant initial_val = _get_initial_val(p_data);
- // convert INT to REAL is better for interpolaters
+
+ // If we're looking at an INT value, instead convert it to a REAL
+ // This is better for interpolation
if (initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
- //_calc_delta_val(p_data.initial_val, p_data.final_val, p_data.delta_val);
+ // Calculate the delta based on the initial value and the final value
_calc_delta_val(initial_val, p_data.final_val, p_data.delta_val);
return p_data.delta_val;
}
case INTER_CALLBACK:
+ // Callbacks have no special delta
break;
}
+ // If we've made it here, use the initial value as the delta
return p_data.initial_val;
}
Variant Tween::_run_equation(InterpolateData &p_data) {
-
+ // Get the initial and delta values from the data
Variant &initial_val = _get_initial_val(p_data);
Variant &delta_val = _get_delta_val(p_data);
Variant result;
@@ -346,48 +391,59 @@ Variant Tween::_run_equation(InterpolateData &p_data) {
#define APPLY_EQUATION(element) \
r.element = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, i.element, d.element, p_data.duration);
+ // What type of data are we interpolating?
switch (initial_val.get_type()) {
case Variant::BOOL:
+ // Run the boolean specific equation (checking if it is at least 0.5)
result = (_run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, initial_val, delta_val, p_data.duration)) >= 0.5;
break;
case Variant::INT:
+ // Run the integer specific equation
result = (int)_run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (int)initial_val, (int)delta_val, p_data.duration);
break;
case Variant::REAL:
+ // Run the REAL specific equation
result = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (real_t)initial_val, (real_t)delta_val, p_data.duration);
break;
case Variant::VECTOR2: {
+ // Get vectors for initial and delta values
Vector2 i = initial_val;
Vector2 d = delta_val;
Vector2 r;
+ // Execute the equation and mutate the r vector
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(x);
APPLY_EQUATION(y);
-
result = r;
} break;
case Variant::VECTOR3: {
+ // Get vectors for initial and delta values
Vector3 i = initial_val;
Vector3 d = delta_val;
Vector3 r;
+ // Execute the equation and mutate the r vector
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(x);
APPLY_EQUATION(y);
APPLY_EQUATION(z);
-
result = r;
} break;
case Variant::BASIS: {
+ // Get the basis for initial and delta values
Basis i = initial_val;
Basis d = delta_val;
Basis r;
+ // Execute the equation on all the basis and mutate the r basis
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(elements[0][0]);
APPLY_EQUATION(elements[0][1]);
APPLY_EQUATION(elements[0][2]);
@@ -397,55 +453,63 @@ Variant Tween::_run_equation(InterpolateData &p_data) {
APPLY_EQUATION(elements[2][0]);
APPLY_EQUATION(elements[2][1]);
APPLY_EQUATION(elements[2][2]);
-
result = r;
} break;
case Variant::TRANSFORM2D: {
+ // Get the transforms for initial and delta values
Transform2D i = initial_val;
Transform2D d = delta_val;
Transform2D r;
+ // Execute the equation on the transforms and mutate the r transform
+ // This uses the custom APPLY_EQUATION macro defined above
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]);
-
result = r;
} break;
case Variant::QUAT: {
+ // Get the quaternian for the initial and delta values
Quat i = initial_val;
Quat d = delta_val;
Quat r;
+ // Execute the equation on the quaternian values and mutate the r quaternian
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(x);
APPLY_EQUATION(y);
APPLY_EQUATION(z);
APPLY_EQUATION(w);
-
result = r;
} break;
case Variant::AABB: {
+ // Get the AABB's for the initial and delta values
AABB i = initial_val;
AABB d = delta_val;
AABB r;
+ // Execute the equation for the position and size of the AABB's and mutate the r AABB
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(position.x);
APPLY_EQUATION(position.y);
APPLY_EQUATION(position.z);
APPLY_EQUATION(size.x);
APPLY_EQUATION(size.y);
APPLY_EQUATION(size.z);
-
result = r;
} break;
case Variant::TRANSFORM: {
+ // Get the transforms for the initial and delta values
Transform i = initial_val;
Transform d = delta_val;
Transform r;
+ // Execute the equation for each of the transforms and their origin and mutate the r transform
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(basis.elements[0][0]);
APPLY_EQUATION(basis.elements[0][1]);
APPLY_EQUATION(basis.elements[0][2]);
@@ -458,40 +522,45 @@ Variant Tween::_run_equation(InterpolateData &p_data) {
APPLY_EQUATION(origin.x);
APPLY_EQUATION(origin.y);
APPLY_EQUATION(origin.z);
-
result = r;
} break;
case Variant::COLOR: {
+ // Get the Color for initial and delta value
Color i = initial_val;
Color d = delta_val;
Color r;
+ // Apply the equation on the Color RGBA, and mutate the r color
+ // This uses the custom APPLY_EQUATION macro defined above
APPLY_EQUATION(r);
APPLY_EQUATION(g);
APPLY_EQUATION(b);
APPLY_EQUATION(a);
-
result = r;
} break;
default: {
+ // If unknown, just return the initial value
result = initial_val;
} break;
};
#undef APPLY_EQUATION
-
+ // Return the result that was computed
return result;
}
bool Tween::_apply_tween_value(InterpolateData &p_data, Variant &value) {
+ // Get the object we want to apply the new value to
Object *object = ObjectDB::get_instance(p_data.id);
ERR_FAIL_COND_V(object == NULL, false);
+ // What kind of data are we mutating?
switch (p_data.type) {
case INTER_PROPERTY:
case FOLLOW_PROPERTY:
case TARGETING_PROPERTY: {
+ // Simply set the property on the object
bool valid = false;
object->set_indexed(p_data.key, value, &valid);
return valid;
@@ -500,85 +569,112 @@ bool Tween::_apply_tween_value(InterpolateData &p_data, Variant &value) {
case INTER_METHOD:
case FOLLOW_METHOD:
case TARGETING_METHOD: {
+ // We want to call the method on the target object
Variant::CallError error;
+
+ // Do we have a non-nil value passed in?
if (value.get_type() != Variant::NIL) {
+ // Pass it as an argument to the function call
Variant *arg[1] = { &value };
object->call(p_data.key[0], (const Variant **)arg, 1, error);
} else {
+ // Don't pass any argument
object->call(p_data.key[0], NULL, 0, error);
}
+ // Did we get an error from the function call?
if (error.error == Variant::CallError::CALL_OK)
return true;
return false;
}
case INTER_CALLBACK:
+ // Nothing to apply for a callback
break;
};
+ // No issues found!
return true;
}
void Tween::_tween_process(float p_delta) {
-
+ // Process all of the pending commands
_process_pending_commands();
+ // If the scale is 0, make no progress on the tweens
if (speed_scale == 0)
return;
- p_delta *= speed_scale;
+ // Update the delta and whether we are pending an update
+ p_delta *= speed_scale;
pending_update++;
- // if repeat and all interpolates was finished then reset all interpolates
- bool all_finished = true;
- if (repeat) {
+ // Are we repeating the interpolations?
+ if (repeat) {
+ // For each interpolation...
+ bool repeats_finished = true;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the data from it
InterpolateData &data = E->get();
+ // Is not finished?
if (!data.finish) {
- all_finished = false;
+ // We aren't finished yet, no need to check the rest
+ repeats_finished = false;
break;
}
}
- if (all_finished)
+ // If we are all finished, we can reset all of the tweens
+ if (repeats_finished)
reset_all();
}
- all_finished = true;
+ // Are all of the tweens complete?
+ bool all_finished = true;
+
+ // For each tween we wish to interpolate...
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ // Get the data from it
InterpolateData &data = E->get();
+
+ // Track if we hit one that isn't finished yet
all_finished = all_finished && data.finish;
+ // Is the data not active or already finished? No need to go any further
if (!data.active || data.finish)
continue;
+ // Get the target object for this interpolation
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+ // Are we still delaying this tween?
bool prev_delaying = data.elapsed <= data.delay;
data.elapsed += p_delta;
if (data.elapsed < data.delay)
continue;
else if (prev_delaying) {
-
+ // We can apply the tween's value to the data and emit that the tween has started
_apply_tween_value(data, data.initial_val);
emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false));
}
+ // Are we at the end of the tween?
if (data.elapsed > (data.delay + data.duration)) {
-
+ // Set the elapsed time to the end and mark this one as finished
data.elapsed = data.delay + data.duration;
data.finish = true;
}
+ // Are we interpolating a callback?
if (data.type == INTER_CALLBACK) {
+ // Is the tween completed?
if (data.finish) {
+ // Are we calling this callback deferred or immediately?
if (data.call_deferred) {
-
+ // Run the deferred function callback, applying the correct number of arguments
switch (data.args) {
case 0:
object->call_deferred(data.key[0]);
@@ -600,6 +696,7 @@ void Tween::_tween_process(float p_delta) {
break;
}
} else {
+ // Call the function directly with the arguments
Variant::CallError error;
Variant *arg[5] = {
&data.arg[0],
@@ -612,23 +709,35 @@ void Tween::_tween_process(float p_delta) {
}
}
} else {
+ // We can apply the value directly
Variant result = _run_equation(data);
_apply_tween_value(data, result);
+
+ // Emit that the tween has taken a step
emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result);
}
+ // Is the tween now finished?
if (data.finish) {
+ // Set it to the final value directly
_apply_tween_value(data, data.final_val);
+
+ // Mark the tween as completed and emit the signal
data.elapsed = 0;
emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false));
- // not repeat mode, remove completed action
+
+ // If we are not repeating the tween, remove it
if (!repeat)
call_deferred("_remove_by_uid", data.uid);
- } else if (!repeat)
+ } else if (!repeat) {
+ // Check whether all tweens are finished
all_finished = all_finished && data.finish;
+ }
}
+ // One less update left to go
pending_update--;
+ // If all tweens are completed, we no longer need to be active
if (all_finished) {
set_active(false);
emit_signal("tween_all_completed");
@@ -636,76 +745,75 @@ void Tween::_tween_process(float p_delta) {
}
void Tween::set_tween_process_mode(TweenProcessMode p_mode) {
-
tween_process_mode = p_mode;
}
Tween::TweenProcessMode Tween::get_tween_process_mode() const {
-
return tween_process_mode;
}
bool Tween::is_active() const {
-
return is_processing_internal() || is_physics_processing_internal();
}
void Tween::set_active(bool p_active) {
-
+ // Do nothing if it's the same active mode that we currently are
if (is_active() == p_active)
return;
+ // Depending on physics or idle, set processing
switch (tween_process_mode) {
-
case TWEEN_PROCESS_IDLE: set_process_internal(p_active); break;
case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_active); break;
}
}
bool Tween::is_repeat() const {
-
return repeat;
}
void Tween::set_repeat(bool p_repeat) {
-
repeat = p_repeat;
}
void Tween::set_speed_scale(float p_speed) {
-
speed_scale = p_speed;
}
float Tween::get_speed_scale() const {
-
return speed_scale;
}
bool Tween::start() {
+ // Are there any pending updates?
if (pending_update != 0) {
+ // Start the tweens after deferring
call_deferred("start");
return true;
}
+ // We want to be activated
set_active(true);
return true;
}
bool Tween::reset(Object *p_object, StringName p_key) {
-
+ // Find all interpolations that use the same object and target string
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the target object
InterpolateData &data = E->get();
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+ // Do we have the correct object and key?
if (object == p_object && (data.concatenated_key == p_key || p_key == "")) {
-
+ // Reset the tween to the initial state
data.elapsed = 0;
data.finish = false;
+
+ // Also apply the initial state if there isn't a delay
if (data.delay == 0)
_apply_tween_value(data, data.initial_val);
}
@@ -715,13 +823,15 @@ bool Tween::reset(Object *p_object, StringName p_key) {
}
bool Tween::reset_all() {
-
+ // Go through all interpolations
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the target data and set it back to the initial state
InterpolateData &data = E->get();
data.elapsed = 0;
data.finish = false;
+
+ // If there isn't a delay, apply the value to the object
if (data.delay == 0)
_apply_tween_value(data, data.initial_val);
}
@@ -730,15 +840,19 @@ bool Tween::reset_all() {
}
bool Tween::stop(Object *p_object, StringName p_key) {
-
+ // Find the tween that has the given target object and string key
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ // Get the object the tween is targeting
InterpolateData &data = E->get();
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+
+ // Is this the correct object and does it have the given key?
if (object == p_object && (data.concatenated_key == p_key || p_key == ""))
+ // Disable the tween
data.active = false;
}
pending_update--;
@@ -746,12 +860,13 @@ bool Tween::stop(Object *p_object, StringName p_key) {
}
bool Tween::stop_all() {
-
+ // We no longer need to be active since all tweens have been stopped
set_active(false);
+ // For each interpolation...
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Simply set it inactive
InterpolateData &data = E->get();
data.active = false;
}
@@ -760,16 +875,20 @@ bool Tween::stop_all() {
}
bool Tween::resume(Object *p_object, StringName p_key) {
-
+ // We need to be activated
+ // TODO: What if no tween is found??
set_active(true);
+ // Find the tween that uses the given target object and string key
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Grab the object
InterpolateData &data = E->get();
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+
+ // If the object and string key match, activate it
if (object == p_object && (data.concatenated_key == p_key || p_key == ""))
data.active = true;
}
@@ -778,12 +897,14 @@ bool Tween::resume(Object *p_object, StringName p_key) {
}
bool Tween::resume_all() {
-
+ // Set ourselves active so we can process tweens
+ // TODO: What if there are no tweens? We get set to active for no reason!
set_active(true);
+ // For each interpolation...
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Simply grab it and set it to active
InterpolateData &data = E->get();
data.active = true;
}
@@ -792,35 +913,46 @@ bool Tween::resume_all() {
}
bool Tween::remove(Object *p_object, StringName p_key) {
+ // If we are still updating, call this function again later
if (pending_update != 0) {
call_deferred("remove", p_object, p_key);
return true;
}
+
+ // For each interpolation...
List<List<InterpolateData>::Element *> for_removal;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the target object
InterpolateData &data = E->get();
Object *object = ObjectDB::get_instance(data.id);
if (object == NULL)
continue;
+
+ // If the target object and string key match, queue it for removal
if (object == p_object && (data.concatenated_key == p_key || p_key == "")) {
for_removal.push_back(E);
}
}
+
+ // For each interpolation we wish to remove...
for (List<List<InterpolateData>::Element *>::Element *E = for_removal.front(); E; E = E->next()) {
+ // Erase it
interpolates.erase(E->get());
}
return true;
}
void Tween::_remove_by_uid(int uid) {
+ // If we are still updating, call this function again later
if (pending_update != 0) {
call_deferred("_remove_by_uid", uid);
return;
}
+ // Find the interpolation that matches the given UID
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
if (uid == E->get().uid) {
+ // It matches, erase it and stop looking
E->erase();
break;
}
@@ -829,49 +961,61 @@ void Tween::_remove_by_uid(int uid) {
void Tween::_push_interpolate_data(InterpolateData &p_data) {
pending_update++;
+
+ // Add the new interpolation
p_data.uid = ++uid;
interpolates.push_back(p_data);
+
pending_update--;
}
bool Tween::remove_all() {
-
+ // If we are still updating, call this function again later
if (pending_update != 0) {
call_deferred("remove_all");
return true;
}
+ // We no longer need to be active
set_active(false);
+
+ // Clear out all interpolations and reset the uid
interpolates.clear();
uid = 0;
+
return true;
}
bool Tween::seek(real_t p_time) {
-
+ // Go through each interpolation...
pending_update++;
for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the target data
InterpolateData &data = E->get();
+ // Update the elapsed data to be set to the target time
data.elapsed = p_time;
- if (data.elapsed < data.delay) {
+ // Are we at the end?
+ if (data.elapsed < data.delay) {
+ // There is still time left to go
data.finish = false;
continue;
} else if (data.elapsed >= (data.delay + data.duration)) {
-
- data.finish = true;
+ // We are past the end of it, set the elapsed time to the end and mark as finished
data.elapsed = (data.delay + data.duration);
+ data.finish = true;
} else {
+ // We are not finished with this interpolation yet
data.finish = false;
}
+ // If we are a callback, do nothing special
if (data.type == INTER_CALLBACK) {
continue;
}
+ // Run the equation on the data and apply the value
Variant result = _run_equation(data);
-
_apply_tween_value(data, result);
}
pending_update--;
@@ -879,13 +1023,16 @@ bool Tween::seek(real_t p_time) {
}
real_t Tween::tell() const {
-
+ // We want to grab the position of the furthest along tween
pending_update++;
real_t pos = 0;
- for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ // For each interpolation...
+ for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
+ // Get the data and figure out if it's position is further along than the previous ones
const InterpolateData &data = E->get();
if (data.elapsed > pos)
+ // Save it if so
pos = data.elapsed;
}
pending_update--;
@@ -893,55 +1040,63 @@ real_t Tween::tell() const {
}
real_t Tween::get_runtime() const {
-
+ // If the tween isn't moving, it'll last forever
if (speed_scale == 0) {
return INFINITY;
}
pending_update++;
+
+ // For each interpolation...
real_t runtime = 0;
for (const List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) {
-
+ // Get the tween data and see if it's runtime is greater than the previous tweens
const InterpolateData &data = E->get();
real_t t = data.delay + data.duration;
if (t > runtime)
+ // This is the longest running tween
runtime = t;
}
pending_update--;
+ // Adjust the runtime for the current speed scale
return runtime / speed_scale;
}
bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final_val, Variant &p_delta_val) {
+ // Get the initial, final, and delta values
const Variant &initial_val = p_initial_val;
const Variant &final_val = p_final_val;
Variant &delta_val = p_delta_val;
+ // What kind of data are we interpolating?
switch (initial_val.get_type()) {
case Variant::BOOL:
- //delta_val = p_final_val;
- delta_val = (int)p_final_val - (int)p_initial_val;
- break;
-
+ // We'll treat booleans just like integers
case Variant::INT:
+ // Compute the integer delta
delta_val = (int)final_val - (int)initial_val;
break;
case Variant::REAL:
+ // Convert to REAL and find the delta
delta_val = (real_t)final_val - (real_t)initial_val;
break;
case Variant::VECTOR2:
+ // Convert to Vectors and find the delta
delta_val = final_val.operator Vector2() - initial_val.operator Vector2();
break;
case Variant::VECTOR3:
+ // Convert to Vectors and find the delta
delta_val = final_val.operator Vector3() - initial_val.operator Vector3();
break;
case Variant::BASIS: {
+ // Build a new basis which is the delta between the initial and final values
Basis i = initial_val;
Basis f = final_val;
delta_val = Basis(f.elements[0][0] - i.elements[0][0],
@@ -956,6 +1111,7 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final
} break;
case Variant::TRANSFORM2D: {
+ // Build a new transform which is the difference between the initial and final values
Transform2D i = initial_val;
Transform2D f = final_val;
Transform2D d = Transform2D();
@@ -967,15 +1123,21 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final
d[2][1] = f.elements[2][1] - i.elements[2][1];
delta_val = d;
} break;
+
case Variant::QUAT:
+ // Convert to quaternianls and find the delta
delta_val = final_val.operator Quat() - initial_val.operator Quat();
break;
+
case Variant::AABB: {
+ // Build a new AABB and use the new position and sizes to make a delta
AABB i = initial_val;
AABB f = final_val;
delta_val = AABB(f.position - i.position, f.size - i.size);
} break;
+
case Variant::TRANSFORM: {
+ // Build a new transform which is the difference between the initial and final values
Transform i = initial_val;
Transform f = final_val;
Transform d;
@@ -994,124 +1156,157 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final
delta_val = d;
} break;
+
case Variant::COLOR: {
+ // Make a new color which is the difference between each the color's RGBA attributes
Color i = initial_val;
Color f = final_val;
delta_val = Color(f.r - i.r, f.g - i.g, f.b - i.b, f.a - i.a);
} break;
default:
+ // TODO: Should move away from a 'magic string'?
ERR_PRINT("Invalid param type, except(int/real/vector2/vector/matrix/matrix32/quat/aabb/transform/color)");
return false;
};
return true;
}
-bool Tween::interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
- if (pending_update != 0) {
- _add_pending_command("interpolate_property", p_object, p_property, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
- return true;
- }
- p_property = p_property.get_as_property_path();
-
- if (p_initial_val.get_type() == Variant::NIL) p_initial_val = p_object->get_indexed(p_property.get_subnames());
-
- // convert INT to REAL is better for interpolaters
- if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
- if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
-
- ERR_FAIL_COND_V(p_object == NULL, false);
- ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
- ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
- ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
- ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
- ERR_FAIL_COND_V(p_delay < 0, false);
+bool Tween::_build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
- bool prop_valid = false;
- p_object->get_indexed(p_property.get_subnames(), &prop_valid);
- ERR_FAIL_COND_V(!prop_valid, false);
+ // TODO: Add initialization+implementation for remaining interpolation types
+ // TODO: Fix this method's organization to take advantage of the type
+ // Make a new interpolation data
InterpolateData data;
data.active = true;
- data.type = INTER_PROPERTY;
+ data.type = p_interpolation_type;
data.finish = false;
data.elapsed = 0;
+ // Validate and apply interpolation data
+
+ // Give it the object
+ ERR_EXPLAIN("Invalid object provided to Tween!");
+ ERR_FAIL_COND_V(p_object == NULL, false); // Is the object real
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false); // Is the object a valid instance?
data.id = p_object->get_instance_id();
- data.key = p_property.get_subnames();
- data.concatenated_key = p_property.get_concatenated_subnames();
+
+ // Validate the initial and final values
+ ERR_EXPLAIN("Initial value type does not match final value type!"); // TODO: Print both types to make debugging easier
+ ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false); // Do the initial and final value types match?
data.initial_val = p_initial_val;
data.final_val = p_final_val;
+
+ // Check the Duration
+ ERR_EXPLAIN("Only non-negative duration values allowed in Tweens!");
+ ERR_FAIL_COND_V(p_duration < 0, false); // Is the tween duration non-negative
data.duration = p_duration;
+
+ // Tween Delay
+ ERR_EXPLAIN("Only non-negative delay values allowed in Tweens!");
+ ERR_FAIL_COND_V(p_delay < 0, false); // Is the delay non-negative?
+ data.delay = p_delay;
+
+ // Transition type
+ ERR_EXPLAIN("Invalid transition type provided to Tween");
+ ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false); // Is the transition type valid
data.trans_type = p_trans_type;
+
+ // Easing type
+ ERR_EXPLAIN("Invalid easing type provided to Tween");
+ ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false); // Is the easing type valid
data.ease_type = p_ease_type;
- data.delay = p_delay;
+ // Is the property defined?
+ if (p_property) {
+ // Check that the object actually contains the given property
+ bool prop_valid = false;
+ p_object->get_indexed(p_property->get_subnames(), &prop_valid);
+ ERR_EXPLAIN("Tween target object has no property named: " + p_property->get_concatenated_subnames());
+ ERR_FAIL_COND_V(!prop_valid, false);
+
+ data.key = p_property->get_subnames();
+ data.concatenated_key = p_property->get_concatenated_subnames();
+ }
+
+ // Is the method defined?
+ if (p_method) {
+ // Does the object even have the requested method?
+ ERR_EXPLAIN("Tween target object has no method named: " + *p_method); // TODO: Fix this error message
+ ERR_FAIL_COND_V(!p_object->has_method(*p_method), false);
+
+ data.key.push_back(*p_method);
+ data.concatenated_key = *p_method;
+ }
+
+ // Is there not a valid delta?
if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
return false;
+ // Add this interpolation to the total
_push_interpolate_data(data);
return true;
}
-bool Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+bool Tween::interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are busy updating, call this function again later
if (pending_update != 0) {
- _add_pending_command("interpolate_method", p_object, p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
+ _add_pending_command("interpolate_property", p_object, p_property, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
- // convert INT to REAL is better for interpolaters
- if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
- if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
- ERR_FAIL_COND_V(p_object == NULL, false);
- ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
- ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
- ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
- ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
- ERR_FAIL_COND_V(p_delay < 0, false);
+ // Get the property from the node path
+ p_property = p_property.get_as_property_path();
- ERR_EXPLAIN("Object has no method named: %s" + p_method);
- ERR_FAIL_COND_V(!p_object->has_method(p_method), false);
+ // If no initial value given, grab the initial value from the object
+ // TODO: Is this documented? This is very useful and removes a lot of clutter from tweens!
+ if (p_initial_val.get_type() == Variant::NIL) p_initial_val = p_object->get_indexed(p_property.get_subnames());
- InterpolateData data;
- data.active = true;
- data.type = INTER_METHOD;
- data.finish = false;
- data.elapsed = 0;
+ // Convert any integers into REALs as they are better for interpolation
+ if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
- data.id = p_object->get_instance_id();
- data.key.push_back(p_method);
- data.concatenated_key = p_method;
- data.initial_val = p_initial_val;
- data.final_val = p_final_val;
- data.duration = p_duration;
- data.trans_type = p_trans_type;
- data.ease_type = p_ease_type;
- data.delay = p_delay;
+ // Build the interpolation data
+ bool result = _build_interpolation(INTER_PROPERTY, p_object, &p_property, NULL, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
+ return result;
+}
- if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
- return false;
+bool Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are busy updating, call this function again later
+ if (pending_update != 0) {
+ _add_pending_command("interpolate_method", p_object, p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
+ return true;
+ }
- _push_interpolate_data(data);
- return true;
+ // Convert any integers into REALs as they are better for interpolation
+ if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+
+ // Build the interpolation data
+ bool result = _build_interpolation(INTER_METHOD, p_object, NULL, &p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
+ return result;
}
bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) {
-
+ // If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("interpolate_callback", p_object, p_duration, p_callback, p_arg1, p_arg2, p_arg3, p_arg4, p_arg5);
return true;
}
+ // Check that the target object is valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
+
+ // Duration cannot be negative
ERR_FAIL_COND_V(p_duration < 0, false);
+ // Check whether the object even has the callback
ERR_EXPLAIN("Object has no callback named: %s" + p_callback);
ERR_FAIL_COND_V(!p_object->has_method(p_callback), false);
+ // Build a new InterpolationData
InterpolateData data;
data.active = true;
data.type = INTER_CALLBACK;
@@ -1119,12 +1314,14 @@ bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_c
data.call_deferred = false;
data.elapsed = 0;
+ // Give the data it's configuration
data.id = p_object->get_instance_id();
data.key.push_back(p_callback);
data.concatenated_key = p_callback;
data.duration = p_duration;
data.delay = 0;
+ // Add arguments to the interpolation
int args = 0;
if (p_arg5.get_type() != Variant::NIL)
args = 5;
@@ -1146,23 +1343,30 @@ bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_c
data.arg[3] = p_arg4;
data.arg[4] = p_arg5;
+ // Add the new interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE) {
-
+ // If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("interpolate_deferred_callback", p_object, p_duration, p_callback, p_arg1, p_arg2, p_arg3, p_arg4, p_arg5);
return true;
}
+
+ // Check that the target object is valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
+
+ // No negative durations allowed
ERR_FAIL_COND_V(p_duration < 0, false);
+ // Confirm the callback exists on the object
ERR_EXPLAIN("Object has no callback named: %s" + p_callback);
ERR_FAIL_COND_V(!p_object->has_method(p_callback), false);
+ // Create a new InterpolateData for the callback
InterpolateData data;
data.active = true;
data.type = INTER_CALLBACK;
@@ -1170,12 +1374,14 @@ bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, S
data.call_deferred = true;
data.elapsed = 0;
+ // Give the data it's configuration
data.id = p_object->get_instance_id();
data.key.push_back(p_callback);
data.concatenated_key = p_callback;
data.duration = p_duration;
data.delay = 0;
+ // Collect arguments for the callback
int args = 0;
if (p_arg5.get_type() != Variant::NIL)
args = 5;
@@ -1197,32 +1403,46 @@ bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, S
data.arg[3] = p_arg4;
data.arg[4] = p_arg5;
+ // Add the new interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are already updating, call this function again later
if (pending_update != 0) {
_add_pending_command("follow_property", p_object, p_property, p_initial_val, p_target, p_target_property, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
+
+ // Get the two properties from their paths
p_property = p_property.get_as_property_path();
p_target_property = p_target_property.get_as_property_path();
+ // If no initial value is given, grab it from the source object
+ // TODO: Is this documented? It's really helpful for decluttering tweens
if (p_initial_val.get_type() == Variant::NIL) p_initial_val = p_object->get_indexed(p_property.get_subnames());
- // convert INT to REAL is better for interpolaters
+ // Convert initial INT values to REAL as they are better for interpolation
if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ // Confirm the source and target objects are valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_target == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_target), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
+
+ // No negative durations
+ ERR_FAIL_COND_V(p_duration < 0, false);
+
+ // Ensure transition and easing types are valid
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+
+ // No negative delays
ERR_FAIL_COND_V(p_delay < 0, false);
+ // Confirm the source and target objects have the desired properties
bool prop_valid = false;
p_object->get_indexed(p_property.get_subnames(), &prop_valid);
ERR_FAIL_COND_V(!prop_valid, false);
@@ -1231,16 +1451,20 @@ bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_ini
Variant target_val = p_target->get_indexed(p_target_property.get_subnames(), &target_prop_valid);
ERR_FAIL_COND_V(!target_prop_valid, false);
- // convert INT to REAL is better for interpolaters
+ // Convert target INT to REAL since it is better for interpolation
if (target_val.get_type() == Variant::INT) target_val = target_val.operator real_t();
+
+ // Verify that the target value and initial value are the same type
ERR_FAIL_COND_V(target_val.get_type() != p_initial_val.get_type(), false);
+ // Create a new InterpolateData
InterpolateData data;
data.active = true;
data.type = FOLLOW_PROPERTY;
data.finish = false;
data.elapsed = 0;
+ // Give the InterpolateData it's configuration
data.id = p_object->get_instance_id();
data.key = p_property.get_subnames();
data.concatenated_key = p_property.get_concatenated_subnames();
@@ -1252,46 +1476,59 @@ bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_ini
data.ease_type = p_ease_type;
data.delay = p_delay;
+ // Add the interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("follow_method", p_object, p_method, p_initial_val, p_target, p_target_method, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
- // convert INT to REAL is better for interpolaters
+ // Convert initial INT values to REAL as they are better for interpolation
if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ // Verify the source and target objects are valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_target == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_target), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
+
+ // No negative durations
+ ERR_FAIL_COND_V(p_duration < 0, false);
+
+ // Ensure that the transition and ease types are valid
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+
+ // No negative delays
ERR_FAIL_COND_V(p_delay < 0, false);
+ // Confirm both objects have the target methods
ERR_EXPLAIN("Object has no method named: %s" + p_method);
ERR_FAIL_COND_V(!p_object->has_method(p_method), false);
ERR_EXPLAIN("Target has no method named: %s" + p_target_method);
ERR_FAIL_COND_V(!p_target->has_method(p_target_method), false);
+ // Call the method to get the target value
Variant::CallError error;
Variant target_val = p_target->call(p_target_method, NULL, 0, error);
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, false);
- // convert INT to REAL is better for interpolaters
+ // Convert target INT values to REAL as they are better for interpolation
if (target_val.get_type() == Variant::INT) target_val = target_val.operator real_t();
ERR_FAIL_COND_V(target_val.get_type() != p_initial_val.get_type(), false);
+ // Make the new InterpolateData for the method follow
InterpolateData data;
data.active = true;
data.type = FOLLOW_METHOD;
data.finish = false;
data.elapsed = 0;
+ // Give the data it's configuration
data.id = p_object->get_instance_id();
data.key.push_back(p_method);
data.concatenated_key = p_method;
@@ -1303,31 +1540,41 @@ bool Tween::follow_method(Object *p_object, StringName p_method, Variant p_initi
data.ease_type = p_ease_type;
data.delay = p_delay;
+ // Add the new interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
-
+ // If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("targeting_property", p_object, p_property, p_initial, p_initial_property, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
+ // Grab the target property and the target property
p_property = p_property.get_as_property_path();
p_initial_property = p_initial_property.get_as_property_path();
- // convert INT to REAL is better for interpolaters
+ // Convert the initial INT values to REAL as they are better for Interpolation
if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+ // Verify both objects are valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_initial == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_initial), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
+
+ // No negative durations
+ ERR_FAIL_COND_V(p_duration < 0, false);
+
+ // Ensure transition and easing types are valid
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+
+ // No negative delays
ERR_FAIL_COND_V(p_delay < 0, false);
+ // Ensure the initial and target properties exist on their objects
bool prop_valid = false;
p_object->get_indexed(p_property.get_subnames(), &prop_valid);
ERR_FAIL_COND_V(!prop_valid, false);
@@ -1336,16 +1583,18 @@ bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_
Variant initial_val = p_initial->get_indexed(p_initial_property.get_subnames(), &initial_prop_valid);
ERR_FAIL_COND_V(!initial_prop_valid, false);
- // convert INT to REAL is better for interpolaters
+ // Convert the initial INT value to REAL as it is better for interpolation
if (initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
ERR_FAIL_COND_V(initial_val.get_type() != p_final_val.get_type(), false);
+ // Build the InterpolateData object
InterpolateData data;
data.active = true;
data.type = TARGETING_PROPERTY;
data.finish = false;
data.elapsed = 0;
+ // Give the data it's configuration
data.id = p_object->get_instance_id();
data.key = p_property.get_subnames();
data.concatenated_key = p_property.get_concatenated_subnames();
@@ -1358,49 +1607,64 @@ bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_
data.ease_type = p_ease_type;
data.delay = p_delay;
+ // Ensure there is a valid delta
if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
return false;
+ // Add the interpolation
_push_interpolate_data(data);
return true;
}
bool Tween::targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) {
+ // If we are currently updating, call this function again later
if (pending_update != 0) {
_add_pending_command("targeting_method", p_object, p_method, p_initial, p_initial_method, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay);
return true;
}
- // convert INT to REAL is better for interpolaters
+
+ // Convert final INT values to REAL as they are better for interpolation
if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+ // Make sure the given objects are valid
ERR_FAIL_COND_V(p_object == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_initial == NULL, false);
ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_initial), false);
- ERR_FAIL_COND_V(p_duration <= 0, false);
+
+ // No negative durations
+ ERR_FAIL_COND_V(p_duration < 0, false);
+
+ // Ensure transition and easing types are valid
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+
+ // No negative delays
ERR_FAIL_COND_V(p_delay < 0, false);
+ // Make sure both objects have the given method
ERR_EXPLAIN("Object has no method named: %s" + p_method);
ERR_FAIL_COND_V(!p_object->has_method(p_method), false);
ERR_EXPLAIN("Initial Object has no method named: %s" + p_initial_method);
ERR_FAIL_COND_V(!p_initial->has_method(p_initial_method), false);
+ // Call the method to get the initial value
Variant::CallError error;
Variant initial_val = p_initial->call(p_initial_method, NULL, 0, error);
ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, false);
- // convert INT to REAL is better for interpolaters
+ // Convert initial INT values to REAL as they aer better for interpolation
if (initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
ERR_FAIL_COND_V(initial_val.get_type() != p_final_val.get_type(), false);
+ // Build the new InterpolateData object
InterpolateData data;
data.active = true;
data.type = TARGETING_METHOD;
data.finish = false;
data.elapsed = 0;
+ // Configure the data
data.id = p_object->get_instance_id();
data.key.push_back(p_method);
data.concatenated_key = p_method;
@@ -1413,16 +1677,17 @@ bool Tween::targeting_method(Object *p_object, StringName p_method, Object *p_in
data.ease_type = p_ease_type;
data.delay = p_delay;
+ // Ensure there is a valid delta
if (!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
return false;
+ // Add the interpolation
_push_interpolate_data(data);
return true;
}
Tween::Tween() {
-
- //String autoplay;
+ // Initialize tween attributes
tween_process_mode = TWEEN_PROCESS_IDLE;
repeat = false;
speed_scale = 1;
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 6fe3bffdbe..64ce099ecd 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -135,6 +135,7 @@ private:
void _tween_process(float p_delta);
void _remove_by_uid(int uid);
void _push_interpolate_data(InterpolateData &p_data);
+ bool _build_interpolation(InterpolateType p_interpolation_type, Object *p_object, NodePath *p_property, StringName *p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 5ef2557383..fadf5e432c 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -216,9 +216,7 @@ void BaseButton::set_pressed(bool p_pressed) {
if (p_pressed) {
_unpress_group();
}
- if (toggle_mode) {
- _toggled(status.pressed);
- }
+ _toggled(status.pressed);
update();
}
@@ -337,9 +335,6 @@ bool BaseButton::is_keep_pressed_outside() const {
void BaseButton::set_shortcut(const Ref<ShortCut> &p_shortcut) {
- if (shortcut.is_null() == p_shortcut.is_null())
- return;
-
shortcut = p_shortcut;
set_process_unhandled_input(shortcut.is_valid());
}
@@ -356,11 +351,10 @@ void BaseButton::_unhandled_input(Ref<InputEvent> p_event) {
return; //ignore because of modal window
if (is_toggle_mode()) {
- set_pressed(!is_pressed());
- emit_signal("toggled", is_pressed());
+ set_pressed(!is_pressed()); // Also calls _toggled() internally.
}
- emit_signal("pressed");
+ _pressed();
}
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 76275c2420..9d7c08d3f3 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -29,23 +29,23 @@
/*************************************************************************/
#include "control.h"
-#include "core/project_settings.h"
-#include "scene/main/canvas_layer.h"
-#include "scene/main/viewport.h"
-#include "servers/visual_server.h"
#include "core/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/print_string.h"
+#include "core/project_settings.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
+#include "scene/main/canvas_layer.h"
+#include "scene/main/viewport.h"
#include "scene/scene_string_names.h"
+#include "servers/visual_server.h"
+
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#endif
-#include <stdio.h>
Dictionary Control::_edit_get_state() const {
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index bdb1342019..bc8dcf0e82 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -48,8 +48,9 @@ void FileDialog::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- refresh->set_icon(get_icon("reload"));
dir_up->set_icon(get_icon("parent_folder"));
+ refresh->set_icon(get_icon("reload"));
+ show_hidden->set_icon(get_icon("toggle_hidden"));
}
if (p_what == NOTIFICATION_POPUP_HIDE) {
@@ -393,20 +394,19 @@ void FileDialog::update_file_list() {
List<String> files;
List<String> dirs;
- bool isdir;
- bool ishidden;
- bool show_hidden = show_hidden_files;
+ bool is_dir;
+ bool is_hidden;
String item;
- while ((item = dir_access->get_next(&isdir)) != "") {
+ while ((item = dir_access->get_next(&is_dir)) != "") {
if (item == "." || item == "..")
continue;
- ishidden = dir_access->current_is_hidden();
+ is_hidden = dir_access->current_is_hidden();
- if (show_hidden || !ishidden) {
- if (!isdir)
+ if (show_hidden_files || !is_hidden) {
+ if (!is_dir)
files.push_back(item);
else
dirs.push_back(item);
@@ -873,6 +873,13 @@ FileDialog::FileDialog() {
refresh->connect("pressed", this, "_update_file_list");
hbc->add_child(refresh);
+ show_hidden = memnew(ToolButton);
+ show_hidden->set_toggle_mode(true);
+ show_hidden->set_pressed(is_showing_hidden_files());
+ show_hidden->set_tooltip(RTR("Toggle Hidden Files"));
+ show_hidden->connect("toggled", this, "set_show_hidden_files");
+ hbc->add_child(show_hidden);
+
drives = memnew(OptionButton);
hbc->add_child(drives);
drives->connect("item_selected", this, "_select_drive");
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 85edac0b12..9f7ea1a2f2 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -90,6 +90,7 @@ private:
ToolButton *dir_up;
ToolButton *refresh;
+ ToolButton *show_hidden;
Vector<String> filters;
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index cfbc9d6d18..75f5f79873 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -29,10 +29,17 @@
/*************************************************************************/
#include "gradient_edit.h"
+
#include "core/os/keyboard.h"
-#include "editor/editor_scale.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_scale.h"
#define SPACING (3 * EDSCALE)
+#define POINT_WIDTH (8 * EDSCALE)
+#else
+#define SPACING 3
+#define POINT_WIDTH 8
+#endif
GradientEdit::GradientEdit() {
grabbed = -1;
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 662278a17b..6f31107729 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -36,8 +36,6 @@
#include "scene/resources/default_theme/theme_data.h"
#include "scene/resources/gradient.h"
-#define POINT_WIDTH (8 * EDSCALE)
-
class GradientEdit : public Control {
GDCLASS(GradientEdit, Control);
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index e8692d56d2..6463ee5ad5 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -159,9 +159,7 @@ void GraphNode::_resort() {
fit_child_in_rect(c, r);
cache_y.push_back(vofs + size.y * 0.5);
- if (vofs > 0)
- vofs += sep;
- vofs += size.y;
+ vofs += size.y + sep;
}
update();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 406c8e4f48..91b76839d7 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -749,9 +749,21 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
search_string = "";
}
- search_string += String::chr(k->get_unicode());
- for (int i = 0; i < items.size(); i++) {
- if (items[i].text.begins_with(search_string)) {
+ if (String::chr(k->get_unicode()) != search_string)
+ search_string += String::chr(k->get_unicode());
+
+ for (int i = current + 1; i <= items.size(); i++) {
+ if (i == items.size()) {
+ if (current == 0)
+ break;
+ else
+ i = 0;
+ }
+
+ if (i == current)
+ break;
+
+ if (items[i].text.findn(search_string) == 0) {
set_current(i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -1248,7 +1260,7 @@ int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const {
Rect2 rc = items[i].rect_cache;
if (i % current_columns == current_columns - 1) {
- rc.size.width = get_size().width; //not right but works
+ rc.size.width = get_size().width - rc.position.x; //make sure you can still select the last item when clicking past the column
}
if (rc.has_point(pos)) {
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 1f778bc516..da6bff8ab8 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -44,7 +44,7 @@
static bool _is_text_char(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+ return !is_symbol(c);
}
void LineEdit::_gui_input(Ref<InputEvent> p_event) {
@@ -539,7 +539,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (handled) {
accept_event();
- } else if (!k->get_alt() && !k->get_command()) {
+ } else if (!k->get_command()) {
if (k->get_unicode() >= 32 && k->get_scancode() != KEY_DELETE) {
if (editable) {
@@ -923,7 +923,8 @@ void LineEdit::cut_text() {
void LineEdit::paste_text() {
- String paste_buffer = OS::get_singleton()->get_clipboard();
+ // Strip escape characters like \n and \t as they can't be displayed on LineEdit.
+ String paste_buffer = OS::get_singleton()->get_clipboard().strip_escapes();
if (paste_buffer != "") {
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 694065497a..7027fceb84 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -354,6 +354,7 @@ OptionButton::OptionButton() {
add_child(popup);
popup->set_pass_on_modal_close_click(false);
popup->set_notify_transform(true);
+ popup->set_allow_search(true);
popup->connect("id_pressed", this, "_selected");
popup->connect("id_focused", this, "_focused");
popup->connect("popup_hide", this, "set_pressed", varray(false));
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 984307530d..2cac345dae 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -31,6 +31,7 @@
#include "popup_menu.h"
#include "core/os/input.h"
#include "core/os/keyboard.h"
+#include "core/os/os.h"
#include "core/print_string.h"
#include "core/translation.h"
@@ -380,6 +381,43 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
_scroll(-pan_gesture->get_delta().y, pan_gesture->get_position());
}
}
+
+ Ref<InputEventKey> k = p_event;
+
+ if (allow_search && k.is_valid() && k->get_unicode()) {
+
+ uint64_t now = OS::get_singleton()->get_ticks_msec();
+ uint64_t diff = now - search_time_msec;
+ uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000));
+ search_time_msec = now;
+
+ if (diff > max_interval) {
+ search_string = "";
+ }
+
+ if (String::chr(k->get_unicode()) != search_string)
+ search_string += String::chr(k->get_unicode());
+
+ for (int i = mouse_over + 1; i <= items.size(); i++) {
+ if (i == items.size()) {
+ if (mouse_over <= 0)
+ break;
+ else
+ i = 0;
+ }
+
+ if (i == mouse_over)
+ break;
+
+ if (items[i].text.findn(search_string) == 0) {
+ mouse_over = i;
+ emit_signal("id_focused", i);
+ update();
+ accept_event();
+ break;
+ }
+ }
+ }
}
bool PopupMenu::has_point(const Point2 &p_point) const {
@@ -1289,6 +1327,16 @@ float PopupMenu::get_submenu_popup_delay() const {
return submenu_timer->get_wait_time();
}
+void PopupMenu::set_allow_search(bool p_allow) {
+
+ allow_search = p_allow;
+}
+
+bool PopupMenu::get_allow_search() const {
+
+ return allow_search;
+}
+
void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) {
hide_on_window_lose_focus = p_enabled;
@@ -1407,6 +1455,9 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus);
ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus);
+ ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search);
+ ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search);
+
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
@@ -1414,6 +1465,7 @@ void PopupMenu::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "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_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
@@ -1435,6 +1487,10 @@ PopupMenu::PopupMenu() {
initial_button_mask = 0;
during_grabbed_click = false;
+ allow_search = false;
+ search_time_msec = 0;
+ search_string = "";
+
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
set_hide_on_item_selection(true);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 687006d58d..babdd21281 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -112,6 +112,10 @@ class PopupMenu : public Popup {
void _ref_shortcut(Ref<ShortCut> p_sc);
void _unref_shortcut(Ref<ShortCut> p_sc);
+ bool allow_search;
+ uint64_t search_time_msec;
+ String search_string;
+
protected:
virtual bool has_point(const Point2 &p_point) const;
@@ -206,6 +210,9 @@ public:
void set_submenu_popup_delay(float p_time);
float get_submenu_popup_delay() const;
+ void set_allow_search(bool p_allow);
+ bool get_allow_search() const;
+
virtual void popup(const Rect2 &p_bounds = Rect2());
void set_hide_on_window_lose_focus(bool p_enabled);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index da452e3f10..8891f679ee 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -307,6 +307,13 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
switch (it->type) {
+ case ITEM_ALIGN: {
+
+ ItemAlign *align_it = static_cast<ItemAlign *>(it);
+
+ align = align_it->align;
+
+ } break;
case ITEM_TEXT: {
ItemText *text = static_cast<ItemText *>(it);
@@ -592,7 +599,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
//assign actual widths
for (int i = 0; i < table->columns.size(); i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand)
+ if (table->columns[i].expand && total_ratio > 0)
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
table->total_width += table->columns[i].width + hseparation;
}
@@ -1411,9 +1418,13 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
if (p_enter)
current = p_item;
- if (p_ensure_newline && current_frame->lines[current_frame->lines.size() - 1].from) {
- _invalidate_current_line(current_frame);
- current_frame->lines.resize(current_frame->lines.size() + 1);
+ if (p_ensure_newline) {
+ Item *from = current_frame->lines[current_frame->lines.size() - 1].from;
+ // only create a new line for Item types that generate content/layout, ignore those that represent formatting/styling
+ if (from && from->type != ITEM_FONT && from->type != ITEM_COLOR && from->type != ITEM_UNDERLINE && from->type != ITEM_STRIKETHROUGH) {
+ _invalidate_current_line(current_frame);
+ current_frame->lines.resize(current_frame->lines.size() + 1);
+ }
}
if (current_frame->lines[current_frame->lines.size() - 1].from == NULL) {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 3117d8c59f..83ce71e959 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -50,7 +50,7 @@ inline bool _is_symbol(CharType c) {
static bool _is_text_char(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+ return !is_symbol(c);
}
static bool _is_whitespace(CharType c) {
@@ -4078,7 +4078,7 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_
cursor.line = p_row;
int n_col = get_char_pos_for_line(cursor.last_fit_x, p_row, p_wrap_index);
- if (is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) {
+ if (n_col != 0 && is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) {
Vector<String> rows = get_wrap_rows_text(p_row);
int row_end_col = 0;
for (int i = 0; i < p_wrap_index + 1; i++) {
@@ -4682,6 +4682,8 @@ bool TextEdit::has_keyword_color(String p_keyword) const {
}
Color TextEdit::get_keyword_color(String p_keyword) const {
+
+ ERR_FAIL_COND_V(!keywords.has(p_keyword), Color());
return keywords[p_keyword];
}
@@ -6342,14 +6344,14 @@ int TextEdit::get_info_gutter_width() const {
return info_gutter_width;
}
-void TextEdit::set_hiding_enabled(int p_enabled) {
+void TextEdit::set_hiding_enabled(bool p_enabled) {
if (!p_enabled)
unhide_all_lines();
hiding_enabled = p_enabled;
update();
}
-int TextEdit::is_hiding_enabled() const {
+bool TextEdit::is_hiding_enabled() const {
return hiding_enabled;
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 68e590f1e6..0c26602d2b 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -697,8 +697,8 @@ public:
void set_info_gutter_width(int p_gutter_width);
int get_info_gutter_width() const;
- void set_hiding_enabled(int p_enabled);
- int is_hiding_enabled() const;
+ void set_hiding_enabled(bool p_enabled);
+ bool is_hiding_enabled() const;
void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index e5313061da..522c1ecb6a 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -39,7 +39,7 @@
#include "scene/main/viewport.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
#endif
#include <limits.h>
@@ -318,7 +318,7 @@ void TreeItem::set_custom_draw(int p_column, Object *p_object, const StringName
void TreeItem::set_collapsed(bool p_collapsed) {
- if (collapsed == p_collapsed)
+ if (collapsed == p_collapsed || !tree)
return;
collapsed = p_collapsed;
TreeItem *ci = tree->selected_item;
@@ -344,8 +344,7 @@ void TreeItem::set_collapsed(bool p_collapsed) {
}
_changed_notify();
- if (tree)
- tree->emit_signal("item_collapsed", this);
+ tree->emit_signal("item_collapsed", this);
}
bool TreeItem::is_collapsed() {
@@ -389,7 +388,7 @@ TreeItem *TreeItem::get_children() {
return children;
}
-TreeItem *TreeItem::get_prev_visible() {
+TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
TreeItem *current = this;
@@ -398,8 +397,20 @@ TreeItem *TreeItem::get_prev_visible() {
if (!prev) {
current = current->parent;
- if (!current || (current == tree->root && tree->hide_root))
+ if (current == tree->root && tree->hide_root) {
return NULL;
+ } else if (!current) {
+ if (p_wrap) {
+ current = this;
+ TreeItem *temp = this->get_next_visible();
+ while (temp) {
+ current = temp;
+ temp = temp->get_next_visible();
+ }
+ } else {
+ return NULL;
+ }
+ }
} else {
current = prev;
@@ -415,7 +426,7 @@ TreeItem *TreeItem::get_prev_visible() {
return current;
}
-TreeItem *TreeItem::get_next_visible() {
+TreeItem *TreeItem::get_next_visible(bool p_wrap) {
TreeItem *current = this;
@@ -433,10 +444,14 @@ TreeItem *TreeItem::get_next_visible() {
current = current->parent;
}
- if (current == NULL)
- return NULL;
- else
+ if (!current) {
+ if (p_wrap)
+ return tree->root;
+ else
+ return NULL;
+ } else {
current = current->next;
+ }
}
return current;
@@ -740,8 +755,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent);
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
- ClassDB::bind_method(D_METHOD("get_next_visible"), &TreeItem::get_next_visible);
- ClassDB::bind_method(D_METHOD("get_prev_visible"), &TreeItem::get_prev_visible);
+ ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
@@ -3491,6 +3506,7 @@ void Tree::scroll_to_item(TreeItem *p_item) {
TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) {
+ TreeItem *from = p_at;
while (p_at) {
for (int i = 0; i < columns.size(); i++) {
@@ -3502,9 +3518,12 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
}
if (p_backwards)
- p_at = p_at->get_prev_visible();
+ p_at = p_at->get_prev_visible(true);
else
- p_at = p_at->get_next_visible();
+ p_at = p_at->get_next_visible(true);
+
+ if ((p_at) == from)
+ break;
}
return NULL;
@@ -3512,10 +3531,14 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
TreeItem *Tree::search_item_text(const String &p_find, int *r_col, bool p_selectable) {
- if (!root)
+ TreeItem *from = get_selected();
+
+ if (!from)
+ from = root;
+ if (!from)
return NULL;
- return _search_item_text(root, p_find, r_col, p_selectable);
+ return _search_item_text(from->get_next_visible(true), p_find, r_col, p_selectable);
}
void Tree::_do_incr_search(const String &p_add) {
@@ -3524,7 +3547,7 @@ void Tree::_do_incr_search(const String &p_add) {
uint64_t diff = time - last_keypress;
if (diff > uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000)))
incr_search = p_add;
- else
+ else if (incr_search != p_add)
incr_search += p_add;
last_keypress = time;
@@ -3697,6 +3720,10 @@ String Tree::get_tooltip(const Point2 &p_pos) const {
const TreeItem::Cell &c = it->cells[col];
int col_width = get_column_width(col);
+
+ for (int i = 0; i < col; i++)
+ pos.x -= get_column_width(i);
+
for (int j = c.buttons.size() - 1; j >= 0; j--) {
Ref<Texture> b = c.buttons[j].texture;
Size2 size = b->get_size() + cache.button_pressed->get_minimum_size();
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 26d64baafb..45d451eb72 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -238,8 +238,8 @@ public:
TreeItem *get_parent();
TreeItem *get_children();
- TreeItem *get_prev_visible();
- TreeItem *get_next_visible();
+ TreeItem *get_prev_visible(bool p_wrap = false);
+ TreeItem *get_next_visible(bool p_wrap = false);
void remove_child(TreeItem *p_item);
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 2f23c11748..ee23b24b3c 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1165,7 +1165,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) {
ERR_FAIL_NULL(p_child);
if (p_child == this) {
- ERR_EXPLAIN("Can't add child '" + p_child->get_name() + "' to itself.")
+ ERR_EXPLAIN("Can't add child '" + p_child->get_name() + "' to itself.");
ERR_FAIL_COND(p_child == this); // adding to itself!
}
@@ -1199,7 +1199,7 @@ void Node::add_child_below_node(Node *p_node, Node *p_child, bool p_legible_uniq
if (is_a_parent_of(p_node)) {
move_child(p_child, p_node->get_position_in_parent() + 1);
} else {
- WARN_PRINTS("Cannot move under node " + p_node->get_name() + " as " + p_child->get_name() + " does not share a parent.")
+ WARN_PRINTS("Cannot move under node " + p_node->get_name() + " as " + p_child->get_name() + " does not share a parent.");
}
}
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 65cda73a23..7f0cebd492 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1244,7 +1244,7 @@ void SceneTree::_update_root_rect() {
root->update_canvas_items(); //force them to update just in case
if (use_font_oversampling) {
- WARN_PRINT("Font oversampling does not work in 'Viewport' stretch mode, only '2D'.")
+ WARN_PRINT("Font oversampling does not work in 'Viewport' stretch mode, only '2D'.");
}
} break;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 5440c88fa8..0423fcb5f0 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -48,6 +48,7 @@
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/line_2d.h"
#include "scene/2d/mesh_instance_2d.h"
+#include "scene/2d/multimesh_instance_2d.h"
#include "scene/2d/navigation_2d.h"
#include "scene/2d/parallax_background.h"
#include "scene/2d/parallax_layer.h"
@@ -144,7 +145,6 @@
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "scene/resources/mesh_data_tool.h"
-#include "scene/resources/mesh_library.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/particles_material.h"
#include "scene/resources/physics_material.h"
@@ -209,6 +209,7 @@
#include "scene/3d/visibility_notifier.h"
#include "scene/animation/skeleton_ik.h"
#include "scene/resources/environment.h"
+#include "scene/resources/mesh_library.h"
#endif
static Ref<ResourceFormatSaverText> resource_saver_text;
@@ -468,7 +469,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
#endif
- ClassDB::register_class<MeshLibrary>();
+
AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel())));
ClassDB::register_class<Shader>();
@@ -543,6 +544,7 @@ void register_scene_types() {
ClassDB::register_class<Position2D>();
ClassDB::register_class<Line2D>();
ClassDB::register_class<MeshInstance2D>();
+ ClassDB::register_class<MultiMeshInstance2D>();
ClassDB::register_virtual_class<CollisionObject2D>();
ClassDB::register_virtual_class<PhysicsBody2D>();
ClassDB::register_class<StaticBody2D>();
@@ -587,9 +589,13 @@ void register_scene_types() {
SceneTree::add_idle_callback(ParticlesMaterial::flush_changes);
ParticlesMaterial::init_shaders();
-#ifndef _3D_DISABLED
ClassDB::register_virtual_class<Mesh>();
ClassDB::register_class<ArrayMesh>();
+ ClassDB::register_class<MultiMesh>();
+ ClassDB::register_class<SurfaceTool>();
+ ClassDB::register_class<MeshDataTool>();
+
+#ifndef _3D_DISABLED
ClassDB::register_virtual_class<PrimitiveMesh>();
ClassDB::register_class<CapsuleMesh>();
ClassDB::register_class<CubeMesh>();
@@ -603,7 +609,6 @@ void register_scene_types() {
SceneTree::add_idle_callback(SpatialMaterial::flush_changes);
SpatialMaterial::init_shaders();
- ClassDB::register_class<MultiMesh>();
ClassDB::register_class<MeshLibrary>();
OS::get_singleton()->yield(); //may take time to init
@@ -619,9 +624,6 @@ void register_scene_types() {
ClassDB::register_class<ConvexPolygonShape>();
ClassDB::register_class<ConcavePolygonShape>();
- ClassDB::register_class<SurfaceTool>();
- ClassDB::register_class<MeshDataTool>();
-
OS::get_singleton()->yield(); //may take time to init
ClassDB::register_class<SpatialVelocityTracker>();
@@ -644,6 +646,7 @@ void register_scene_types() {
ClassDB::register_class<GradientTexture>();
ClassDB::register_class<ProxyTexture>();
ClassDB::register_class<AnimatedTexture>();
+ ClassDB::register_class<CameraTexture>();
ClassDB::register_class<CubeMap>();
ClassDB::register_virtual_class<TextureLayered>();
ClassDB::register_class<Texture3D>();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 6ab78326b2..e4da659b0d 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -93,7 +93,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]);
PoolVector<float> values = p_value;
int vcount = values.size();
- ERR_FAIL_COND_V(vcount % 12, false); // shuld be multiple of 11
+ ERR_FAIL_COND_V(vcount % 12, false); // should be multiple of 11
PoolVector<float>::Read r = values.read();
@@ -1298,6 +1298,78 @@ float Animation::track_get_key_time(int p_track, int p_key_idx) const {
ERR_FAIL_V(-1);
}
+void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ Track *t = tracks[p_track];
+
+ switch (t->type) {
+
+ case TYPE_TRANSFORM: {
+
+ TransformTrack *tt = static_cast<TransformTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
+ TKey<TransformKey> key = tt->transforms[p_key_idx];
+ key.time = p_time;
+ tt->transforms.remove(p_key_idx);
+ _insert(p_time, tt->transforms, key);
+ return;
+ }
+ case TYPE_VALUE: {
+
+ ValueTrack *vt = static_cast<ValueTrack *>(t);
+ 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);
+ _insert(p_time, vt->values, key);
+ return;
+ }
+ case TYPE_METHOD: {
+
+ MethodTrack *mt = static_cast<MethodTrack *>(t);
+ 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);
+ _insert(p_time, mt->methods, key);
+ return;
+ }
+ case TYPE_BEZIER: {
+
+ BezierTrack *bt = static_cast<BezierTrack *>(t);
+ 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);
+ _insert(p_time, bt->values, key);
+ return;
+ }
+ case TYPE_AUDIO: {
+
+ AudioTrack *at = static_cast<AudioTrack *>(t);
+ 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);
+ _insert(p_time, at->values, key);
+ return;
+ }
+ case TYPE_ANIMATION: {
+
+ AnimationTrack *at = static_cast<AnimationTrack *>(t);
+ 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);
+ _insert(p_time, at->values, key);
+ return;
+ }
+ }
+
+ ERR_FAIL();
+}
+
float Animation::track_get_key_transition(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
@@ -2691,6 +2763,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "idx", "position"), &Animation::track_remove_key_at_position);
ClassDB::bind_method(D_METHOD("track_set_key_value", "idx", "key", "value"), &Animation::track_set_key_value);
ClassDB::bind_method(D_METHOD("track_set_key_transition", "idx", "key_idx", "transition"), &Animation::track_set_key_transition);
+ ClassDB::bind_method(D_METHOD("track_set_key_time", "idx", "key_idx", "time"), &Animation::track_set_key_time);
ClassDB::bind_method(D_METHOD("track_get_key_transition", "idx", "key_idx"), &Animation::track_get_key_transition);
ClassDB::bind_method(D_METHOD("track_get_key_count", "idx"), &Animation::track_get_key_count);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 36ebaa25d5..59f2ae24c7 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -305,6 +305,7 @@ public:
void track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition = 1);
void track_set_key_transition(int p_track, int p_key_idx, float p_transition);
void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
+ void track_set_key_time(int p_track, int p_key_idx, float p_time);
int track_find_key(int p_track, float p_time, bool p_exact = false) const;
void track_remove_key(int p_track, int p_idx);
void track_remove_key_at_position(int p_track, float p_pos);
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index bdb6c78782..2c5dfc375c 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -541,8 +541,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// File Dialog
- theme->set_icon("reload", "FileDialog", make_icon(icon_reload_png));
theme->set_icon("parent_folder", "FileDialog", make_icon(icon_parent_folder_png));
+ theme->set_icon("reload", "FileDialog", make_icon(icon_reload_png));
+ theme->set_icon("toggle_hidden", "FileDialog", make_icon(icon_visibility_png));
// Popup
@@ -582,14 +583,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// GraphNode
- Ref<StyleBoxTexture> graphsb = make_stylebox(graph_node_png, 6, 24, 6, 5, 16, 24, 16, 5);
- Ref<StyleBoxTexture> graphsbcomment = make_stylebox(graph_node_comment_png, 6, 24, 6, 5, 16, 24, 16, 5);
- Ref<StyleBoxTexture> graphsbcommentselected = make_stylebox(graph_node_comment_focus_png, 6, 24, 6, 5, 16, 24, 16, 5);
- Ref<StyleBoxTexture> graphsbselected = make_stylebox(graph_node_selected_png, 6, 24, 6, 5, 16, 24, 16, 5);
+ 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, 5);
- Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 5);
+ 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(MARGIN_LEFT,10);
//graphsb->set_expand_margin_size(MARGIN_RIGHT,10);
diff --git a/scene/resources/default_theme/icon_visibility.png b/scene/resources/default_theme/icon_visibility.png
new file mode 100644
index 0000000000..6402571f3e
--- /dev/null
+++ b/scene/resources/default_theme/icon_visibility.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index 87b8afd6a3..5e13a6625a 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -194,6 +194,10 @@ static const unsigned char icon_stop_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, 0x20, 0x2, 0x7c, 0x60, 0x26, 0x28, 0xf3, 0xf0, 0x3f, 0x76, 0x8, 0x94, 0xa2, 0x97, 0x82, 0x51, 0x5, 0x84, 0x23, 0x8b, 0x30, 0x0, 0x0, 0x66, 0x60, 0x11, 0xdc, 0x92, 0xb3, 0xb7, 0xe7, 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, 0x3, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0xdb, 0xe1, 0x4f, 0xe0, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 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, 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, 0x86, 0x49, 0x44, 0x41, 0x54, 0x18, 0x19, 0x8d, 0xc1, 0xb, 0x16, 0x42, 0x40, 0x0, 0x86, 0xd1, 0x2f, 0xa2, 0x77, 0x2a, 0x85, 0xde, 0x91, 0x5e, 0x33, 0x8a, 0x7f, 0xff, 0x9b, 0xcb, 0x99, 0x63, 0x1, 0xee, 0xa5, 0x9f, 0xc1, 0x3e, 0x6f, 0xea, 0xfc, 0xe0, 0xd1, 0x99, 0xdd, 0xe5, 0x94, 0xb, 0x9c, 0xf9, 0x57, 0x26, 0x9, 0x82, 0x5d, 0xa1, 0xdf, 0x92, 0x96, 0xf7, 0x94, 0x99, 0xd0, 0x1a, 0x1b, 0x7d, 0x7c, 0xe0, 0x2c, 0x25, 0x6c, 0x2b, 0x1b, 0x93, 0x4a, 0x17, 0xa0, 0x94, 0x2, 0xac, 0x64, 0x8, 0xa5, 0x12, 0x78, 0x48, 0x1, 0x56, 0x32, 0x8c, 0xa4, 0x7, 0x70, 0x93, 0x76, 0xc4, 0xd6, 0x6c, 0xc8, 0xa4, 0x2b, 0x30, 0xac, 0x54, 0x8c, 0x69, 0x4d, 0xad, 0xcc, 0x90, 0xd6, 0xba, 0x91, 0x49, 0xc3, 0x30, 0xb3, 0x6a, 0x22, 0x9c, 0xd5, 0x5b, 0xce, 0x2b, 0xa2, 0xe3, 0x9f, 0xf2, 0xba, 0xce, 0x8f, 0x3e, 0xbd, 0xfc, 0x1, 0xdb, 0xf3, 0x10, 0xc5, 0x78, 0x85, 0x14, 0x89, 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
};
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 52dfffda5b..7c3867beaa 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -111,6 +111,11 @@ void Environment::set_ambient_light_sky_contribution(float p_energy) {
VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, ambient_energy, ambient_sky_contribution);
}
+void Environment::set_camera_feed_id(int p_camera_feed_id) {
+ camera_feed_id = p_camera_feed_id;
+ VS::get_singleton()->environment_set_camera_feed_id(environment, camera_feed_id);
+};
+
Environment::BGMode Environment::get_background() const {
return bg_mode;
@@ -165,6 +170,10 @@ float Environment::get_ambient_light_sky_contribution() const {
return ambient_sky_contribution;
}
+int Environment::get_camera_feed_id(void) const {
+
+ return camera_feed_id;
+}
void Environment::set_tonemapper(ToneMapper p_tone_mapper) {
@@ -321,6 +330,12 @@ void Environment::_validate_property(PropertyInfo &property) const {
}
}
+ if (property.name == "background_camera_feed_id") {
+ if (bg_mode != BG_CAMERA_FEED) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+ }
+
static const char *hide_prefixes[] = {
"fog_",
"auto_exposure_",
@@ -946,6 +961,7 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_ambient_light_color", "color"), &Environment::set_ambient_light_color);
ClassDB::bind_method(D_METHOD("set_ambient_light_energy", "energy"), &Environment::set_ambient_light_energy);
ClassDB::bind_method(D_METHOD("set_ambient_light_sky_contribution", "energy"), &Environment::set_ambient_light_sky_contribution);
+ ClassDB::bind_method(D_METHOD("set_camera_feed_id", "camera_feed_id"), &Environment::set_camera_feed_id);
ClassDB::bind_method(D_METHOD("get_background"), &Environment::get_background);
ClassDB::bind_method(D_METHOD("get_sky"), &Environment::get_sky);
@@ -959,9 +975,10 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_ambient_light_color"), &Environment::get_ambient_light_color);
ClassDB::bind_method(D_METHOD("get_ambient_light_energy"), &Environment::get_ambient_light_energy);
ClassDB::bind_method(D_METHOD("get_ambient_light_sky_contribution"), &Environment::get_ambient_light_sky_contribution);
+ ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &Environment::get_camera_feed_id);
ADD_GROUP("Background", "background_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Color+Sky,Canvas,Keep"), "set_background", "get_background");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Color+Sky,Canvas,Keep,Camera Feed"), "set_background", "get_background");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "background_sky", PROPERTY_HINT_RESOURCE_TYPE, "Sky"), "set_sky", "get_sky");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "background_sky_custom_fov", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_sky_custom_fov", "get_sky_custom_fov");
ADD_PROPERTY(PropertyInfo(Variant::BASIS, "background_sky_orientation"), "set_sky_orientation", "get_sky_orientation");
@@ -970,6 +987,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background_color"), "set_bg_color", "get_bg_color");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "background_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_bg_energy", "get_bg_energy");
ADD_PROPERTY(PropertyInfo(Variant::INT, "background_canvas_max_layer", PROPERTY_HINT_RANGE, "-1000,1000,1"), "set_canvas_max_layer", "get_canvas_max_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "background_camera_feed_id", PROPERTY_HINT_RANGE, "1,10,1"), "set_camera_feed_id", "get_camera_feed_id");
ADD_GROUP("Ambient Light", "ambient_light_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_color"), "set_ambient_light_color", "get_ambient_light_color");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ambient_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_light_energy", "get_ambient_light_energy");
@@ -1265,6 +1283,7 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(BG_SKY);
BIND_ENUM_CONSTANT(BG_COLOR_SKY);
BIND_ENUM_CONSTANT(BG_CANVAS);
+ BIND_ENUM_CONSTANT(BG_CAMERA_FEED);
BIND_ENUM_CONSTANT(BG_MAX);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_ADDITIVE);
@@ -1310,6 +1329,7 @@ Environment::Environment() :
ambient_energy = 1.0;
//ambient_sky_contribution = 1.0;
set_ambient_light_sky_contribution(1.0);
+ set_camera_feed_id(1);
tone_mapper = TONE_MAPPER_LINEAR;
tonemap_exposure = 1.0;
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index a54f13a88f..acce9c09a2 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -49,6 +49,7 @@ public:
BG_COLOR_SKY,
BG_CANVAS,
BG_KEEP,
+ BG_CAMERA_FEED,
BG_MAX
};
@@ -98,6 +99,7 @@ private:
Color ambient_color;
float ambient_energy;
float ambient_sky_contribution;
+ int camera_feed_id;
ToneMapper tone_mapper;
float tonemap_exposure;
@@ -192,6 +194,7 @@ public:
void set_ambient_light_color(const Color &p_color);
void set_ambient_light_energy(float p_energy);
void set_ambient_light_sky_contribution(float p_energy);
+ void set_camera_feed_id(int p_camera_feed_id);
BGMode get_background() const;
Ref<Sky> get_sky() const;
@@ -205,6 +208,7 @@ public:
Color get_ambient_light_color() const;
float get_ambient_light_energy() const;
float get_ambient_light_sky_contribution() const;
+ int get_camera_feed_id(void) const;
void set_tonemapper(ToneMapper p_tone_mapper);
ToneMapper get_tonemapper() const;
diff --git a/scene/resources/height_map_shape.cpp b/scene/resources/height_map_shape.cpp
index 8cd271dab0..f763700d52 100644
--- a/scene/resources/height_map_shape.cpp
+++ b/scene/resources/height_map_shape.cpp
@@ -85,6 +85,7 @@ void HeightMapShape::_update_shape() {
d["min_height"] = min_height;
d["max_height"] = max_height;
PhysicsServer::get_singleton()->shape_set_data(get_shape(), d);
+ Shape::_update_shape();
}
void HeightMapShape::set_map_width(int p_new) {
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 67639858ce..6443b44bb6 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -489,6 +489,7 @@ void Mesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_surface_count"), &Mesh::get_surface_count);
ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &Mesh::surface_get_arrays);
ClassDB::bind_method(D_METHOD("surface_get_blend_shape_arrays", "surf_idx"), &Mesh::surface_get_blend_shape_arrays);
+ ClassDB::bind_method(D_METHOD("surface_set_material", "surf_idx", "material"), &Mesh::surface_set_material);
ClassDB::bind_method(D_METHOD("surface_get_material", "surf_idx"), &Mesh::surface_get_material);
BIND_ENUM_CONSTANT(PRIMITIVE_POINTS);
@@ -1305,7 +1306,6 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("surface_get_array_index_len", "surf_idx"), &ArrayMesh::surface_get_array_index_len);
ClassDB::bind_method(D_METHOD("surface_get_format", "surf_idx"), &ArrayMesh::surface_get_format);
ClassDB::bind_method(D_METHOD("surface_get_primitive_type", "surf_idx"), &ArrayMesh::surface_get_primitive_type);
- ClassDB::bind_method(D_METHOD("surface_set_material", "surf_idx", "material"), &ArrayMesh::surface_set_material);
ClassDB::bind_method(D_METHOD("surface_find_by_name", "name"), &ArrayMesh::surface_find_by_name);
ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name);
ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name);
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 1457d283bd..b38791b9a6 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -128,6 +128,7 @@ public:
virtual Array surface_get_blend_shape_arrays(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;
@@ -215,8 +216,8 @@ public:
PrimitiveType surface_get_primitive_type(int p_idx) const;
bool surface_is_alpha_sorting_enabled(int p_idx) const;
- void surface_set_material(int p_idx, const Ref<Material> &p_material);
- Ref<Material> surface_get_material(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;
int surface_find_by_name(const String &p_name) const;
void surface_set_name(int p_idx, const String &p_name);
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 2c6f30f429..99286668ce 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -92,8 +92,8 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
if (i > 0) {
- ERR_EXPLAIN(vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name]))
- ERR_FAIL_COND_V(n.parent == -1, NULL)
+ ERR_EXPLAIN(vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name]));
+ ERR_FAIL_COND_V(n.parent == -1, NULL);
NODE_FROM_ID(nparent, n.parent);
#ifdef DEBUG_ENABLED
if (!nparent && (n.parent & FLAG_ID_IS_PATH)) {
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index ef67e6ea80..758475b75e 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -1186,6 +1186,7 @@ void ParticlesMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
BIND_ENUM_CONSTANT(FLAG_ROTATE_Y);
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_Z);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index db58fe7823..04d13c8869 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -157,6 +157,12 @@ Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const {
return primitive_type;
}
+void PrimitiveMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
+ ERR_FAIL_INDEX(p_idx, 1);
+
+ set_material(p_material);
+}
+
Ref<Material> PrimitiveMesh::surface_get_material(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, 1, NULL);
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 88a26801b7..0045a48736 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -72,6 +72,7 @@ public:
virtual Array surface_get_blend_shape_arrays(int p_surface) const;
virtual uint32_t surface_get_format(int p_idx) const;
virtual Mesh::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;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 6e7bb27e74..0921c0dae9 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1712,15 +1712,15 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
Vector<StringName> groups = state->get_node_groups(i);
String header = "[node";
- header += " name=\"" + String(name) + "\"";
+ header += " name=\"" + String(name).c_escape() + "\"";
if (type != StringName()) {
header += " type=\"" + String(type) + "\"";
}
if (path != NodePath()) {
- header += " parent=\"" + String(path.simplified()) + "\"";
+ header += " parent=\"" + String(path.simplified()).c_escape() + "\"";
}
if (owner != NodePath() && owner != NodePath(".")) {
- header += " owner=\"" + String(owner.simplified()) + "\"";
+ header += " owner=\"" + String(owner.simplified()).c_escape() + "\"";
}
if (index >= 0) {
header += " index=\"" + itos(index) + "\"";
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 92172912c2..6e0bc43296 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -36,6 +36,7 @@
#include "core/os/os.h"
#include "mesh.h"
#include "scene/resources/bit_map.h"
+#include "servers/camera/camera_feed.h"
Size2 Texture::get_size() const {
@@ -232,7 +233,7 @@ Image::Format ImageTexture::get_format() const {
#ifndef DISABLE_DEPRECATED
Error ImageTexture::load(const String &p_path) {
- WARN_DEPRECATED
+ WARN_DEPRECATED;
Ref<Image> img;
img.instance();
Error err = img->load(p_path);
@@ -2498,3 +2499,107 @@ String ResourceFormatLoaderTextureLayered::get_resource_type(const String &p_pat
return "TextureArray";
return "";
}
+
+void CameraTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_camera_feed_id", "feed_id"), &CameraTexture::set_camera_feed_id);
+ ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &CameraTexture::get_camera_feed_id);
+
+ ClassDB::bind_method(D_METHOD("set_which_feed", "which_feed"), &CameraTexture::set_which_feed);
+ ClassDB::bind_method(D_METHOD("get_which_feed"), &CameraTexture::get_which_feed);
+
+ ClassDB::bind_method(D_METHOD("set_camera_active", "active"), &CameraTexture::set_camera_active);
+ ClassDB::bind_method(D_METHOD("get_camera_active"), &CameraTexture::get_camera_active);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "camera_feed_id"), "set_camera_feed_id", "get_camera_feed_id");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "which_feed"), "set_which_feed", "get_which_feed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_is_active"), "set_camera_active", "get_camera_active");
+}
+
+int CameraTexture::get_width() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_base_width();
+ } else {
+ return 0;
+ }
+}
+
+int CameraTexture::get_height() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_base_height();
+ } else {
+ return 0;
+ }
+}
+
+bool CameraTexture::has_alpha() const {
+ return false;
+}
+
+RID CameraTexture::get_rid() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_texture(which_feed);
+ } else {
+ return RID();
+ }
+}
+
+void CameraTexture::set_flags(uint32_t p_flags) {
+ // not supported
+}
+
+uint32_t CameraTexture::get_flags() const {
+ // not supported
+ return 0;
+}
+
+Ref<Image> CameraTexture::get_data() const {
+ // not (yet) supported
+ return Ref<Image>();
+}
+
+void CameraTexture::set_camera_feed_id(int p_new_id) {
+ camera_feed_id = p_new_id;
+ _change_notify();
+}
+
+int CameraTexture::get_camera_feed_id() const {
+ return camera_feed_id;
+}
+
+void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) {
+ which_feed = p_which;
+ _change_notify();
+}
+
+CameraServer::FeedImage CameraTexture::get_which_feed() const {
+ return which_feed;
+}
+
+void CameraTexture::set_camera_active(bool p_active) {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ feed->set_active(p_active);
+ _change_notify();
+ }
+}
+
+bool CameraTexture::get_camera_active() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->is_active();
+ } else {
+ return false;
+ }
+}
+
+CameraTexture::CameraTexture() {
+ camera_feed_id = 0;
+ which_feed = CameraServer::FEED_RGBA_IMAGE;
+}
+
+CameraTexture::~CameraTexture() {
+ // nothing to do here yet
+}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 58287b7593..021673f072 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -39,6 +39,7 @@
#include "core/resource.h"
#include "scene/resources/curve.h"
#include "scene/resources/gradient.h"
+#include "servers/camera_server.h"
#include "servers/visual_server.h"
/**
@@ -740,4 +741,38 @@ public:
~AnimatedTexture();
};
+class CameraTexture : public Texture {
+ GDCLASS(CameraTexture, Texture)
+
+private:
+ int camera_feed_id;
+ CameraServer::FeedImage which_feed;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_width() const;
+ virtual int get_height() const;
+ virtual RID get_rid() const;
+ virtual bool has_alpha() const;
+
+ virtual void set_flags(uint32_t p_flags);
+ virtual uint32_t get_flags() const;
+
+ virtual Ref<Image> get_data() const;
+
+ void set_camera_feed_id(int p_new_id);
+ int get_camera_feed_id() const;
+
+ void set_which_feed(CameraServer::FeedImage p_which);
+ CameraServer::FeedImage get_which_feed() const;
+
+ void set_camera_active(bool p_active);
+ bool get_camera_active() const;
+
+ CameraTexture();
+ ~CameraTexture();
+};
+
#endif
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 5b5968c10f..d09fac47f0 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -319,6 +319,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/z_index_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
}
p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "occluder_offset"));
@@ -646,6 +647,36 @@ Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask,
}
}
+Vector2 TileSet::atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node, const Vector2 &p_tile_location) {
+
+ ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
+ //First try to forward selection to script
+ if (get_script_instance() != NULL) {
+ if (get_script_instance()->has_method("_forward_atlas_subtile_selection")) {
+ Variant ret = get_script_instance()->call("_forward_atlas_subtile_selection", p_id, p_tilemap_node, p_tile_location);
+ if (ret.get_type() == Variant::VECTOR2) {
+ return ret;
+ }
+ }
+ }
+
+ Vector2 coord = tile_get_region(p_id).size / autotile_get_size(p_id);
+
+ List<Vector2> coords;
+ for (int x = 0; x < coord.x; x++) {
+ for (int y = 0; y < coord.y; y++) {
+ for (int i = 0; i < autotile_get_subtile_priority(p_id, Vector2(x, y)); i++) {
+ coords.push_back(Vector2(x, y));
+ }
+ }
+ }
+ if (coords.size() == 0) {
+ return autotile_get_icon_coordinate(p_id);
+ } else {
+ return coords[Math::random(0, (int)coords.size())];
+ }
+}
+
void TileSet::tile_set_name(int p_id, const String &p_name) {
ERR_FAIL_COND(!tile_map.has(p_id));
@@ -1141,6 +1172,7 @@ void TileSet::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_tile_bound", PropertyInfo(Variant::INT, "drawn_id"), PropertyInfo(Variant::INT, "neighbor_id")));
BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_subtile_selection", PropertyInfo(Variant::INT, "autotile_id"), PropertyInfo(Variant::INT, "bitmask"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
+ BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_atlas_subtile_selection", PropertyInfo(Variant::INT, "atlastile_id"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
BIND_ENUM_CONSTANT(BITMASK_2X2);
BIND_ENUM_CONSTANT(BITMASK_3X3_MINIMAL);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index fb84cee218..5fc22b9fc6 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -195,6 +195,7 @@ public:
uint32_t autotile_get_bitmask(int p_id, Vector2 p_coord);
const Map<Vector2, uint32_t> &autotile_get_bitmask_map(int p_id);
Vector2 autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node = NULL, const Vector2 &p_tile_location = Vector2());
+ Vector2 atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node = NULL, const Vector2 &p_tile_location = Vector2());
void tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape);
Ref<Shape2D> tile_get_shape(int p_id, int p_shape_id) const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 9b2e410985..dd595d9ff8 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -297,7 +297,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
if (MAX(0, from_port_type - 2) != (MAX(0, to_port_type - 2))) {
ERR_EXPLAIN("Incompatible port types (scalar/vec/bool with transform");
- ERR_FAIL_V(ERR_INVALID_PARAMETER)
+ ERR_FAIL_V(ERR_INVALID_PARAMETER);
return ERR_INVALID_PARAMETER;
}
diff --git a/servers/SCsub b/servers/SCsub
index f4af347fe6..34ba70b8cb 100644
--- a/servers/SCsub
+++ b/servers/SCsub
@@ -6,6 +6,7 @@ env.servers_sources = []
env.add_source_files(env.servers_sources, "*.cpp")
SConscript('arvr/SCsub')
+SConscript('camera/SCsub')
SConscript('physics/SCsub')
SConscript('physics_2d/SCsub')
SConscript('visual/SCsub')
diff --git a/servers/arvr/arvr_interface.cpp b/servers/arvr/arvr_interface.cpp
index 686ad0ba9b..e1b7611354 100644
--- a/servers/arvr/arvr_interface.cpp
+++ b/servers/arvr/arvr_interface.cpp
@@ -56,6 +56,7 @@ void ARVRInterface::_bind_methods() {
// but we do have properties specific to AR....
ClassDB::bind_method(D_METHOD("get_anchor_detection_is_enabled"), &ARVRInterface::get_anchor_detection_is_enabled);
ClassDB::bind_method(D_METHOD("set_anchor_detection_is_enabled", "enable"), &ARVRInterface::set_anchor_detection_is_enabled);
+ ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &ARVRInterface::get_camera_feed_id);
ADD_GROUP("AR", "ar_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ar_is_anchor_detection_enabled"), "set_anchor_detection_is_enabled", "get_anchor_detection_is_enabled");
@@ -136,3 +137,9 @@ bool ARVRInterface::get_anchor_detection_is_enabled() const {
void ARVRInterface::set_anchor_detection_is_enabled(bool p_enable){
// don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc.
};
+
+int ARVRInterface::get_camera_feed_id() {
+ // don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc.
+
+ return 0;
+};
diff --git a/servers/arvr/arvr_interface.h b/servers/arvr/arvr_interface.h
index 9ea59a3961..ffafa4fcf5 100644
--- a/servers/arvr/arvr_interface.h
+++ b/servers/arvr/arvr_interface.h
@@ -101,6 +101,7 @@ public:
/** specific to AR **/
virtual bool get_anchor_detection_is_enabled() const;
virtual void set_anchor_detection_is_enabled(bool p_enable);
+ virtual int get_camera_feed_id();
/** rendering and internal **/
diff --git a/servers/audio/effects/audio_effect_spectrum_analyzer.cpp b/servers/audio/effects/audio_effect_spectrum_analyzer.cpp
index 8e15e9288f..f5ac0afefa 100644
--- a/servers/audio/effects/audio_effect_spectrum_analyzer.cpp
+++ b/servers/audio/effects/audio_effect_spectrum_analyzer.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* audio_effect_spectrum_analyzer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 "audio_effect_spectrum_analyzer.h"
#include "servers/audio_server.h"
diff --git a/servers/audio/effects/audio_effect_spectrum_analyzer.h b/servers/audio/effects/audio_effect_spectrum_analyzer.h
index 0534426da3..4f4c3c8a58 100644
--- a/servers/audio/effects/audio_effect_spectrum_analyzer.h
+++ b/servers/audio/effects/audio_effect_spectrum_analyzer.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* audio_effect_spectrum_analyzer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 AUDIO_EFFECT_SPECTRUM_ANALYZER_H
#define AUDIO_EFFECT_SPECTRUM_ANALYZER_H
diff --git a/servers/audio/effects/audio_stream_generator.cpp b/servers/audio/effects/audio_stream_generator.cpp
index f4a66b5643..49af63e82a 100644
--- a/servers/audio/effects/audio_stream_generator.cpp
+++ b/servers/audio/effects/audio_stream_generator.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* audio_stream_generator.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 "audio_stream_generator.h"
void AudioStreamGenerator::set_mix_rate(float p_mix_rate) {
diff --git a/servers/audio/effects/audio_stream_generator.h b/servers/audio/effects/audio_stream_generator.h
index 2082682907..c3490ddaa5 100644
--- a/servers/audio/effects/audio_stream_generator.h
+++ b/servers/audio/effects/audio_stream_generator.h
@@ -1,5 +1,35 @@
-#ifndef AUDIO_STREAM_USER_FED_H
-#define AUDIO_STREAM_USER_FED_H
+/*************************************************************************/
+/* audio_stream_generator.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (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 AUDIO_STREAM_GENERATOR_H
+#define AUDIO_STREAM_GENERATOR_H
#include "core/ring_buffer.h"
#include "servers/audio/audio_stream.h"
@@ -63,4 +93,4 @@ public:
AudioStreamGeneratorPlayback();
};
-#endif // AUDIO_STREAM_USER_FED_H
+#endif // AUDIO_STREAM_GENERATOR_H
diff --git a/servers/camera/SCsub b/servers/camera/SCsub
new file mode 100644
index 0000000000..ccc76e823f
--- /dev/null
+++ b/servers/camera/SCsub
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.servers_sources, "*.cpp")
+
+Export('env')
diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp
new file mode 100644
index 0000000000..b96e9b749f
--- /dev/null
+++ b/servers/camera/camera_feed.cpp
@@ -0,0 +1,266 @@
+/*************************************************************************/
+/* camera_feed.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 "camera_feed.h"
+#include "servers/visual_server.h"
+
+void CameraFeed::_bind_methods() {
+ // The setters prefixed with _ are only exposed so we can have feeds through GDNative!
+ // They should not be called by the end user.
+
+ ClassDB::bind_method(D_METHOD("get_id"), &CameraFeed::get_id);
+ ClassDB::bind_method(D_METHOD("get_name"), &CameraFeed::get_name);
+ ClassDB::bind_method(D_METHOD("_set_name", "name"), &CameraFeed::set_name);
+
+ ClassDB::bind_method(D_METHOD("is_active"), &CameraFeed::is_active);
+ ClassDB::bind_method(D_METHOD("set_active", "active"), &CameraFeed::set_active);
+
+ ClassDB::bind_method(D_METHOD("get_position"), &CameraFeed::get_position);
+ ClassDB::bind_method(D_METHOD("_set_position", "position"), &CameraFeed::set_position);
+
+ // Note, for transform some feeds may override what the user sets (such as ARKit)
+ ClassDB::bind_method(D_METHOD("get_transform"), &CameraFeed::get_transform);
+ ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CameraFeed::set_transform);
+
+ ClassDB::bind_method(D_METHOD("_set_RGB_img", "rgb_img"), &CameraFeed::set_RGB_img);
+ ClassDB::bind_method(D_METHOD("_set_YCbCr_img", "ycbcr_img"), &CameraFeed::set_YCbCr_img);
+ ClassDB::bind_method(D_METHOD("_set_YCbCr_imgs", "y_img", "cbcr_img"), &CameraFeed::set_YCbCr_imgs);
+ ClassDB::bind_method(D_METHOD("_allocate_texture", "width", "height", "format", "texture_type", "data_type"), &CameraFeed::allocate_texture);
+
+ ADD_GROUP("Feed", "feed_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "feed_is_active"), "set_active", "is_active");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "feed_transform"), "set_transform", "get_transform");
+
+ BIND_ENUM_CONSTANT(FEED_NOIMAGE);
+ BIND_ENUM_CONSTANT(FEED_RGB);
+ BIND_ENUM_CONSTANT(FEED_YCbCr);
+ BIND_ENUM_CONSTANT(FEED_YCbCr_Sep);
+
+ BIND_ENUM_CONSTANT(FEED_UNSPECIFIED);
+ BIND_ENUM_CONSTANT(FEED_FRONT);
+ BIND_ENUM_CONSTANT(FEED_BACK);
+}
+
+int CameraFeed::get_id() const {
+ return id;
+}
+
+bool CameraFeed::is_active() const {
+ return active;
+}
+
+void CameraFeed::set_active(bool p_is_active) {
+ if (p_is_active == active) {
+ // all good
+ } else if (p_is_active) {
+ // attempt to activate this feed
+ if (activate_feed()) {
+ print_line("Activate " + name);
+ active = true;
+ }
+ } else {
+ // just deactivate it
+ deactivate_feed();
+ print_line("Deactivate " + name);
+ active = false;
+ }
+}
+
+String CameraFeed::get_name() const {
+ return name;
+}
+
+void CameraFeed::set_name(String p_name) {
+ name = p_name;
+}
+
+int CameraFeed::get_base_width() const {
+ return base_width;
+}
+
+int CameraFeed::get_base_height() const {
+ return base_height;
+}
+
+CameraFeed::FeedDataType CameraFeed::get_datatype() const {
+ return datatype;
+}
+
+CameraFeed::FeedPosition CameraFeed::get_position() const {
+ return position;
+}
+
+void CameraFeed::set_position(CameraFeed::FeedPosition p_position) {
+ position = p_position;
+}
+
+Transform2D CameraFeed::get_transform() const {
+ return transform;
+}
+
+void CameraFeed::set_transform(const Transform2D &p_transform) {
+ transform = p_transform;
+}
+
+RID CameraFeed::get_texture(CameraServer::FeedImage p_which) {
+ return texture[p_which];
+}
+
+CameraFeed::CameraFeed() {
+ // initialize our feed
+ id = CameraServer::get_singleton()->get_free_id();
+ name = "???";
+ active = false;
+ datatype = CameraFeed::FEED_RGB;
+ position = CameraFeed::FEED_UNSPECIFIED;
+ transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
+
+ // create a texture object
+ VisualServer *vs = VisualServer::get_singleton();
+ texture[CameraServer::FEED_Y_IMAGE] = vs->texture_create(); // also used for RGBA
+ texture[CameraServer::FEED_CbCr_IMAGE] = vs->texture_create();
+}
+
+CameraFeed::CameraFeed(String p_name, FeedPosition p_position) {
+ // initialize our feed
+ id = CameraServer::get_singleton()->get_free_id();
+ base_width = 0;
+ base_height = 0;
+ name = p_name;
+ active = false;
+ datatype = CameraFeed::FEED_NOIMAGE;
+ position = p_position;
+ transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
+
+ // create a texture object
+ VisualServer *vs = VisualServer::get_singleton();
+ texture[CameraServer::FEED_Y_IMAGE] = vs->texture_create(); // also used for RGBA
+ texture[CameraServer::FEED_CbCr_IMAGE] = vs->texture_create();
+}
+
+CameraFeed::~CameraFeed() {
+ // Free our textures
+ VisualServer *vs = VisualServer::get_singleton();
+ vs->free(texture[CameraServer::FEED_Y_IMAGE]);
+ vs->free(texture[CameraServer::FEED_CbCr_IMAGE]);
+}
+
+void CameraFeed::set_RGB_img(Ref<Image> p_rgb_img) {
+ if (active) {
+ VisualServer *vs = VisualServer::get_singleton();
+
+ int new_width = p_rgb_img->get_width();
+ int new_height = p_rgb_img->get_height();
+
+ if ((base_width != new_width) || (base_height != new_height)) {
+ // We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
+ base_width = new_width;
+ base_height = new_height;
+
+ vs->texture_allocate(texture[CameraServer::FEED_RGBA_IMAGE], new_width, new_height, 0, Image::FORMAT_RGB8, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAGS_DEFAULT);
+ }
+
+ vs->texture_set_data(texture[CameraServer::FEED_RGBA_IMAGE], p_rgb_img);
+ datatype = CameraFeed::FEED_RGB;
+ }
+}
+
+void CameraFeed::set_YCbCr_img(Ref<Image> p_ycbcr_img) {
+ if (active) {
+ VisualServer *vs = VisualServer::get_singleton();
+
+ int new_width = p_ycbcr_img->get_width();
+ int new_height = p_ycbcr_img->get_height();
+
+ if ((base_width != new_width) || (base_height != new_height)) {
+ // We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
+ base_width = new_width;
+ base_height = new_height;
+
+ vs->texture_allocate(texture[CameraServer::FEED_RGBA_IMAGE], new_width, new_height, 0, Image::FORMAT_RGB8, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAGS_DEFAULT);
+ }
+
+ vs->texture_set_data(texture[CameraServer::FEED_RGBA_IMAGE], p_ycbcr_img);
+ datatype = CameraFeed::FEED_YCbCr;
+ }
+}
+
+void CameraFeed::set_YCbCr_imgs(Ref<Image> p_y_img, Ref<Image> p_cbcr_img) {
+ if (active) {
+ VisualServer *vs = VisualServer::get_singleton();
+
+ ///@TODO investigate whether we can use thirdparty/misc/yuv2rgb.h here to convert our YUV data to RGB, our shader approach is potentially faster though..
+ // Wondering about including that into multiple projects, may cause issues.
+ // That said, if we convert to RGB, we could enable using texture resources again...
+
+ int new_y_width = p_y_img->get_width();
+ int new_y_height = p_y_img->get_height();
+ int new_cbcr_width = p_cbcr_img->get_width();
+ int new_cbcr_height = p_cbcr_img->get_height();
+
+ if ((base_width != new_y_width) || (base_height != new_y_height)) {
+ // We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
+ base_width = new_y_width;
+ base_height = new_y_height;
+
+ vs->texture_allocate(texture[CameraServer::FEED_Y_IMAGE], new_y_width, new_y_height, 0, Image::FORMAT_R8, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_USED_FOR_STREAMING);
+
+ ///@TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion
+ vs->texture_allocate(texture[CameraServer::FEED_CbCr_IMAGE], new_cbcr_width, new_cbcr_height, 0, Image::FORMAT_RG8, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_USED_FOR_STREAMING);
+ }
+
+ vs->texture_set_data(texture[CameraServer::FEED_Y_IMAGE], p_y_img);
+ vs->texture_set_data(texture[CameraServer::FEED_CbCr_IMAGE], p_cbcr_img);
+ datatype = CameraFeed::FEED_YCbCr_Sep;
+ }
+}
+
+void CameraFeed::allocate_texture(int p_width, int p_height, Image::Format p_format, VisualServer::TextureType p_texture_type, FeedDataType p_data_type) {
+ VisualServer *vs = VisualServer::get_singleton();
+
+ if ((base_width != p_width) || (base_height != p_height)) {
+ // We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
+ base_width = p_width;
+ base_height = p_height;
+
+ vs->texture_allocate(texture[0], p_width, p_height, 0, p_format, p_texture_type, VS::TEXTURE_FLAGS_DEFAULT);
+ }
+
+ datatype = p_data_type;
+}
+
+bool CameraFeed::activate_feed() {
+ // nothing to do here
+ return true;
+}
+
+void CameraFeed::deactivate_feed() {
+ // nothing to do here
+}
diff --git a/servers/camera/camera_feed.h b/servers/camera/camera_feed.h
new file mode 100644
index 0000000000..aa8a78b2f6
--- /dev/null
+++ b/servers/camera/camera_feed.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* camera_feed.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 CAMERA_FEED_H
+#define CAMERA_FEED_H
+
+#include "core/image.h"
+#include "core/math/transform_2d.h"
+#include "servers/camera_server.h"
+#include "servers/visual_server.h"
+
+/**
+ @author Bastiaan Olij <mux213@gmail.com>
+
+ The camera server is a singleton object that gives access to the various
+ camera feeds that can be used as the background for our environment.
+**/
+
+class CameraFeed : public Reference {
+ GDCLASS(CameraFeed, Reference);
+
+public:
+ enum FeedDataType {
+ FEED_NOIMAGE, // we don't have an image yet
+ FEED_RGB, // our texture will contain a normal RGB texture that can be used directly
+ FEED_YCbCr, // our texture will contain a YCbCr texture that needs to be converted to RGB before output
+ FEED_YCbCr_Sep // our camera is split into two textures, first plane contains Y data, second plane contains CbCr data
+ };
+
+ enum FeedPosition {
+ FEED_UNSPECIFIED, // we have no idea
+ FEED_FRONT, // this is a camera on the front of the device
+ FEED_BACK // this is a camera on the back of the device
+ };
+
+private:
+ int id; // unique id for this, for internal use in case feeds are removed
+ int base_width;
+ int base_height;
+
+protected:
+ String name; // name of our camera feed
+ FeedDataType datatype; // type of texture data stored
+ FeedPosition position; // position of camera on the device
+ Transform2D transform; // display transform
+
+ bool active; // only when active do we actually update the camera texture each frame
+ RID texture[CameraServer::FEED_IMAGES]; // texture images needed for this
+
+ static void _bind_methods();
+
+public:
+ int get_id() const;
+ bool is_active() const;
+ void set_active(bool p_is_active);
+
+ String get_name() const;
+ void set_name(String p_name);
+
+ int get_base_width() const;
+ int get_base_height() const;
+
+ FeedPosition get_position() const;
+ void set_position(FeedPosition p_position);
+
+ Transform2D get_transform() const;
+ void set_transform(const Transform2D &p_transform);
+
+ RID get_texture(CameraServer::FeedImage p_which);
+
+ CameraFeed();
+ CameraFeed(String p_name, FeedPosition p_position = CameraFeed::FEED_UNSPECIFIED);
+ virtual ~CameraFeed();
+
+ FeedDataType get_datatype() const;
+ void set_RGB_img(Ref<Image> p_rgb_img);
+ void set_YCbCr_img(Ref<Image> p_ycbcr_img);
+ void set_YCbCr_imgs(Ref<Image> p_y_img, Ref<Image> p_cbcr_img);
+ void allocate_texture(int p_width, int p_height, Image::Format p_format, VisualServer::TextureType p_texture_type, FeedDataType p_data_type);
+
+ virtual bool activate_feed();
+ virtual void deactivate_feed();
+};
+
+VARIANT_ENUM_CAST(CameraFeed::FeedDataType);
+VARIANT_ENUM_CAST(CameraFeed::FeedPosition);
+
+#endif /* !CAMERA_FEED_H */
diff --git a/servers/camera_server.cpp b/servers/camera_server.cpp
new file mode 100644
index 0000000000..f52d3decae
--- /dev/null
+++ b/servers/camera_server.cpp
@@ -0,0 +1,169 @@
+/*************************************************************************/
+/* camera_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 "camera_server.h"
+#include "servers/camera/camera_feed.h"
+#include "visual_server.h"
+
+////////////////////////////////////////////////////////
+// CameraServer
+
+void CameraServer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_feed", "index"), &CameraServer::get_feed);
+ ClassDB::bind_method(D_METHOD("get_feed_count"), &CameraServer::get_feed_count);
+ ClassDB::bind_method(D_METHOD("feeds"), &CameraServer::get_feeds);
+
+ ClassDB::bind_method(D_METHOD("add_feed", "feed"), &CameraServer::add_feed);
+ ClassDB::bind_method(D_METHOD("remove_feed", "feed"), &CameraServer::remove_feed);
+
+ ADD_SIGNAL(MethodInfo("camera_feed_added", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("camera_feed_removed", PropertyInfo(Variant::INT, "id")));
+
+ BIND_ENUM_CONSTANT(FEED_RGBA_IMAGE);
+ BIND_ENUM_CONSTANT(FEED_YCbCr_IMAGE);
+ BIND_ENUM_CONSTANT(FEED_Y_IMAGE);
+ BIND_ENUM_CONSTANT(FEED_CbCr_IMAGE);
+};
+
+CameraServer *CameraServer::singleton = NULL;
+
+CameraServer *CameraServer::get_singleton() {
+ return singleton;
+};
+
+int CameraServer::get_free_id() {
+ bool id_exists = true;
+ int newid = 0;
+
+ // find a free id
+ while (id_exists) {
+ newid++;
+ id_exists = false;
+ for (int i = 0; i < feeds.size() && !id_exists; i++) {
+ if (feeds[i]->get_id() == newid) {
+ id_exists = true;
+ };
+ };
+ };
+
+ return newid;
+};
+
+int CameraServer::get_feed_index(int p_id) {
+ for (int i = 0; i < feeds.size(); i++) {
+ if (feeds[i]->get_id() == p_id) {
+ return i;
+ };
+ };
+
+ return -1;
+};
+
+Ref<CameraFeed> CameraServer::get_feed_by_id(int p_id) {
+ int index = get_feed_index(p_id);
+
+ if (index == -1) {
+ return NULL;
+ } else {
+ return feeds[index];
+ }
+};
+
+void CameraServer::add_feed(const Ref<CameraFeed> &p_feed) {
+ // add our feed
+ feeds.push_back(p_feed);
+
+// record for debugging
+#ifdef DEBUG_ENABLED
+ print_line("Registered camera " + p_feed->get_name() + " with id " + itos(p_feed->get_id()) + " position " + itos(p_feed->get_position()) + " at index " + itos(feeds.size() - 1));
+#endif
+
+ // let whomever is interested know
+ emit_signal("camera_feed_added", p_feed->get_id());
+};
+
+void CameraServer::remove_feed(const Ref<CameraFeed> &p_feed) {
+ for (int i = 0; i < feeds.size(); i++) {
+ if (feeds[i] == p_feed) {
+ int feed_id = p_feed->get_id();
+
+// record for debugging
+#ifdef DEBUG_ENABLED
+ print_line("Removed camera " + p_feed->get_name() + " with id " + itos(feed_id) + " position " + itos(p_feed->get_position()));
+#endif
+
+ // remove it from our array, if this results in our feed being unreferenced it will be destroyed
+ feeds.remove(i);
+
+ // let whomever is interested know
+ emit_signal("camera_feed_removed", feed_id);
+ return;
+ };
+ };
+};
+
+Ref<CameraFeed> CameraServer::get_feed(int p_index) {
+ ERR_FAIL_INDEX_V(p_index, feeds.size(), NULL);
+
+ return feeds[p_index];
+};
+
+int CameraServer::get_feed_count() {
+ return feeds.size();
+};
+
+Array CameraServer::get_feeds() {
+ Array return_feeds;
+ int cc = get_feed_count();
+ return_feeds.resize(cc);
+
+ for (int i = 0; i < feeds.size(); i++) {
+ return_feeds[i] = get_feed(i);
+ };
+
+ return return_feeds;
+};
+
+RID CameraServer::feed_texture(int p_id, CameraServer::FeedImage p_texture) {
+ int index = get_feed_index(p_id);
+ ERR_FAIL_COND_V(index == -1, RID());
+
+ Ref<CameraFeed> feed = get_feed(index);
+
+ return feed->get_texture(p_texture);
+};
+
+CameraServer::CameraServer() {
+ singleton = this;
+};
+
+CameraServer::~CameraServer() {
+ singleton = NULL;
+};
diff --git a/servers/camera_server.h b/servers/camera_server.h
new file mode 100644
index 0000000000..c7716a3cff
--- /dev/null
+++ b/servers/camera_server.h
@@ -0,0 +1,96 @@
+/*************************************************************************/
+/* camera_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 CAMERA_SERVER_H
+#define CAMERA_SERVER_H
+
+#include "core/object.h"
+#include "core/os/thread_safe.h"
+#include "core/reference.h"
+#include "core/rid.h"
+#include "core/variant.h"
+
+/**
+ @author Bastiaan Olij <mux213@gmail.com>
+
+ The camera server is a singleton object that gives access to the various
+ camera feeds that can be used as the background for our environment.
+**/
+
+class CameraFeed;
+
+class CameraServer : public Object {
+ GDCLASS(CameraServer, Object);
+ _THREAD_SAFE_CLASS_
+
+public:
+ enum FeedImage {
+ FEED_RGBA_IMAGE = 0,
+ FEED_YCbCr_IMAGE = 0,
+ FEED_Y_IMAGE = 0,
+ FEED_CbCr_IMAGE = 1,
+ FEED_IMAGES = 2
+ };
+
+private:
+protected:
+ Vector<Ref<CameraFeed> > feeds;
+
+ static CameraServer *singleton;
+
+ static void _bind_methods();
+
+public:
+ static CameraServer *get_singleton();
+
+ // Right now we identify our feed by it's ID when it's used in the background.
+ // May see if we can change this to purely relying on CameraFeed objects or by name.
+ int get_free_id();
+ int get_feed_index(int p_id);
+ Ref<CameraFeed> get_feed_by_id(int p_id);
+
+ // add and remove feeds
+ void add_feed(const Ref<CameraFeed> &p_feed);
+ void remove_feed(const Ref<CameraFeed> &p_feed);
+
+ // get our feeds
+ Ref<CameraFeed> get_feed(int p_idx);
+ int get_feed_count();
+ Array get_feeds();
+
+ RID feed_texture(int p_id, FeedImage p_texture);
+
+ CameraServer();
+ ~CameraServer();
+};
+
+VARIANT_ENUM_CAST(CameraServer::FeedImage);
+
+#endif /* CAMERA_SERVER_H */
diff --git a/servers/physics_2d/joints_2d_sw.cpp b/servers/physics_2d/joints_2d_sw.cpp
index 954b0fa3ea..acdaa6e6df 100644
--- a/servers/physics_2d/joints_2d_sw.cpp
+++ b/servers/physics_2d/joints_2d_sw.cpp
@@ -92,7 +92,7 @@ normal_relative_velocity(Body2DSW *a, Body2DSW *b, Vector2 rA, Vector2 rB, Vecto
bool PinJoint2DSW::setup(real_t p_step) {
Space2DSW *space = A->get_space();
- ERR_FAIL_COND_V(!space, false;)
+ ERR_FAIL_COND_V(!space, false);
rA = A->get_transform().basis_xform(anchor_A);
rB = B ? B->get_transform().basis_xform(anchor_B) : anchor_B;
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index f3394019f5..f7cec6a378 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -54,6 +54,8 @@
#include "audio/effects/audio_effect_stereo_enhance.h"
#include "audio/effects/audio_stream_generator.h"
#include "audio_server.h"
+#include "camera/camera_feed.h"
+#include "camera_server.h"
#include "core/script_debugger_remote.h"
#include "physics/physics_server_sw.h"
#include "physics_2d/physics_2d_server_sw.h"
@@ -114,6 +116,7 @@ void register_server_types() {
ClassDB::register_virtual_class<PhysicsServer>();
ClassDB::register_virtual_class<Physics2DServer>();
ClassDB::register_class<ARVRServer>();
+ ClassDB::register_class<CameraServer>();
shader_types = memnew(ShaderTypes);
@@ -169,6 +172,8 @@ void register_server_types() {
ClassDB::register_virtual_class<AudioEffectSpectrumAnalyzerInstance>();
}
+ ClassDB::register_class<CameraFeed>();
+
ClassDB::register_virtual_class<Physics2DDirectBodyState>();
ClassDB::register_virtual_class<Physics2DDirectSpaceState>();
ClassDB::register_virtual_class<Physics2DShapeQueryResult>();
@@ -208,4 +213,5 @@ void register_server_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer", PhysicsServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("Physics2DServer", Physics2DServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ARVRServer", ARVRServer::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton()));
}
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 31888261ec..47929b1a77 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -60,6 +60,7 @@ public:
virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0;
virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0;
virtual void environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy = 1.0, float p_sky_contribution = 0.0) = 0;
+ virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) = 0;
virtual void environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_far_amount, VS::EnvironmentDOFBlurQuality p_quality) = 0;
virtual void environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_far_amount, VS::EnvironmentDOFBlurQuality p_quality) = 0;
@@ -204,6 +205,7 @@ public:
virtual uint32_t texture_get_height(RID p_texture) const = 0;
virtual uint32_t texture_get_depth(RID p_texture) const = 0;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth_3d) = 0;
+ virtual void texture_bind(RID p_texture, uint32_t p_texture_no) = 0;
virtual void texture_set_path(RID p_texture, const String &p_path) = 0;
virtual String texture_get_path(RID p_texture) const = 0;
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index 6efd05593e..42b9f19d9d 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -132,6 +132,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
"TYPE_SAMPLERCUBE",
"INTERPOLATION_FLAT",
"INTERPOLATION_SMOOTH",
+ "CONST",
"PRECISION_LOW",
"PRECISION_MID",
"PRECISION_HIGH",
@@ -271,6 +272,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_TYPE_SAMPLERCUBE, "samplerCube" },
{ TK_INTERPOLATION_FLAT, "flat" },
{ TK_INTERPOLATION_SMOOTH, "smooth" },
+ { TK_CONST, "const" },
{ TK_PRECISION_LOW, "lowp" },
{ TK_PRECISION_MID, "mediump" },
{ TK_PRECISION_HIGH, "highp" },
@@ -920,6 +922,16 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String
return true;
}
+ if (shader->constants.has(p_identifier)) {
+ if (r_data_type) {
+ *r_data_type = shader->constants[p_identifier].type;
+ }
+ if (r_type) {
+ *r_type = IDENTIFIER_CONSTANT;
+ }
+ return true;
+ }
+
for (int i = 0; i < shader->functions.size(); i++) {
if (!shader->functions[i].callable)
@@ -2699,6 +2711,12 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI
return false;
}
+ if (shader->constants.has(var->name)) {
+ if (r_message)
+ *r_message = RTR("Constants cannot be modified.");
+ return false;
+ }
+
if (!(p_builtin_types.has(var->name) && p_builtin_types[var->name].constant)) {
return true;
}
@@ -3993,7 +4011,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
} else {
- //nothng else, so expression
+ //nothing else, so expression
_set_tkpos(pos); //rollback
Node *expr = _parse_and_reduce_expression(p_block, p_builtin_types);
if (!expr)
@@ -4322,24 +4340,30 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
} break;
default: {
- //function
+ //function or constant variable
+ bool is_constant = false;
DataPrecision precision = PRECISION_DEFAULT;
DataType type;
StringName name;
+ if (tk.type == TK_CONST) {
+ is_constant = true;
+ tk = _get_token();
+ }
+
if (is_token_precision(tk.type)) {
precision = get_token_precision(tk.type);
tk = _get_token();
}
if (!is_token_datatype(tk.type)) {
- _set_error("Expected function, uniform or varying ");
+ _set_error("Expected constant, function, uniform or varying ");
return ERR_PARSE_ERROR;
}
if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for function return (samplers not allowed)");
+ _set_error("Invalid data type for constants or function return (samplers not allowed)");
return ERR_PARSE_ERROR;
}
@@ -4359,8 +4383,73 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after identifier");
- return ERR_PARSE_ERROR;
+ if (type == TYPE_VOID) {
+ _set_error("Expected '(' after function identifier");
+ return ERR_PARSE_ERROR;
+ }
+
+ //variable
+
+ while (true) {
+ ShaderNode::Constant constant;
+ constant.type = type;
+ constant.precision = precision;
+ constant.initializer = NULL;
+
+ if (tk.type == TK_OP_ASSIGN) {
+
+ if (!is_constant) {
+ _set_error("Expected 'const' keyword before constant definition");
+ return ERR_PARSE_ERROR;
+ }
+
+ //variable created with assignment! must parse an expression
+ Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
+ if (!expr)
+ return ERR_PARSE_ERROR;
+
+ if (expr->type != Node::TYPE_CONSTANT) {
+ _set_error("Expected constant expression after '='");
+ return ERR_PARSE_ERROR;
+ }
+
+ constant.initializer = static_cast<ConstantNode *>(expr);
+
+ if (type != expr->get_datatype()) {
+ _set_error("Invalid assignment of '" + get_datatype_name(expr->get_datatype()) + "' to '" + get_datatype_name(type) + "'");
+ return ERR_PARSE_ERROR;
+ }
+ tk = _get_token();
+ } else {
+ _set_error("Expected initialization of constant");
+ return ERR_PARSE_ERROR;
+ }
+
+ shader->constants[name] = constant;
+ if (tk.type == TK_COMMA) {
+ tk = _get_token();
+ if (tk.type != TK_IDENTIFIER) {
+ _set_error("Expected identifier after type");
+ return ERR_PARSE_ERROR;
+ }
+
+ name = tk.text;
+ if (_find_identifier(NULL, Map<StringName, BuiltInInfo>(), name)) {
+ _set_error("Redefinition of '" + String(name) + "'");
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = _get_token();
+
+ } else if (tk.type == TK_SEMICOLON) {
+ break;
+ } else {
+ _set_error("Expected ',' or ';' after constant");
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ break;
}
Map<StringName, BuiltInInfo> builtin_types;
diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h
index 67c273d267..934dc2c403 100644
--- a/servers/visual/shader_language.h
+++ b/servers/visual/shader_language.h
@@ -80,6 +80,7 @@ public:
TK_TYPE_SAMPLERCUBE,
TK_INTERPOLATION_FLAT,
TK_INTERPOLATION_SMOOTH,
+ TK_CONST,
TK_PRECISION_LOW,
TK_PRECISION_MID,
TK_PRECISION_HIGH,
@@ -440,6 +441,13 @@ public:
};
struct ShaderNode : public Node {
+
+ struct Constant {
+ DataType type;
+ DataPrecision precision;
+ ConstantNode *initializer;
+ };
+
struct Function {
StringName name;
FunctionNode *function;
@@ -492,6 +500,7 @@ public:
}
};
+ Map<StringName, Constant> constants;
Map<StringName, Varying> varyings;
Map<StringName, Uniform> uniforms;
Vector<StringName> render_modes;
@@ -632,6 +641,7 @@ private:
IDENTIFIER_FUNCTION_ARGUMENT,
IDENTIFIER_LOCAL_VAR,
IDENTIFIER_BUILTIN_VAR,
+ IDENTIFIER_CONSTANT,
};
bool _find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = NULL, IdentifierType *r_type = NULL);
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index 921d55556d..f6d26c07d4 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -159,6 +159,7 @@ public:
BIND1RC(uint32_t, texture_get_height, RID)
BIND1RC(uint32_t, texture_get_depth, RID)
BIND4(texture_set_size_override, RID, int, int, int)
+ BIND2(texture_bind, RID, uint32_t)
BIND3(texture_set_detect_3d_callback, RID, TextureDetectCallback, void *)
BIND3(texture_set_detect_srgb_callback, RID, TextureDetectCallback, void *)
@@ -503,6 +504,7 @@ public:
BIND2(environment_set_bg_energy, RID, float)
BIND2(environment_set_canvas_max_layer, RID, int)
BIND4(environment_set_ambient_light, RID, const Color &, float, float)
+ BIND2(environment_set_camera_feed_id, RID, int)
BIND7(environment_set_ssr, RID, bool, int, float, float, float, bool)
BIND13(environment_set_ssao, RID, bool, float, float, float, float, float, float, float, const Color &, EnvironmentSSAOQuality, EnvironmentSSAOBlur, float)
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index cd24deb60c..3fdb4acd3c 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -95,6 +95,7 @@ public:
FUNC1RC(uint32_t, texture_get_height, RID)
FUNC1RC(uint32_t, texture_get_depth, RID)
FUNC4(texture_set_size_override, RID, int, int, int)
+ FUNC2(texture_bind, RID, uint32_t)
FUNC3(texture_set_detect_3d_callback, RID, TextureDetectCallback, void *)
FUNC3(texture_set_detect_srgb_callback, RID, TextureDetectCallback, void *)
@@ -430,6 +431,7 @@ public:
FUNC2(environment_set_bg_energy, RID, float)
FUNC2(environment_set_canvas_max_layer, RID, int)
FUNC4(environment_set_ambient_light, RID, const Color &, float, float)
+ FUNC2(environment_set_camera_feed_id, RID, int)
FUNC7(environment_set_ssr, RID, bool, int, float, float, float, bool)
FUNC13(environment_set_ssao, RID, bool, float, float, float, float, float, float, float, const Color &, EnvironmentSSAOQuality, EnvironmentSSAOBlur, float)
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index 0fe00ad61a..a9ea983cc7 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -1676,6 +1676,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("texture_set_path", "texture", "path"), &VisualServer::texture_set_path);
ClassDB::bind_method(D_METHOD("texture_get_path", "texture"), &VisualServer::texture_get_path);
ClassDB::bind_method(D_METHOD("texture_set_shrink_all_x2_on_set_data", "shrink"), &VisualServer::texture_set_shrink_all_x2_on_set_data);
+ ClassDB::bind_method(D_METHOD("texture_bind", "texture", "number"), &VisualServer::texture_bind);
ClassDB::bind_method(D_METHOD("texture_debug_usage"), &VisualServer::_texture_debug_usage_bind);
ClassDB::bind_method(D_METHOD("textures_keep_original", "enable"), &VisualServer::textures_keep_original);
diff --git a/servers/visual_server.h b/servers/visual_server.h
index 01be996bfc..70222d09b1 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -140,6 +140,7 @@ public:
virtual uint32_t texture_get_height(RID p_texture) const = 0;
virtual uint32_t texture_get_depth(RID p_texture) const = 0;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth_3d) = 0;
+ virtual void texture_bind(RID p_texture, uint32_t p_texture_no) = 0;
virtual void texture_set_path(RID p_texture, const String &p_path) = 0;
virtual String texture_get_path(RID p_texture) const = 0;
@@ -707,6 +708,7 @@ public:
ENV_BG_COLOR_SKY,
ENV_BG_CANVAS,
ENV_BG_KEEP,
+ ENV_BG_CAMERA_FEED,
ENV_BG_MAX
};
@@ -718,6 +720,7 @@ public:
virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0;
virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0;
virtual void environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy = 1.0, float p_sky_contribution = 0.0) = 0;
+ virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) = 0;
//set default SSAO options
//set default SSR options
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 732c08fdea..3e1e8f9734 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -26,7 +26,7 @@ comments.
## bullet
- Upstream: https://github.com/bulletphysics/bullet3
-- Version: 2.88
+- Version: git (5ec8339, 2019)
- License: zlib
Files extracted from upstream source:
diff --git a/thirdparty/bullet/Bullet3Common/b3Quaternion.h b/thirdparty/bullet/Bullet3Common/b3Quaternion.h
index 9bd5ff7d90..4fdd72dcc4 100644
--- a/thirdparty/bullet/Bullet3Common/b3Quaternion.h
+++ b/thirdparty/bullet/Bullet3Common/b3Quaternion.h
@@ -92,8 +92,11 @@ public:
/**@brief Set the rotation using axis angle notation
* @param axis The axis around which to rotate
* @param angle The magnitude of the rotation in Radians */
- void setRotation(const b3Vector3& axis, const b3Scalar& _angle)
+ void setRotation(const b3Vector3& axis1, const b3Scalar& _angle)
{
+ b3Vector3 axis = axis1;
+ axis.safeNormalize();
+
b3Scalar d = axis.length();
b3Assert(d != b3Scalar(0.0));
if (d < B3_EPSILON)
diff --git a/thirdparty/bullet/Bullet3Common/b3Vector3.h b/thirdparty/bullet/Bullet3Common/b3Vector3.h
index 56e6c13311..a70d68d6e1 100644
--- a/thirdparty/bullet/Bullet3Common/b3Vector3.h
+++ b/thirdparty/bullet/Bullet3Common/b3Vector3.h
@@ -36,7 +36,7 @@ subject to the following restrictions:
#pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255'
#endif
-#define B3_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x))
+#define B3_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff)
//#define b3_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) )
#define b3_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask))
#define b3_splat3_ps(_a, _i) b3_pshufd_ps((_a), B3_SHUFFLE(_i, _i, _i, 3))
diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvt.cpp b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvt.cpp
index 37156fd589..166cb04c0b 100644
--- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvt.cpp
+++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvt.cpp
@@ -37,7 +37,7 @@ static DBVT_INLINE int indexof(const btDbvtNode* node)
static DBVT_INLINE btDbvtVolume merge(const btDbvtVolume& a,
const btDbvtVolume& b)
{
-#if (DBVT_MERGE_IMPL == DBVT_IMPL_SSE)
+#ifdef BT_USE_SSE
ATTRIBUTE_ALIGNED16(char locals[sizeof(btDbvtAabbMm)]);
btDbvtVolume* ptr = (btDbvtVolume*)locals;
btDbvtVolume& res = *ptr;
@@ -80,6 +80,7 @@ static DBVT_INLINE void deletenode(btDbvt* pdbvt,
static void recursedeletenode(btDbvt* pdbvt,
btDbvtNode* node)
{
+ if (node == 0) return;
if (!node->isleaf())
{
recursedeletenode(pdbvt, node->childs[0]);
@@ -298,7 +299,7 @@ static int split(btDbvtNode** leaves,
static btDbvtVolume bounds(btDbvtNode** leaves,
int count)
{
-#if DBVT_MERGE_IMPL == DBVT_IMPL_SSE
+#ifdef BT_USE_SSE
ATTRIBUTE_ALIGNED16(char locals[sizeof(btDbvtVolume)]);
btDbvtVolume* ptr = (btDbvtVolume*)locals;
btDbvtVolume& volume = *ptr;
diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp
index 166cf771fe..b7fe0a1f34 100644
--- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp
+++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp
@@ -123,11 +123,11 @@ protected:
void btSimpleBroadphase::destroyProxy(btBroadphaseProxy* proxyOrg, btDispatcher* dispatcher)
{
+ m_pairCache->removeOverlappingPairsContainingProxy(proxyOrg, dispatcher);
+
btSimpleBroadphaseProxy* proxy0 = static_cast<btSimpleBroadphaseProxy*>(proxyOrg);
freeHandle(proxy0);
- m_pairCache->removeOverlappingPairsContainingProxy(proxyOrg, dispatcher);
-
//validate();
}
diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp
index 98a02d0c45..b48d9301d7 100644
--- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp
+++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp
@@ -43,6 +43,7 @@ btCollisionObject::btCollisionObject()
m_userObjectPointer(0),
m_userIndex2(-1),
m_userIndex(-1),
+ m_userIndex3(-1),
m_hitFraction(btScalar(1.)),
m_ccdSweptSphereRadius(btScalar(0.)),
m_ccdMotionThreshold(btScalar(0.)),
diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h
index 56b3d89e56..85dc488c8c 100644
--- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h
+++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h
@@ -101,6 +101,8 @@ protected:
int m_userIndex;
+ int m_userIndex3;
+
///time of impact calculation
btScalar m_hitFraction;
@@ -526,6 +528,11 @@ public:
return m_userIndex2;
}
+ int getUserIndex3() const
+ {
+ return m_userIndex3;
+ }
+
///users can point to their objects, userPointer is not used by Bullet
void setUserPointer(void* userPointer)
{
@@ -543,6 +550,11 @@ public:
m_userIndex2 = index;
}
+ void setUserIndex3(int index)
+ {
+ m_userIndex3 = index;
+ }
+
int getUpdateRevisionInternal() const
{
return m_updateRevision;
diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp
index b30ce03164..71184f36ac 100644
--- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp
+++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp
@@ -19,10 +19,10 @@ subject to the following restrictions:
#include "BulletCollision/CollisionShapes/btCollisionShape.h"
#include "BulletCollision/CollisionShapes/btConvexShape.h"
#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h"
-#include "BulletCollision/CollisionShapes/btSphereShape.h" //for raycasting
-#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" //for raycasting
-#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" //for raycasting
-#include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h" //for raycasting
+#include "BulletCollision/CollisionShapes/btSphereShape.h" //for raycasting
+#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" //for raycasting
+#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" //for raycasting
+#include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h" //for raycasting
#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h"
#include "BulletCollision/CollisionShapes/btCompoundShape.h"
#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h"
@@ -414,7 +414,9 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans, co
rcb.m_hitFraction = resultCallback.m_closestHitFraction;
triangleMesh->performRaycast(&rcb, rayFromLocalScaled, rayToLocalScaled);
}
- else if (collisionShape->getShapeType()==TERRAIN_SHAPE_PROXYTYPE)
+ else if (((resultCallback.m_flags&btTriangleRaycastCallback::kF_DisableHeightfieldAccelerator)==0)
+ && collisionShape->getShapeType() == TERRAIN_SHAPE_PROXYTYPE
+ )
{
///optimized version for btHeightfieldTerrainShape
btHeightfieldTerrainShape* heightField = (btHeightfieldTerrainShape*)collisionShape;
@@ -422,7 +424,7 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans, co
btVector3 rayFromLocal = worldTocollisionObject * rayFromTrans.getOrigin();
btVector3 rayToLocal = worldTocollisionObject * rayToTrans.getOrigin();
- BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,&resultCallback,collisionObjectWrap->getCollisionObject(),heightField,colObjWorldTransform);
+ BridgeTriangleRaycastCallback rcb(rayFromLocal, rayToLocal, &resultCallback, collisionObjectWrap->getCollisionObject(), heightField, colObjWorldTransform);
rcb.m_hitFraction = resultCallback.m_closestHitFraction;
heightField->performRaycast(&rcb, rayFromLocal, rayToLocal);
}
diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp
index 65b669e1c0..9694f4ddb3 100644
--- a/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp
+++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp
@@ -27,7 +27,7 @@ btConvexPolyhedron::~btConvexPolyhedron()
{
}
-inline bool IsAlmostZero(const btVector3& v)
+inline bool IsAlmostZero1(const btVector3& v)
{
if (btFabs(v.x()) > 1e-6 || btFabs(v.y()) > 1e-6 || btFabs(v.z()) > 1e-6) return false;
return true;
@@ -122,8 +122,8 @@ void btConvexPolyhedron::initialize()
for (int p = 0; p < m_uniqueEdges.size(); p++)
{
- if (IsAlmostZero(m_uniqueEdges[p] - edge) ||
- IsAlmostZero(m_uniqueEdges[p] + edge))
+ if (IsAlmostZero1(m_uniqueEdges[p] - edge) ||
+ IsAlmostZero1(m_uniqueEdges[p] + edge))
{
found = true;
break;
diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp
index 4adf27e6bb..34ec2d8c45 100644
--- a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp
+++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp
@@ -71,9 +71,10 @@ void btHeightfieldTerrainShape::initialize(
m_flipQuadEdges = flipQuadEdges;
m_useDiamondSubdivision = false;
m_useZigzagSubdivision = false;
+ m_flipTriangleWinding = false;
m_upAxis = upAxis;
m_localScaling.setValue(btScalar(1.), btScalar(1.), btScalar(1.));
- m_vboundsGrid = NULL;
+
m_vboundsChunkSize = 0;
m_vboundsGridWidth = 0;
m_vboundsGridLength = 0;
@@ -335,30 +336,37 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
for (int x = startX; x < endX; x++)
{
btVector3 vertices[3];
+ int indices[3] = { 0, 1, 2 };
+ if (m_flipTriangleWinding)
+ {
+ indices[0] = 2;
+ indices[2] = 0;
+ }
+
if (m_flipQuadEdges || (m_useDiamondSubdivision && !((j + x) & 1)) || (m_useZigzagSubdivision && !(j & 1)))
{
//first triangle
- getVertex(x, j, vertices[0]);
- getVertex(x, j + 1, vertices[1]);
- getVertex(x + 1, j + 1, vertices[2]);
+ getVertex(x, j, vertices[indices[0]]);
+ getVertex(x, j + 1, vertices[indices[1]]);
+ getVertex(x + 1, j + 1, vertices[indices[2]]);
callback->processTriangle(vertices, x, j);
//second triangle
// getVertex(x,j,vertices[0]);//already got this vertex before, thanks to Danny Chapman
- getVertex(x + 1, j + 1, vertices[1]);
- getVertex(x + 1, j, vertices[2]);
+ getVertex(x + 1, j + 1, vertices[indices[1]]);
+ getVertex(x + 1, j, vertices[indices[2]]);
callback->processTriangle(vertices, x, j);
}
else
{
//first triangle
- getVertex(x, j, vertices[0]);
- getVertex(x, j + 1, vertices[1]);
- getVertex(x + 1, j, vertices[2]);
+ getVertex(x, j, vertices[indices[0]]);
+ getVertex(x, j + 1, vertices[indices[1]]);
+ getVertex(x + 1, j, vertices[indices[2]]);
callback->processTriangle(vertices, x, j);
//second triangle
- getVertex(x + 1, j, vertices[0]);
+ getVertex(x + 1, j, vertices[indices[0]]);
//getVertex(x,j+1,vertices[1]);
- getVertex(x + 1, j + 1, vertices[2]);
+ getVertex(x + 1, j + 1, vertices[indices[2]]);
callback->processTriangle(vertices, x, j);
}
}
@@ -381,39 +389,42 @@ const btVector3& btHeightfieldTerrainShape::getLocalScaling() const
return m_localScaling;
}
-
-
-struct GridRaycastState
+namespace
{
- int x; // Next quad coords
- int z;
- int prev_x; // Previous quad coords
- int prev_z;
- btScalar param; // Exit param for previous quad
- btScalar prevParam; // Enter param for previous quad
- btScalar maxDistanceFlat;
- btScalar maxDistance3d;
-};
-
+ struct GridRaycastState
+ {
+ int x; // Next quad coords
+ int z;
+ int prev_x; // Previous quad coords
+ int prev_z;
+ btScalar param; // Exit param for previous quad
+ btScalar prevParam; // Enter param for previous quad
+ btScalar maxDistanceFlat;
+ btScalar maxDistance3d;
+ };
+}
// TODO Does it really need to take 3D vectors?
/// Iterates through a virtual 2D grid of unit-sized square cells,
/// and executes an action on each cell intersecting the given segment, ordered from begin to end.
/// Initially inspired by http://www.cse.yorku.ca/~amana/research/grid.pdf
template <typename Action_T>
-void gridRaycast(Action_T &quadAction, const btVector3 &beginPos, const btVector3 &endPos)
+void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector3& endPos, int indices[3])
{
GridRaycastState rs;
rs.maxDistance3d = beginPos.distance(endPos);
if (rs.maxDistance3d < 0.0001)
+ {
// Consider the ray is too small to hit anything
return;
+ }
+
- btScalar rayDirectionFlatX = endPos[0] - beginPos[0];
- btScalar rayDirectionFlatZ = endPos[2] - beginPos[2];
+ btScalar rayDirectionFlatX = endPos[indices[0]] - beginPos[indices[0]];
+ btScalar rayDirectionFlatZ = endPos[indices[2]] - beginPos[indices[2]];
rs.maxDistanceFlat = btSqrt(rayDirectionFlatX * rayDirectionFlatX + rayDirectionFlatZ * rayDirectionFlatZ);
- if(rs.maxDistanceFlat < 0.0001)
+ if (rs.maxDistanceFlat < 0.0001)
{
// Consider the ray vertical
rayDirectionFlatX = 0;
@@ -433,34 +444,46 @@ void gridRaycast(Action_T &quadAction, const btVector3 &beginPos, const btVector
const btScalar paramDeltaZ = ziStep != 0 ? 1.f / btFabs(rayDirectionFlatZ) : infinite;
// pos = param * dir
- btScalar paramCrossX; // At which value of `param` we will cross a x-axis lane?
- btScalar paramCrossZ; // At which value of `param` we will cross a z-axis lane?
+ btScalar paramCrossX; // At which value of `param` we will cross a x-axis lane?
+ btScalar paramCrossZ; // At which value of `param` we will cross a z-axis lane?
// paramCrossX and paramCrossZ are initialized as being the first cross
// X initialization
if (xiStep != 0)
{
if (xiStep == 1)
- paramCrossX = (ceil(beginPos[0]) - beginPos[0]) * paramDeltaX;
+ {
+ paramCrossX = (ceil(beginPos[indices[0]]) - beginPos[indices[0]]) * paramDeltaX;
+ }
else
- paramCrossX = (beginPos[0] - floor(beginPos[0])) * paramDeltaX;
+ {
+ paramCrossX = (beginPos[indices[0]] - floor(beginPos[indices[0]])) * paramDeltaX;
+ }
}
else
- paramCrossX = infinite; // Will never cross on X
+ {
+ paramCrossX = infinite; // Will never cross on X
+ }
// Z initialization
if (ziStep != 0)
{
if (ziStep == 1)
- paramCrossZ = (ceil(beginPos[2]) - beginPos[2]) * paramDeltaZ;
+ {
+ paramCrossZ = (ceil(beginPos[indices[2]]) - beginPos[indices[2]]) * paramDeltaZ;
+ }
else
- paramCrossZ = (beginPos[2] - floor(beginPos[2])) * paramDeltaZ;
+ {
+ paramCrossZ = (beginPos[indices[2]] - floor(beginPos[indices[2]])) * paramDeltaZ;
+ }
}
else
- paramCrossZ = infinite; // Will never cross on Z
+ {
+ paramCrossZ = infinite; // Will never cross on Z
+ }
- rs.x = static_cast<int>(floor(beginPos[0]));
- rs.z = static_cast<int>(floor(beginPos[2]));
+ rs.x = static_cast<int>(floor(beginPos[indices[0]]));
+ rs.z = static_cast<int>(floor(beginPos[indices[2]]));
// Workaround cases where the ray starts at an integer position
if (paramCrossX == 0.0)
@@ -469,7 +492,9 @@ void gridRaycast(Action_T &quadAction, const btVector3 &beginPos, const btVector
// If going backwards, we should ignore the position we would get by the above flooring,
// because the ray is not heading in that direction
if (xiStep == -1)
+ {
rs.x -= 1;
+ }
}
if (paramCrossZ == 0.0)
@@ -513,14 +538,15 @@ void gridRaycast(Action_T &quadAction, const btVector3 &beginPos, const btVector
break;
}
else
+ {
quadAction(rs);
+ }
}
}
-
struct ProcessTrianglesAction
{
- const btHeightfieldTerrainShape *shape;
+ const btHeightfieldTerrainShape* shape;
bool flipQuadEdges;
bool useDiamondSubdivision;
int width;
@@ -529,11 +555,15 @@ struct ProcessTrianglesAction
void exec(int x, int z) const
{
- if(x < 0 || z < 0 || x >= width || z >= length)
+ if (x < 0 || z < 0 || x >= width || z >= length)
+ {
return;
+ }
btVector3 vertices[3];
+ // TODO Since this is for raycasts, we could greatly benefit from an early exit on the first hit
+
// Check quad
if (flipQuadEdges || (useDiamondSubdivision && (((z + x) & 1) > 0)))
{
@@ -565,16 +595,15 @@ struct ProcessTrianglesAction
}
}
- void operator ()(const GridRaycastState &bs) const
+ void operator()(const GridRaycastState& bs) const
{
exec(bs.prev_x, bs.prev_z);
}
};
-
struct ProcessVBoundsAction
{
- const btHeightfieldTerrainShape::Range *vbounds;
+ const btAlignedObjectArray<btHeightfieldTerrainShape::Range>& vbounds;
int width;
int length;
int chunkSize;
@@ -583,15 +612,23 @@ struct ProcessVBoundsAction
btVector3 rayEnd;
btVector3 rayDir;
+ int* m_indices;
ProcessTrianglesAction processTriangles;
- void operator ()(const GridRaycastState &rs) const
+ ProcessVBoundsAction(const btAlignedObjectArray<btHeightfieldTerrainShape::Range>& bnd, int* indices)
+ : vbounds(bnd),
+ m_indices(indices)
+ {
+ }
+ void operator()(const GridRaycastState& rs) const
{
int x = rs.prev_x;
int z = rs.prev_z;
- if(x < 0 || z < 0 || x >= width || z >= length)
+ if (x < 0 || z < 0 || x >= width || z >= length)
+ {
return;
+ }
const btHeightfieldTerrainShape::Range chunk = vbounds[x + z * width];
@@ -608,10 +645,14 @@ struct ProcessVBoundsAction
// We did enter the flat projection of the AABB,
// but we have to check if we intersect it on the vertical axis
- if (enterPos[1] > chunk.max && exitPos[1] > chunk.max)
+ if (enterPos[1] > chunk.max && exitPos[m_indices[1]] > chunk.max)
+ {
return;
- if (enterPos[1] < chunk.min && exitPos[1] < chunk.min)
+ }
+ if (enterPos[1] < chunk.min && exitPos[m_indices[1]] < chunk.min)
+ {
return;
+ }
}
else
{
@@ -621,13 +662,12 @@ struct ProcessVBoundsAction
exitPos = rayEnd;
}
- gridRaycast(processTriangles, enterPos, exitPos);
+ gridRaycast(processTriangles, enterPos, exitPos, m_indices);
// Note: it could be possible to have more than one grid at different levels,
// to do this there would be a branch using a pointer to another ProcessVBoundsAction
}
};
-
// TODO How do I interrupt the ray when there is a hit? `callback` does not return any result
/// Performs a raycast using a hierarchical Bresenham algorithm.
/// Does not allocate any memory by itself.
@@ -648,10 +688,16 @@ void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, con
processTriangles.length = m_heightStickLength - 1;
// TODO Transform vectors to account for m_upAxis
- int iBeginX = static_cast<int>(floor(beginPos[0]));
- int iBeginZ = static_cast<int>(floor(beginPos[2]));
- int iEndX = static_cast<int>(floor(endPos[0]));
- int iEndZ = static_cast<int>(floor(endPos[2]));
+ int indices[3] = { 0, 1, 2 };
+ if (m_upAxis == 2)
+ {
+ indices[1] = 2;
+ indices[2] = 1;
+ }
+ int iBeginX = static_cast<int>(floor(beginPos[indices[0]]));
+ int iBeginZ = static_cast<int>(floor(beginPos[indices[2]]));
+ int iEndX = static_cast<int>(floor(endPos[indices[0]]));
+ int iEndZ = static_cast<int>(floor(endPos[indices[2]]));
if (iBeginX == iEndX && iBeginZ == iEndZ)
{
@@ -662,36 +708,36 @@ void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, con
return;
}
- if (m_vboundsGrid == NULL)
+
+
+ if (m_vboundsGrid.size()==0)
{
// Process all quads intersecting the flat projection of the ray
- gridRaycast(processTriangles, beginPos, endPos);
+ gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
}
else
{
btVector3 rayDiff = endPos - beginPos;
- btScalar flatDistance2 = rayDiff[0] * rayDiff[0] + rayDiff[2] * rayDiff[2];
+ btScalar flatDistance2 = rayDiff[indices[0]] * rayDiff[indices[0]] + rayDiff[indices[2]] * rayDiff[indices[2]];
if (flatDistance2 < m_vboundsChunkSize * m_vboundsChunkSize)
{
// Don't use chunks, the ray is too short in the plane
- gridRaycast(processTriangles, beginPos, endPos);
+ gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
}
- ProcessVBoundsAction processVBounds;
+ ProcessVBoundsAction processVBounds(m_vboundsGrid, &indices[0]);
processVBounds.width = m_vboundsGridWidth;
processVBounds.length = m_vboundsGridLength;
- processVBounds.vbounds = m_vboundsGrid;
processVBounds.rayBegin = beginPos;
processVBounds.rayEnd = endPos;
processVBounds.rayDir = rayDiff.normalized();
processVBounds.processTriangles = processTriangles;
processVBounds.chunkSize = m_vboundsChunkSize;
// The ray is long, run raycast on a higher-level grid
- gridRaycast(processVBounds, beginPos / m_vboundsChunkSize, endPos / m_vboundsChunkSize);
+ gridRaycast(processVBounds, beginPos / m_vboundsChunkSize, endPos / m_vboundsChunkSize, indices);
}
}
-
/// Builds a grid data structure storing the min and max heights of the terrain in chunks.
/// if chunkSize is zero, that accelerator is removed.
/// If you modify the heights, you need to rebuild this accelerator.
@@ -708,11 +754,15 @@ void btHeightfieldTerrainShape::buildAccelerator(int chunkSize)
int nChunksZ = m_heightStickLength / chunkSize;
if (m_heightStickWidth % chunkSize > 0)
- ++nChunksX; // In case terrain size isn't dividable by chunk size
+ {
+ ++nChunksX; // In case terrain size isn't dividable by chunk size
+ }
if (m_heightStickLength % chunkSize > 0)
+ {
++nChunksZ;
+ }
- if(m_vboundsGridWidth != nChunksX || m_vboundsGridLength != nChunksZ)
+ if (m_vboundsGridWidth != nChunksX || m_vboundsGridLength != nChunksZ)
{
clearAccelerator();
m_vboundsGridWidth = nChunksX;
@@ -720,13 +770,13 @@ void btHeightfieldTerrainShape::buildAccelerator(int chunkSize)
}
if (nChunksX == 0 || nChunksZ == 0)
+ {
return;
+ }
- // TODO What is the recommended way to allocate this?
// This data structure is only reallocated if the required size changed
- if (m_vboundsGrid == NULL)
- m_vboundsGrid = new Range[nChunksX * nChunksZ];
-
+ m_vboundsGrid.resize(nChunksX * nChunksZ);
+
// Compute min and max height for all chunks
for (int cz = 0; cz < nChunksZ; ++cz)
{
@@ -760,19 +810,27 @@ void btHeightfieldTerrainShape::buildAccelerator(int chunkSize)
for (int z = z0; z < z0 + chunkSize + 1; ++z)
{
if (z >= m_heightStickLength)
+ {
continue;
+ }
for (int x = x0; x < x0 + chunkSize + 1; ++x)
{
if (x >= m_heightStickWidth)
+ {
continue;
+ }
btScalar height = getRawHeightFieldValue(x, z);
if (height < r.min)
+ {
r.min = height;
+ }
else if (height > r.max)
+ {
r.max = height;
+ }
}
}
@@ -781,15 +839,7 @@ void btHeightfieldTerrainShape::buildAccelerator(int chunkSize)
}
}
-
void btHeightfieldTerrainShape::clearAccelerator()
{
- if (m_vboundsGrid)
- {
- // TODO What is the recommended way to deallocate this?
- delete[] m_vboundsGrid;
- m_vboundsGrid = 0;
- }
-}
-
-
+ m_vboundsGrid.clear();
+} \ No newline at end of file
diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h
index e23b548cb2..43e1d25e3d 100644
--- a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h
+++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h
@@ -17,7 +17,7 @@ subject to the following restrictions:
#define BT_HEIGHTFIELD_TERRAIN_SHAPE_H
#include "btConcaveShape.h"
-
+#include "LinearMath/btAlignedObjectArray.h"
///btHeightfieldTerrainShape simulates a 2D heightfield terrain
/**
@@ -73,7 +73,8 @@ ATTRIBUTE_ALIGNED16(class)
btHeightfieldTerrainShape : public btConcaveShape
{
public:
- struct Range {
+ struct Range
+ {
btScalar min;
btScalar max;
};
@@ -102,13 +103,13 @@ protected:
bool m_flipQuadEdges;
bool m_useDiamondSubdivision;
bool m_useZigzagSubdivision;
-
+ bool m_flipTriangleWinding;
int m_upAxis;
btVector3 m_localScaling;
// Accelerator
- Range *m_vboundsGrid;
+ btAlignedObjectArray<Range> m_vboundsGrid;
int m_vboundsGridWidth;
int m_vboundsGridLength;
int m_vboundsChunkSize;
@@ -157,6 +158,10 @@ public:
///could help compatibility with Ogre heightfields. See https://code.google.com/p/bullet/issues/detail?id=625
void setUseZigzagSubdivision(bool useZigzagSubdivision = true) { m_useZigzagSubdivision = useZigzagSubdivision; }
+ void setFlipTriangleWinding(bool flipTriangleWinding)
+ {
+ m_flipTriangleWinding = flipTriangleWinding;
+ }
virtual void getAabb(const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const;
virtual void processAllTriangles(btTriangleCallback * callback, const btVector3& aabbMin, const btVector3& aabbMax) const;
@@ -166,16 +171,20 @@ public:
virtual void setLocalScaling(const btVector3& scaling);
virtual const btVector3& getLocalScaling() const;
-
- void getVertex(int x,int y,btVector3& vertex) const;
- void performRaycast (btTriangleCallback* callback, const btVector3& raySource, const btVector3& rayTarget) const;
+ void getVertex(int x, int y, btVector3& vertex) const;
+
+ void performRaycast(btTriangleCallback * callback, const btVector3& raySource, const btVector3& rayTarget) const;
- void buildAccelerator(int chunkSize=16);
+ void buildAccelerator(int chunkSize = 16);
void clearAccelerator();
+ int getUpAxis() const
+ {
+ return m_upAxis;
+ }
//debugging
virtual const char* getName() const { return "HEIGHTFIELD"; }
};
-#endif //BT_HEIGHTFIELD_TERRAIN_SHAPE_H
+#endif //BT_HEIGHTFIELD_TERRAIN_SHAPE_H \ No newline at end of file
diff --git a/thirdparty/bullet/BulletCollision/Gimpact/btGImpactBvhStructs.h b/thirdparty/bullet/BulletCollision/Gimpact/btGImpactBvhStructs.h
index 54888c6757..8f78c234b4 100644
--- a/thirdparty/bullet/BulletCollision/Gimpact/btGImpactBvhStructs.h
+++ b/thirdparty/bullet/BulletCollision/Gimpact/btGImpactBvhStructs.h
@@ -28,28 +28,7 @@ subject to the following restrictions:
#include "btBoxCollision.h"
#include "btTriangleShapeEx.h"
-
-//! Overlapping pair
-struct GIM_PAIR
-{
- int m_index1;
- int m_index2;
- GIM_PAIR()
- {
- }
-
- GIM_PAIR(const GIM_PAIR& p)
- {
- m_index1 = p.m_index1;
- m_index2 = p.m_index2;
- }
-
- GIM_PAIR(int index1, int index2)
- {
- m_index1 = index1;
- m_index2 = index2;
- }
-};
+#include "gim_pair.h" //for GIM_PAIR
///GIM_BVH_DATA is an internal GIMPACT collision structure to contain axis aligned bounding box
struct GIM_BVH_DATA
diff --git a/thirdparty/bullet/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp b/thirdparty/bullet/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp
index 3d8ab9f520..73e3db1010 100644
--- a/thirdparty/bullet/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp
+++ b/thirdparty/bullet/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp
@@ -18,7 +18,7 @@ subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
/*
-Author: Francisco Len Nßjera
+Author: Francisco Leon Najera
Concave-Concave Collision
*/
@@ -590,14 +590,16 @@ void btGImpactCollisionAlgorithm::gimpact_vs_shape(const btCollisionObjectWrappe
}
btCollisionObjectWrapper ob0(body0Wrap, colshape0, body0Wrap->getCollisionObject(), body0Wrap->getWorldTransform(), m_part0, m_triface0);
- const btCollisionObjectWrapper* prevObj0 = m_resultOut->getBody0Wrap();
+ const btCollisionObjectWrapper* prevObj;
if (m_resultOut->getBody0Wrap()->getCollisionObject() == ob0.getCollisionObject())
{
+ prevObj = m_resultOut->getBody0Wrap();
m_resultOut->setBody0Wrap(&ob0);
}
else
{
+ prevObj = m_resultOut->getBody1Wrap();
m_resultOut->setBody1Wrap(&ob0);
}
@@ -610,7 +612,15 @@ void btGImpactCollisionAlgorithm::gimpact_vs_shape(const btCollisionObjectWrappe
{
shape_vs_shape_collision(&ob0, body1Wrap, colshape0, shape1);
}
- m_resultOut->setBody0Wrap(prevObj0);
+
+ if (m_resultOut->getBody0Wrap()->getCollisionObject() == ob0.getCollisionObject())
+ {
+ m_resultOut->setBody0Wrap(prevObj);
+ }
+ else
+ {
+ m_resultOut->setBody1Wrap(prevObj);
+ }
}
shape0->unlockChildShapes();
diff --git a/thirdparty/bullet/BulletCollision/Gimpact/btGImpactShape.h b/thirdparty/bullet/BulletCollision/Gimpact/btGImpactShape.h
index 5b85e87041..eb33ce05e2 100644
--- a/thirdparty/bullet/BulletCollision/Gimpact/btGImpactShape.h
+++ b/thirdparty/bullet/BulletCollision/Gimpact/btGImpactShape.h
@@ -1,5 +1,5 @@
/*! \file btGImpactShape.h
-\author Francisco Len Nßjera
+\author Francisco Leon Najera
*/
/*
This source file is part of GIMPACT Library.
diff --git a/thirdparty/bullet/BulletCollision/Gimpact/gim_box_set.h b/thirdparty/bullet/BulletCollision/Gimpact/gim_box_set.h
index 0522007e4f..afc591dac0 100644
--- a/thirdparty/bullet/BulletCollision/Gimpact/gim_box_set.h
+++ b/thirdparty/bullet/BulletCollision/Gimpact/gim_box_set.h
@@ -37,28 +37,7 @@ email: projectileman@yahoo.com
#include "gim_radixsort.h"
#include "gim_box_collision.h"
#include "gim_tri_collision.h"
-
-//! Overlapping pair
-struct GIM_PAIR
-{
- GUINT m_index1;
- GUINT m_index2;
- GIM_PAIR()
- {
- }
-
- GIM_PAIR(const GIM_PAIR& p)
- {
- m_index1 = p.m_index1;
- m_index2 = p.m_index2;
- }
-
- GIM_PAIR(GUINT index1, GUINT index2)
- {
- m_index1 = index1;
- m_index2 = index2;
- }
-};
+#include "gim_pair.h"
//! A pairset array
class gim_pair_set : public gim_array<GIM_PAIR>
diff --git a/thirdparty/bullet/BulletCollision/Gimpact/gim_pair.h b/thirdparty/bullet/BulletCollision/Gimpact/gim_pair.h
new file mode 100644
index 0000000000..56c185a5dc
--- /dev/null
+++ b/thirdparty/bullet/BulletCollision/Gimpact/gim_pair.h
@@ -0,0 +1,28 @@
+#ifndef GIM_PAIR_H
+#define GIM_PAIR_H
+
+
+//! Overlapping pair
+struct GIM_PAIR
+{
+ int m_index1;
+ int m_index2;
+ GIM_PAIR()
+ {
+ }
+
+ GIM_PAIR(const GIM_PAIR& p)
+ {
+ m_index1 = p.m_index1;
+ m_index2 = p.m_index2;
+ }
+
+ GIM_PAIR(int index1, int index2)
+ {
+ m_index1 = index1;
+ m_index2 = index2;
+ }
+};
+
+#endif //GIM_PAIR_H
+
diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btConvexCast.h b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btConvexCast.h
index 76f54699c5..77b19be599 100644
--- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btConvexCast.h
+++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btConvexCast.h
@@ -23,11 +23,11 @@ class btMinkowskiSumShape;
#include "LinearMath/btIDebugDraw.h"
#ifdef BT_USE_DOUBLE_PRECISION
-#define MAX_ITERATIONS 64
-#define MAX_EPSILON (SIMD_EPSILON * 10)
+#define MAX_CONVEX_CAST_ITERATIONS 64
+#define MAX_CONVEX_CAST_EPSILON (SIMD_EPSILON * 10)
#else
-#define MAX_ITERATIONS 32
-#define MAX_EPSILON btScalar(0.0001)
+#define MAX_CONVEX_CAST_ITERATIONS 32
+#define MAX_CONVEX_CAST_EPSILON btScalar(0.0001)
#endif
///Typically the conservative advancement reaches solution in a few iterations, clip it to 32 for degenerate cases.
///See discussion about this here http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=565
@@ -58,8 +58,8 @@ public:
: m_fraction(btScalar(BT_LARGE_FLOAT)),
m_debugDrawer(0),
m_allowedPenetration(btScalar(0)),
- m_subSimplexCastMaxIterations(MAX_ITERATIONS),
- m_subSimplexCastEpsilon(MAX_EPSILON)
+ m_subSimplexCastMaxIterations(MAX_CONVEX_CAST_ITERATIONS),
+ m_subSimplexCastEpsilon(MAX_CONVEX_CAST_EPSILON)
{
}
diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp
index 803f6e0671..4339b2ea75 100644
--- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp
+++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp
@@ -1,4 +1,4 @@
-/*
+/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h
index 2b2dfabec2..2d0df718a2 100644
--- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h
+++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h
@@ -37,6 +37,7 @@ public:
///SubSimplexConvexCastRaytest is the default, even if kF_None is set.
kF_UseSubSimplexConvexCastRaytest = 1 << 2, // Uses an approximate but faster ray versus convex intersection algorithm
kF_UseGjkConvexCastRaytest = 1 << 3,
+ kF_DisableHeightfieldAccelerator = 1 << 4, //don't use the heightfield raycast accelerator. See https://github.com/bulletphysics/bullet3/pull/2062
kF_Terminator = 0xFFFFFFFF
};
unsigned int m_flags;
diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp
index b51dfaad3c..2a5efc6495 100644
--- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp
+++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp
@@ -22,6 +22,8 @@ subject to the following restrictions:
#include <string.h> //for memset
+#include <cmath>
+
const int kNoMerge = -1;
bool btBatchedConstraints::s_debugDrawBatches = false;
@@ -520,7 +522,7 @@ static void writeGrainSizes(btBatchedConstraints* bc)
{
const Range& phase = bc->m_phases[iPhase];
int numBatches = phase.end - phase.begin;
- float grainSize = floor((0.25f * numBatches / float(numThreads)) + 0.0f);
+ float grainSize = std::floor((0.25f * numBatches / float(numThreads)) + 0.0f);
bc->m_phaseGrainSize[iPhase] = btMax(1, int(grainSize));
}
}
diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp
index 10678b2a61..ac046aa6ea 100644
--- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp
+++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp
@@ -19,6 +19,7 @@ Written by: Marcus Hennix
#include "BulletDynamics/Dynamics/btRigidBody.h"
#include "LinearMath/btTransformUtil.h"
#include "LinearMath/btMinMax.h"
+#include <cmath>
#include <new>
//#define CONETWIST_USE_OBSOLETE_SOLVER true
@@ -842,7 +843,7 @@ void btConeTwistConstraint::computeConeLimitInfo(const btQuaternion& qCone,
btScalar norm = 1 / (m_swingSpan2 * m_swingSpan2);
norm += surfaceSlope2 / (m_swingSpan1 * m_swingSpan1);
btScalar swingLimit2 = (1 + surfaceSlope2) / norm;
- swingLimit = sqrt(swingLimit2);
+ swingLimit = std::sqrt(swingLimit2);
}
// test!
@@ -887,7 +888,7 @@ btVector3 btConeTwistConstraint::GetPointForAngle(btScalar fAngleInRadians, btSc
btScalar norm = 1 / (m_swingSpan2 * m_swingSpan2);
norm += surfaceSlope2 / (m_swingSpan1 * m_swingSpan1);
btScalar swingLimit2 = (1 + surfaceSlope2) / norm;
- swingLimit = sqrt(swingLimit2);
+ swingLimit = std::sqrt(swingLimit2);
}
// convert into point in constraint space:
diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h
index 808433477c..68a4a07a1d 100644
--- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h
+++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h
@@ -35,6 +35,7 @@ enum btConstraintSolverType
BT_MLCP_SOLVER = 2,
BT_NNCG_SOLVER = 4,
BT_MULTIBODY_SOLVER = 8,
+ BT_BLOCK_SOLVER = 16,
};
class btConstraintSolver
diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h
index 89f8db8b1a..63d7c98e16 100644
--- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h
+++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h
@@ -64,6 +64,7 @@ struct btContactSolverInfoData
btScalar m_restitutionVelocityThreshold;
bool m_jointFeedbackInWorldSpace;
bool m_jointFeedbackInJointFrame;
+ int m_reportSolverAnalytics;
};
struct btContactSolverInfo : public btContactSolverInfoData
@@ -98,6 +99,7 @@ struct btContactSolverInfo : public btContactSolverInfoData
m_restitutionVelocityThreshold = 0.2f; //if the relative velocity is below this threshold, there is zero restitution
m_jointFeedbackInWorldSpace = false;
m_jointFeedbackInJointFrame = false;
+ m_reportSolverAnalytics = 0;
}
};
diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp
index 49c8d9bbf7..9a3b39e6f8 100644
--- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp
+++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp
@@ -32,7 +32,7 @@ Cons:
/*
2007-09-09
-btGeneric6DofConstraint Refactored by Francisco Le?n
+btGeneric6DofConstraint Refactored by Francisco Leon
email: projectileman@yahoo.com
http://gimpact.sf.net
*/
@@ -40,6 +40,7 @@ http://gimpact.sf.net
#include "btGeneric6DofSpring2Constraint.h"
#include "BulletDynamics/Dynamics/btRigidBody.h"
#include "LinearMath/btTransformUtil.h"
+#include <cmath>
#include <new>
btGeneric6DofSpring2Constraint::btGeneric6DofSpring2Constraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB, RotateOrder rotOrder)
@@ -310,9 +311,9 @@ void btGeneric6DofSpring2Constraint::calculateAngleInfo()
case RO_XYZ:
{
//Is this the "line of nodes" calculation choosing planes YZ (B coordinate system) and xy (A coordinate system)? (http://en.wikipedia.org/wiki/Euler_angles)
- //The two planes are non-homologous, so this is a Tait–Bryan angle formalism and not a proper Euler
+ //The two planes are non-homologous, so this is a Tait-Bryan angle formalism and not a proper Euler
//Extrinsic rotations are equal to the reversed order intrinsic rotations so the above xyz extrinsic rotations (axes are fixed) are the same as the zy'x" intrinsic rotations (axes are refreshed after each rotation)
- //that is why xy and YZ planes are chosen (this will describe a zy'x" intrinsic rotation) (see the figure on the left at http://en.wikipedia.org/wiki/Euler_angles under Tait–Bryan angles)
+ //that is why xy and YZ planes are chosen (this will describe a zy'x" intrinsic rotation) (see the figure on the left at http://en.wikipedia.org/wiki/Euler_angles under Tait-Bryan angles)
// x' = Nperp = N.cross(axis2)
// y' = N = axis2.cross(axis0)
// z' = z
@@ -845,7 +846,7 @@ int btGeneric6DofSpring2Constraint::get_limit_motor_info2(
if (m_rbA.getInvMass() == 0) m = mB; else
if (m_rbB.getInvMass() == 0) m = mA; else
m = mA*mB / (mA + mB);
- btScalar angularfreq = sqrt(ks / m);
+ btScalar angularfreq = btSqrt(ks / m);
//limit stiffness (the spring should not be sampled faster that the quarter of its angular frequency)
if (limot->m_springStiffnessLimited && 0.25 < angularfreq * dt)
@@ -865,7 +866,7 @@ int btGeneric6DofSpring2Constraint::get_limit_motor_info2(
// vel + f / m * (rotational ? -1 : 1)
// so in theory this should be set here for m_constraintError
// (with m_constraintError we set a desired velocity for the affected body(es))
- // however in practice any value is fine as long as it is greater then the "proper" velocity,
+ // however in practice any value is fine as long as it is greater than the "proper" velocity,
// because the m_lowerLimit and the m_upperLimit will determinate the strength of the final pulling force
// so it is much simpler (and more robust) just to simply use inf (with the proper sign)
// (Even with our best intent the "new" velocity is only an estimation. If we underestimate
@@ -1085,7 +1086,7 @@ void btGeneric6DofSpring2Constraint::setServoTarget(int index, btScalar targetOr
btScalar target = targetOrg + SIMD_PI;
if (1)
{
- btScalar m = target - SIMD_2_PI * floor(target / SIMD_2_PI);
+ btScalar m = target - SIMD_2_PI * std::floor(target / SIMD_2_PI);
// handle boundary cases resulted from floating-point cut off:
{
if (m >= SIMD_2_PI)
diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h
index bc3ee6d210..00e24364e0 100644
--- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h
+++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h
@@ -294,7 +294,7 @@ protected:
bool m_hasStaticBody;
int m_flags;
- btGeneric6DofSpring2Constraint& operator=(btGeneric6DofSpring2Constraint&)
+ btGeneric6DofSpring2Constraint& operator=(const btGeneric6DofSpring2Constraint&)
{
btAssert(0);
return *this;
diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp
index def3227b43..d3b71e4583 100644
--- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp
+++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp
@@ -394,6 +394,18 @@ btSingleConstraintRowSolver btSequentialImpulseConstraintSolver::getScalarConstr
return gResolveSingleConstraintRowLowerLimit_scalar_reference;
}
+btSingleConstraintRowSolver btSequentialImpulseConstraintSolver::getScalarSplitPenetrationImpulseGeneric()
+{
+ return gResolveSplitPenetrationImpulse_scalar_reference;
+}
+
+btSingleConstraintRowSolver btSequentialImpulseConstraintSolver::getSSE2SplitPenetrationImpulseGeneric()
+{
+ return gResolveSplitPenetrationImpulse_sse2;
+}
+
+
+
#ifdef USE_SIMD
btSingleConstraintRowSolver btSequentialImpulseConstraintSolver::getSSE2ConstraintRowSolverGeneric()
{
@@ -421,6 +433,11 @@ unsigned long btSequentialImpulseConstraintSolver::btRand2()
return m_btSeed2;
}
+unsigned long btSequentialImpulseConstraintSolver::btRand2a(unsigned long& seed)
+{
+ seed = (1664525L * seed + 1013904223L) & 0xffffffff;
+ return seed;
+}
//See ODE: adam's all-int straightforward(?) dRandInt (0..n-1)
int btSequentialImpulseConstraintSolver::btRandInt2(int n)
{
@@ -454,42 +471,44 @@ int btSequentialImpulseConstraintSolver::btRandInt2(int n)
return (int)(r % un);
}
-void btSequentialImpulseConstraintSolver::initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject, btScalar timeStep)
+int btSequentialImpulseConstraintSolver::btRandInt2a(int n, unsigned long& seed)
{
- btRigidBody* rb = collisionObject ? btRigidBody::upcast(collisionObject) : 0;
-
- solverBody->internalGetDeltaLinearVelocity().setValue(0.f, 0.f, 0.f);
- solverBody->internalGetDeltaAngularVelocity().setValue(0.f, 0.f, 0.f);
- solverBody->internalGetPushVelocity().setValue(0.f, 0.f, 0.f);
- solverBody->internalGetTurnVelocity().setValue(0.f, 0.f, 0.f);
+ // seems good; xor-fold and modulus
+ const unsigned long un = static_cast<unsigned long>(n);
+ unsigned long r = btSequentialImpulseConstraintSolver::btRand2a(seed);
- if (rb)
- {
- solverBody->m_worldTransform = rb->getWorldTransform();
- solverBody->internalSetInvMass(btVector3(rb->getInvMass(), rb->getInvMass(), rb->getInvMass()) * rb->getLinearFactor());
- solverBody->m_originalBody = rb;
- solverBody->m_angularFactor = rb->getAngularFactor();
- solverBody->m_linearFactor = rb->getLinearFactor();
- solverBody->m_linearVelocity = rb->getLinearVelocity();
- solverBody->m_angularVelocity = rb->getAngularVelocity();
- solverBody->m_externalForceImpulse = rb->getTotalForce() * rb->getInvMass() * timeStep;
- solverBody->m_externalTorqueImpulse = rb->getTotalTorque() * rb->getInvInertiaTensorWorld() * timeStep;
- }
- else
+ // note: probably more aggressive than it needs to be -- might be
+ // able to get away without one or two of the innermost branches.
+ if (un <= 0x00010000UL)
{
- solverBody->m_worldTransform.setIdentity();
- solverBody->internalSetInvMass(btVector3(0, 0, 0));
- solverBody->m_originalBody = 0;
- solverBody->m_angularFactor.setValue(1, 1, 1);
- solverBody->m_linearFactor.setValue(1, 1, 1);
- solverBody->m_linearVelocity.setValue(0, 0, 0);
- solverBody->m_angularVelocity.setValue(0, 0, 0);
- solverBody->m_externalForceImpulse.setValue(0, 0, 0);
- solverBody->m_externalTorqueImpulse.setValue(0, 0, 0);
+ r ^= (r >> 16);
+ if (un <= 0x00000100UL)
+ {
+ r ^= (r >> 8);
+ if (un <= 0x00000010UL)
+ {
+ r ^= (r >> 4);
+ if (un <= 0x00000004UL)
+ {
+ r ^= (r >> 2);
+ if (un <= 0x00000002UL)
+ {
+ r ^= (r >> 1);
+ }
+ }
+ }
+ }
}
+
+ return (int)(r % un);
}
-btScalar btSequentialImpulseConstraintSolver::restitutionCurve(btScalar rel_vel, btScalar restitution, btScalar velocityThreshold)
+void btSequentialImpulseConstraintSolver::initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject, btScalar timeStep)
+{
+ btSISolverSingleIterationData::initSolverBody(solverBody, collisionObject, timeStep);
+}
+
+btScalar btSequentialImpulseConstraintSolver::restitutionCurveInternal(btScalar rel_vel, btScalar restitution, btScalar velocityThreshold)
{
//printf("rel_vel =%f\n", rel_vel);
if (btFabs(rel_vel) < velocityThreshold)
@@ -498,6 +517,10 @@ btScalar btSequentialImpulseConstraintSolver::restitutionCurve(btScalar rel_vel,
btScalar rest = restitution * -rel_vel;
return rest;
}
+btScalar btSequentialImpulseConstraintSolver::restitutionCurve(btScalar rel_vel, btScalar restitution, btScalar velocityThreshold)
+{
+ return btSequentialImpulseConstraintSolver::restitutionCurveInternal(rel_vel, restitution, velocityThreshold);
+}
void btSequentialImpulseConstraintSolver::applyAnisotropicFriction(btCollisionObject* colObj, btVector3& frictionDirection, int frictionMode)
{
@@ -513,13 +536,13 @@ void btSequentialImpulseConstraintSolver::applyAnisotropicFriction(btCollisionOb
}
}
-void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstraint& solverConstraint, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip)
+void btSequentialImpulseConstraintSolver::setupFrictionConstraintInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btSolverConstraint& solverConstraint, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip)
{
- btSolverBody& solverBodyA = m_tmpSolverBodyPool[solverBodyIdA];
- btSolverBody& solverBodyB = m_tmpSolverBodyPool[solverBodyIdB];
+ btSolverBody& solverBodyA = tmpSolverBodyPool[solverBodyIdA];
+ btSolverBody& solverBodyB = tmpSolverBodyPool[solverBodyIdB];
- btRigidBody* body0 = m_tmpSolverBodyPool[solverBodyIdA].m_originalBody;
- btRigidBody* bodyA = m_tmpSolverBodyPool[solverBodyIdB].m_originalBody;
+ btRigidBody* body0 = tmpSolverBodyPool[solverBodyIdA].m_originalBody;
+ btRigidBody* bodyA = tmpSolverBodyPool[solverBodyIdB].m_originalBody;
solverConstraint.m_solverBodyIdA = solverBodyIdA;
solverConstraint.m_solverBodyIdB = solverBodyIdB;
@@ -605,30 +628,47 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr
}
}
+void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstraint& solverConstraint, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip)
+{
+ btSequentialImpulseConstraintSolver::setupFrictionConstraintInternal(m_tmpSolverBodyPool, solverConstraint, normalAxis, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, desiredVelocity, cfmSlip);
+}
+
+btSolverConstraint& btSequentialImpulseConstraintSolver::addFrictionConstraintInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btConstraintArray& tmpSolverContactFrictionConstraintPool, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip)
+{
+ btSolverConstraint& solverConstraint = tmpSolverContactFrictionConstraintPool.expandNonInitializing();
+ solverConstraint.m_frictionIndex = frictionIndex;
+ setupFrictionConstraintInternal(tmpSolverBodyPool, solverConstraint, normalAxis, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2,
+ colObj0, colObj1, relaxation, infoGlobal, desiredVelocity, cfmSlip);
+ return solverConstraint;
+}
+
+
+
btSolverConstraint& btSequentialImpulseConstraintSolver::addFrictionConstraint(const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip)
{
btSolverConstraint& solverConstraint = m_tmpSolverContactFrictionConstraintPool.expandNonInitializing();
solverConstraint.m_frictionIndex = frictionIndex;
setupFrictionConstraint(solverConstraint, normalAxis, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2,
- colObj0, colObj1, relaxation, infoGlobal, desiredVelocity, cfmSlip);
+ colObj0, colObj1, relaxation, infoGlobal, desiredVelocity, cfmSlip);
return solverConstraint;
}
-void btSequentialImpulseConstraintSolver::setupTorsionalFrictionConstraint(btSolverConstraint& solverConstraint, const btVector3& normalAxis1, int solverBodyIdA, int solverBodyIdB,
- btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2,
- btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation,
- btScalar desiredVelocity, btScalar cfmSlip)
+
+void btSequentialImpulseConstraintSolver::setupTorsionalFrictionConstraintInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btSolverConstraint& solverConstraint, const btVector3& normalAxis1, int solverBodyIdA, int solverBodyIdB,
+ btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2,
+ btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation,
+ btScalar desiredVelocity, btScalar cfmSlip)
{
btVector3 normalAxis(0, 0, 0);
solverConstraint.m_contactNormal1 = normalAxis;
solverConstraint.m_contactNormal2 = -normalAxis;
- btSolverBody& solverBodyA = m_tmpSolverBodyPool[solverBodyIdA];
- btSolverBody& solverBodyB = m_tmpSolverBodyPool[solverBodyIdB];
+ btSolverBody& solverBodyA = tmpSolverBodyPool[solverBodyIdA];
+ btSolverBody& solverBodyB = tmpSolverBodyPool[solverBodyIdB];
- btRigidBody* body0 = m_tmpSolverBodyPool[solverBodyIdA].m_originalBody;
- btRigidBody* bodyA = m_tmpSolverBodyPool[solverBodyIdB].m_originalBody;
+ btRigidBody* body0 = tmpSolverBodyPool[solverBodyIdA].m_originalBody;
+ btRigidBody* bodyA = tmpSolverBodyPool[solverBodyIdB].m_originalBody;
solverConstraint.m_solverBodyIdA = solverBodyIdA;
solverConstraint.m_solverBodyIdB = solverBodyIdB;
@@ -677,15 +717,250 @@ void btSequentialImpulseConstraintSolver::setupTorsionalFrictionConstraint(btSol
}
}
+
+void btSequentialImpulseConstraintSolver::setupTorsionalFrictionConstraint(btSolverConstraint& solverConstraint, const btVector3& normalAxis1, int solverBodyIdA, int solverBodyIdB,
+ btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2,
+ btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation,
+ btScalar desiredVelocity, btScalar cfmSlip)
+
+{
+ setupTorsionalFrictionConstraintInternal(m_tmpSolverBodyPool, solverConstraint, normalAxis1, solverBodyIdA, solverBodyIdB,
+ cp, combinedTorsionalFriction, rel_pos1, rel_pos2,
+ colObj0, colObj1, relaxation,
+ desiredVelocity, cfmSlip);
+
+}
+
+btSolverConstraint& btSequentialImpulseConstraintSolver::addTorsionalFrictionConstraintInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btConstraintArray& tmpSolverContactRollingFrictionConstraintPool, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, btScalar desiredVelocity, btScalar cfmSlip)
+{
+ btSolverConstraint& solverConstraint = tmpSolverContactRollingFrictionConstraintPool.expandNonInitializing();
+ solverConstraint.m_frictionIndex = frictionIndex;
+ setupTorsionalFrictionConstraintInternal(tmpSolverBodyPool, solverConstraint, normalAxis, solverBodyIdA, solverBodyIdB, cp, combinedTorsionalFriction, rel_pos1, rel_pos2,
+ colObj0, colObj1, relaxation, desiredVelocity, cfmSlip);
+ return solverConstraint;
+}
+
+
btSolverConstraint& btSequentialImpulseConstraintSolver::addTorsionalFrictionConstraint(const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, btScalar desiredVelocity, btScalar cfmSlip)
{
btSolverConstraint& solverConstraint = m_tmpSolverContactRollingFrictionConstraintPool.expandNonInitializing();
solverConstraint.m_frictionIndex = frictionIndex;
setupTorsionalFrictionConstraint(solverConstraint, normalAxis, solverBodyIdA, solverBodyIdB, cp, combinedTorsionalFriction, rel_pos1, rel_pos2,
- colObj0, colObj1, relaxation, desiredVelocity, cfmSlip);
+ colObj0, colObj1, relaxation, desiredVelocity, cfmSlip);
return solverConstraint;
}
+int btSISolverSingleIterationData::getOrInitSolverBody(btCollisionObject & body, btScalar timeStep)
+{
+#if BT_THREADSAFE
+ int solverBodyId = -1;
+ bool isRigidBodyType = btRigidBody::upcast(&body) != NULL;
+ if (isRigidBodyType && !body.isStaticOrKinematicObject())
+ {
+ // dynamic body
+ // Dynamic bodies can only be in one island, so it's safe to write to the companionId
+ solverBodyId = body.getCompanionId();
+ if (solverBodyId < 0)
+ {
+ solverBodyId = m_tmpSolverBodyPool.size();
+ btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
+ initSolverBody(&solverBody, &body, timeStep);
+ body.setCompanionId(solverBodyId);
+ }
+ }
+ else if (isRigidBodyType && body.isKinematicObject())
+ {
+ //
+ // NOTE: must test for kinematic before static because some kinematic objects also
+ // identify as "static"
+ //
+ // Kinematic bodies can be in multiple islands at once, so it is a
+ // race condition to write to them, so we use an alternate method
+ // to record the solverBodyId
+ int uniqueId = body.getWorldArrayIndex();
+ const int INVALID_SOLVER_BODY_ID = -1;
+ if (uniqueId >= m_kinematicBodyUniqueIdToSolverBodyTable.size())
+ {
+ m_kinematicBodyUniqueIdToSolverBodyTable.resize(uniqueId + 1, INVALID_SOLVER_BODY_ID);
+ }
+ solverBodyId = m_kinematicBodyUniqueIdToSolverBodyTable[uniqueId];
+ // if no table entry yet,
+ if (solverBodyId == INVALID_SOLVER_BODY_ID)
+ {
+ // create a table entry for this body
+ solverBodyId = m_tmpSolverBodyPool.size();
+ btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
+ initSolverBody(&solverBody, &body, timeStep);
+ m_kinematicBodyUniqueIdToSolverBodyTable[uniqueId] = solverBodyId;
+ }
+ }
+ else
+ {
+ bool isMultiBodyType = (body.getInternalType() & btCollisionObject::CO_FEATHERSTONE_LINK);
+ // Incorrectly set collision object flags can degrade performance in various ways.
+ if (!isMultiBodyType)
+ {
+ btAssert(body.isStaticOrKinematicObject());
+ }
+ //it could be a multibody link collider
+ // all fixed bodies (inf mass) get mapped to a single solver id
+ if (m_fixedBodyId < 0)
+ {
+ m_fixedBodyId = m_tmpSolverBodyPool.size();
+ btSolverBody& fixedBody = m_tmpSolverBodyPool.expand();
+ initSolverBody(&fixedBody, 0, timeStep);
+ }
+ solverBodyId = m_fixedBodyId;
+ }
+ btAssert(solverBodyId >= 0 && solverBodyId < m_tmpSolverBodyPool.size());
+ return solverBodyId;
+#else // BT_THREADSAFE
+
+ int solverBodyIdA = -1;
+
+ if (body.getCompanionId() >= 0)
+ {
+ //body has already been converted
+ solverBodyIdA = body.getCompanionId();
+ btAssert(solverBodyIdA < m_tmpSolverBodyPool.size());
+ }
+ else
+ {
+ btRigidBody* rb = btRigidBody::upcast(&body);
+ //convert both active and kinematic objects (for their velocity)
+ if (rb && (rb->getInvMass() || rb->isKinematicObject()))
+ {
+ solverBodyIdA = m_tmpSolverBodyPool.size();
+ btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
+ initSolverBody(&solverBody, &body, timeStep);
+ body.setCompanionId(solverBodyIdA);
+ }
+ else
+ {
+ if (m_fixedBodyId < 0)
+ {
+ m_fixedBodyId = m_tmpSolverBodyPool.size();
+ btSolverBody& fixedBody = m_tmpSolverBodyPool.expand();
+ initSolverBody(&fixedBody, 0, timeStep);
+ }
+ return m_fixedBodyId;
+ // return 0;//assume first one is a fixed solver body
+ }
+ }
+
+ return solverBodyIdA;
+#endif // BT_THREADSAFE
+}
+void btSISolverSingleIterationData::initSolverBody(btSolverBody * solverBody, btCollisionObject * collisionObject, btScalar timeStep)
+{
+ btRigidBody* rb = collisionObject ? btRigidBody::upcast(collisionObject) : 0;
+
+ solverBody->internalGetDeltaLinearVelocity().setValue(0.f, 0.f, 0.f);
+ solverBody->internalGetDeltaAngularVelocity().setValue(0.f, 0.f, 0.f);
+ solverBody->internalGetPushVelocity().setValue(0.f, 0.f, 0.f);
+ solverBody->internalGetTurnVelocity().setValue(0.f, 0.f, 0.f);
+
+ if (rb)
+ {
+ solverBody->m_worldTransform = rb->getWorldTransform();
+ solverBody->internalSetInvMass(btVector3(rb->getInvMass(), rb->getInvMass(), rb->getInvMass()) * rb->getLinearFactor());
+ solverBody->m_originalBody = rb;
+ solverBody->m_angularFactor = rb->getAngularFactor();
+ solverBody->m_linearFactor = rb->getLinearFactor();
+ solverBody->m_linearVelocity = rb->getLinearVelocity();
+ solverBody->m_angularVelocity = rb->getAngularVelocity();
+ solverBody->m_externalForceImpulse = rb->getTotalForce() * rb->getInvMass() * timeStep;
+ solverBody->m_externalTorqueImpulse = rb->getTotalTorque() * rb->getInvInertiaTensorWorld() * timeStep;
+ }
+ else
+ {
+ solverBody->m_worldTransform.setIdentity();
+ solverBody->internalSetInvMass(btVector3(0, 0, 0));
+ solverBody->m_originalBody = 0;
+ solverBody->m_angularFactor.setValue(1, 1, 1);
+ solverBody->m_linearFactor.setValue(1, 1, 1);
+ solverBody->m_linearVelocity.setValue(0, 0, 0);
+ solverBody->m_angularVelocity.setValue(0, 0, 0);
+ solverBody->m_externalForceImpulse.setValue(0, 0, 0);
+ solverBody->m_externalTorqueImpulse.setValue(0, 0, 0);
+ }
+}
+
+int btSISolverSingleIterationData::getSolverBody(btCollisionObject& body) const
+{
+#if BT_THREADSAFE
+ int solverBodyId = -1;
+ bool isRigidBodyType = btRigidBody::upcast(&body) != NULL;
+ if (isRigidBodyType && !body.isStaticOrKinematicObject())
+ {
+ // dynamic body
+ // Dynamic bodies can only be in one island, so it's safe to write to the companionId
+ solverBodyId = body.getCompanionId();
+ btAssert(solverBodyId >= 0);
+ }
+ else if (isRigidBodyType && body.isKinematicObject())
+ {
+ //
+ // NOTE: must test for kinematic before static because some kinematic objects also
+ // identify as "static"
+ //
+ // Kinematic bodies can be in multiple islands at once, so it is a
+ // race condition to write to them, so we use an alternate method
+ // to record the solverBodyId
+ int uniqueId = body.getWorldArrayIndex();
+ const int INVALID_SOLVER_BODY_ID = -1;
+ if (uniqueId >= m_kinematicBodyUniqueIdToSolverBodyTable.size())
+ {
+ m_kinematicBodyUniqueIdToSolverBodyTable.resize(uniqueId + 1, INVALID_SOLVER_BODY_ID);
+ }
+ solverBodyId = m_kinematicBodyUniqueIdToSolverBodyTable[uniqueId];
+ btAssert(solverBodyId != INVALID_SOLVER_BODY_ID);
+ }
+ else
+ {
+ bool isMultiBodyType = (body.getInternalType() & btCollisionObject::CO_FEATHERSTONE_LINK);
+ // Incorrectly set collision object flags can degrade performance in various ways.
+ if (!isMultiBodyType)
+ {
+ btAssert(body.isStaticOrKinematicObject());
+ }
+ btAssert(m_fixedBodyId >= 0);
+ solverBodyId = m_fixedBodyId;
+ }
+ btAssert(solverBodyId >= 0 && solverBodyId < m_tmpSolverBodyPool.size());
+ return solverBodyId;
+#else // BT_THREADSAFE
+ int solverBodyIdA = -1;
+
+ if (body.getCompanionId() >= 0)
+ {
+ //body has already been converted
+ solverBodyIdA = body.getCompanionId();
+ btAssert(solverBodyIdA < m_tmpSolverBodyPool.size());
+ }
+ else
+ {
+ btRigidBody* rb = btRigidBody::upcast(&body);
+ //convert both active and kinematic objects (for their velocity)
+ if (rb && (rb->getInvMass() || rb->isKinematicObject()))
+ {
+ btAssert(0);
+ }
+ else
+ {
+ if (m_fixedBodyId < 0)
+ {
+ btAssert(0);
+ }
+ return m_fixedBodyId;
+ // return 0;//assume first one is a fixed solver body
+ }
+ }
+
+ return solverBodyIdA;
+#endif // BT_THREADSAFE
+}
+
int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& body, btScalar timeStep)
{
#if BT_THREADSAFE
@@ -789,17 +1064,20 @@ int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject&
}
#include <stdio.h>
-void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstraint& solverConstraint,
- int solverBodyIdA, int solverBodyIdB,
- btManifoldPoint& cp, const btContactSolverInfo& infoGlobal,
- btScalar& relaxation,
- const btVector3& rel_pos1, const btVector3& rel_pos2)
+
+
+void btSequentialImpulseConstraintSolver::setupContactConstraintInternal(btSISolverSingleIterationData& siData,
+ btSolverConstraint& solverConstraint,
+ int solverBodyIdA, int solverBodyIdB,
+ btManifoldPoint& cp, const btContactSolverInfo& infoGlobal,
+ btScalar& relaxation,
+ const btVector3& rel_pos1, const btVector3& rel_pos2)
{
// const btVector3& pos1 = cp.getPositionWorldOnA();
// const btVector3& pos2 = cp.getPositionWorldOnB();
- btSolverBody* bodyA = &m_tmpSolverBodyPool[solverBodyIdA];
- btSolverBody* bodyB = &m_tmpSolverBodyPool[solverBodyIdB];
+ btSolverBody* bodyA = &siData.m_tmpSolverBodyPool[solverBodyIdA];
+ btSolverBody* bodyB = &siData.m_tmpSolverBodyPool[solverBodyIdB];
btRigidBody* rb0 = bodyA->m_originalBody;
btRigidBody* rb1 = bodyB->m_originalBody;
@@ -906,7 +1184,7 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra
solverConstraint.m_friction = cp.m_combinedFriction;
- restitution = restitutionCurve(rel_vel, cp.m_combinedRestitution, infoGlobal.m_restitutionVelocityThreshold);
+ restitution = btSequentialImpulseConstraintSolver::restitutionCurveInternal(rel_vel, cp.m_combinedRestitution, infoGlobal.m_restitutionVelocityThreshold);
if (restitution <= btScalar(0.))
{
restitution = 0.f;
@@ -920,7 +1198,7 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra
if (rb0)
bodyA->internalApplyImpulse(solverConstraint.m_contactNormal1 * bodyA->internalGetInvMass(), solverConstraint.m_angularComponentA, solverConstraint.m_appliedImpulse);
if (rb1)
- bodyB->internalApplyImpulse(-solverConstraint.m_contactNormal2 * bodyB->internalGetInvMass() , -solverConstraint.m_angularComponentB, -(btScalar)solverConstraint.m_appliedImpulse);
+ bodyB->internalApplyImpulse(-solverConstraint.m_contactNormal2 * bodyB->internalGetInvMass(), -solverConstraint.m_angularComponentB, -(btScalar)solverConstraint.m_appliedImpulse);
}
else
{
@@ -974,25 +1252,59 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra
}
}
-void btSequentialImpulseConstraintSolver::setFrictionConstraintImpulse(btSolverConstraint& solverConstraint,
- int solverBodyIdA, int solverBodyIdB,
- btManifoldPoint& cp, const btContactSolverInfo& infoGlobal)
+void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstraint& solverConstraint,
+ int solverBodyIdA, int solverBodyIdB,
+ btManifoldPoint& cp, const btContactSolverInfo& infoGlobal,
+ btScalar& relaxation,
+ const btVector3& rel_pos1, const btVector3& rel_pos2)
+{
+ btSISolverSingleIterationData siData(m_tmpSolverBodyPool,
+ m_tmpSolverContactConstraintPool,
+ m_tmpSolverNonContactConstraintPool,
+ m_tmpSolverContactFrictionConstraintPool,
+ m_tmpSolverContactRollingFrictionConstraintPool,
+ m_orderTmpConstraintPool,
+ m_orderNonContactConstraintPool,
+ m_orderFrictionConstraintPool,
+ m_tmpConstraintSizesPool,
+ m_resolveSingleConstraintRowGeneric,
+ m_resolveSingleConstraintRowLowerLimit,
+ m_resolveSplitPenetrationImpulse,
+ m_kinematicBodyUniqueIdToSolverBodyTable,
+ m_btSeed2,
+ m_fixedBodyId,
+ m_maxOverrideNumSolverIterations
+ );
+
+
+ setupContactConstraintInternal(siData, solverConstraint,
+ solverBodyIdA, solverBodyIdB,
+ cp, infoGlobal,
+ relaxation,
+ rel_pos1, rel_pos2);
+}
+
+
+void btSequentialImpulseConstraintSolver::setFrictionConstraintImpulseInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btConstraintArray& tmpSolverContactFrictionConstraintPool,
+ btSolverConstraint& solverConstraint,
+ int solverBodyIdA, int solverBodyIdB,
+ btManifoldPoint& cp, const btContactSolverInfo& infoGlobal)
{
- btSolverBody* bodyA = &m_tmpSolverBodyPool[solverBodyIdA];
- btSolverBody* bodyB = &m_tmpSolverBodyPool[solverBodyIdB];
+ btSolverBody* bodyA = &tmpSolverBodyPool[solverBodyIdA];
+ btSolverBody* bodyB = &tmpSolverBodyPool[solverBodyIdB];
btRigidBody* rb0 = bodyA->m_originalBody;
btRigidBody* rb1 = bodyB->m_originalBody;
{
- btSolverConstraint& frictionConstraint1 = m_tmpSolverContactFrictionConstraintPool[solverConstraint.m_frictionIndex];
+ btSolverConstraint& frictionConstraint1 = tmpSolverContactFrictionConstraintPool[solverConstraint.m_frictionIndex];
if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING)
{
frictionConstraint1.m_appliedImpulse = cp.m_appliedImpulseLateral1 * infoGlobal.m_warmstartingFactor;
if (rb0)
- bodyA->internalApplyImpulse(frictionConstraint1.m_contactNormal1 * rb0->getInvMass() , frictionConstraint1.m_angularComponentA, frictionConstraint1.m_appliedImpulse);
+ bodyA->internalApplyImpulse(frictionConstraint1.m_contactNormal1 * rb0->getInvMass(), frictionConstraint1.m_angularComponentA, frictionConstraint1.m_appliedImpulse);
if (rb1)
- bodyB->internalApplyImpulse(-frictionConstraint1.m_contactNormal2 * rb1->getInvMass() , -frictionConstraint1.m_angularComponentB, -(btScalar)frictionConstraint1.m_appliedImpulse);
+ bodyB->internalApplyImpulse(-frictionConstraint1.m_contactNormal2 * rb1->getInvMass(), -frictionConstraint1.m_angularComponentB, -(btScalar)frictionConstraint1.m_appliedImpulse);
}
else
{
@@ -1002,7 +1314,7 @@ void btSequentialImpulseConstraintSolver::setFrictionConstraintImpulse(btSolverC
if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS))
{
- btSolverConstraint& frictionConstraint2 = m_tmpSolverContactFrictionConstraintPool[solverConstraint.m_frictionIndex + 1];
+ btSolverConstraint& frictionConstraint2 = tmpSolverContactFrictionConstraintPool[solverConstraint.m_frictionIndex + 1];
if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING)
{
frictionConstraint2.m_appliedImpulse = cp.m_appliedImpulseLateral2 * infoGlobal.m_warmstartingFactor;
@@ -1018,21 +1330,31 @@ void btSequentialImpulseConstraintSolver::setFrictionConstraintImpulse(btSolverC
}
}
-void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* manifold, const btContactSolverInfo& infoGlobal)
+void btSequentialImpulseConstraintSolver::setFrictionConstraintImpulse(btSolverConstraint& solverConstraint,
+ int solverBodyIdA, int solverBodyIdB,
+ btManifoldPoint& cp, const btContactSolverInfo& infoGlobal)
+{
+ setFrictionConstraintImpulseInternal(m_tmpSolverBodyPool, m_tmpSolverContactFrictionConstraintPool,
+ solverConstraint,
+ solverBodyIdA, solverBodyIdB,
+ cp, infoGlobal);
+
+}
+void btSequentialImpulseConstraintSolver::convertContactInternal(btSISolverSingleIterationData& siData, btPersistentManifold* manifold, const btContactSolverInfo& infoGlobal)
{
btCollisionObject *colObj0 = 0, *colObj1 = 0;
colObj0 = (btCollisionObject*)manifold->getBody0();
colObj1 = (btCollisionObject*)manifold->getBody1();
- int solverBodyIdA = getOrInitSolverBody(*colObj0, infoGlobal.m_timeStep);
- int solverBodyIdB = getOrInitSolverBody(*colObj1, infoGlobal.m_timeStep);
+ int solverBodyIdA = siData.getOrInitSolverBody(*colObj0, infoGlobal.m_timeStep);
+ int solverBodyIdB = siData.getOrInitSolverBody(*colObj1, infoGlobal.m_timeStep);
// btRigidBody* bodyA = btRigidBody::upcast(colObj0);
// btRigidBody* bodyB = btRigidBody::upcast(colObj1);
- btSolverBody* solverBodyA = &m_tmpSolverBodyPool[solverBodyIdA];
- btSolverBody* solverBodyB = &m_tmpSolverBodyPool[solverBodyIdB];
+ btSolverBody* solverBodyA = &siData.m_tmpSolverBodyPool[solverBodyIdA];
+ btSolverBody* solverBodyB = &siData.m_tmpSolverBodyPool[solverBodyIdB];
///avoid collision response between two static objects
if (!solverBodyA || (solverBodyA->m_invMass.fuzzyZero() && (!solverBodyB || solverBodyB->m_invMass.fuzzyZero())))
@@ -1049,8 +1371,8 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
btVector3 rel_pos2;
btScalar relaxation;
- int frictionIndex = m_tmpSolverContactConstraintPool.size();
- btSolverConstraint& solverConstraint = m_tmpSolverContactConstraintPool.expandNonInitializing();
+ int frictionIndex = siData.m_tmpSolverContactConstraintPool.size();
+ btSolverConstraint& solverConstraint = siData.m_tmpSolverContactConstraintPool.expandNonInitializing();
solverConstraint.m_solverBodyIdA = solverBodyIdA;
solverConstraint.m_solverBodyIdB = solverBodyIdB;
@@ -1071,16 +1393,20 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
btVector3 vel = vel1 - vel2;
btScalar rel_vel = cp.m_normalWorldOnB.dot(vel);
- setupContactConstraint(solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal, relaxation, rel_pos1, rel_pos2);
+ setupContactConstraintInternal(siData, solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal, relaxation, rel_pos1, rel_pos2);
/////setup the friction constraints
- solverConstraint.m_frictionIndex = m_tmpSolverContactFrictionConstraintPool.size();
+ solverConstraint.m_frictionIndex = siData.m_tmpSolverContactFrictionConstraintPool.size();
if ((cp.m_combinedRollingFriction > 0.f) && (rollingFriction > 0))
{
{
- addTorsionalFrictionConstraint(cp.m_normalWorldOnB, solverBodyIdA, solverBodyIdB, frictionIndex, cp, cp.m_combinedSpinningFriction, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+
+ btSequentialImpulseConstraintSolver::addTorsionalFrictionConstraintInternal(siData.m_tmpSolverBodyPool,
+ siData.m_tmpSolverContactRollingFrictionConstraintPool,
+ cp.m_normalWorldOnB, solverBodyIdA, solverBodyIdB, frictionIndex, cp, cp.m_combinedSpinningFriction, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+
btVector3 axis0, axis1;
btPlaneSpace1(cp.m_normalWorldOnB, axis0, axis1);
axis0.normalize();
@@ -1091,11 +1417,17 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
applyAnisotropicFriction(colObj0, axis1, btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
applyAnisotropicFriction(colObj1, axis1, btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
if (axis0.length() > 0.001)
- addTorsionalFrictionConstraint(axis0, solverBodyIdA, solverBodyIdB, frictionIndex, cp,
- cp.m_combinedRollingFriction, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+ {
+ btSequentialImpulseConstraintSolver::addTorsionalFrictionConstraintInternal(siData.m_tmpSolverBodyPool,
+ siData.m_tmpSolverContactRollingFrictionConstraintPool, axis0, solverBodyIdA, solverBodyIdB, frictionIndex, cp,
+ cp.m_combinedRollingFriction, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+ }
if (axis1.length() > 0.001)
- addTorsionalFrictionConstraint(axis1, solverBodyIdA, solverBodyIdB, frictionIndex, cp,
- cp.m_combinedRollingFriction, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+ {
+ btSequentialImpulseConstraintSolver::addTorsionalFrictionConstraintInternal(siData.m_tmpSolverBodyPool,
+ siData.m_tmpSolverContactRollingFrictionConstraintPool, axis1, solverBodyIdA, solverBodyIdB, frictionIndex, cp,
+ cp.m_combinedRollingFriction, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+ }
}
}
@@ -1124,7 +1456,8 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
cp.m_lateralFrictionDir1 *= 1.f / btSqrt(lat_rel_vel);
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION);
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION);
- addFrictionConstraint(cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
+ btSequentialImpulseConstraintSolver::addFrictionConstraintInternal(siData.m_tmpSolverBodyPool, siData.m_tmpSolverContactFrictionConstraintPool,
+ cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS))
{
@@ -1132,7 +1465,8 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
cp.m_lateralFrictionDir2.normalize(); //??
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION);
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION);
- addFrictionConstraint(cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
+ btSequentialImpulseConstraintSolver::addFrictionConstraintInternal(siData.m_tmpSolverBodyPool, siData.m_tmpSolverContactFrictionConstraintPool,
+ cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
}
}
else
@@ -1141,13 +1475,15 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION);
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION);
- addFrictionConstraint(cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
+ btSequentialImpulseConstraintSolver::addFrictionConstraintInternal(siData.m_tmpSolverBodyPool, siData.m_tmpSolverContactFrictionConstraintPool,
+ cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS))
{
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION);
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION);
- addFrictionConstraint(cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
+ btSequentialImpulseConstraintSolver::addFrictionConstraintInternal(siData.m_tmpSolverBodyPool, siData.m_tmpSolverContactFrictionConstraintPool,
+ cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal);
}
if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS) && (infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION))
@@ -1158,16 +1494,44 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m
}
else
{
- addFrictionConstraint(cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, cp.m_contactMotion1, cp.m_frictionCFM);
+ btSequentialImpulseConstraintSolver::addFrictionConstraintInternal(siData.m_tmpSolverBodyPool, siData.m_tmpSolverContactFrictionConstraintPool,
+ cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, cp.m_contactMotion1, cp.m_frictionCFM);
if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS))
- addFrictionConstraint(cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, cp.m_contactMotion2, cp.m_frictionCFM);
+ {
+ btSequentialImpulseConstraintSolver::addFrictionConstraintInternal(siData.m_tmpSolverBodyPool, siData.m_tmpSolverContactFrictionConstraintPool,
+ cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, cp.m_contactMotion2, cp.m_frictionCFM);
+ }
}
- setFrictionConstraintImpulse(solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal);
+ btSequentialImpulseConstraintSolver::setFrictionConstraintImpulseInternal(
+ siData.m_tmpSolverBodyPool, siData.m_tmpSolverContactFrictionConstraintPool,
+ solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal);
}
}
}
+void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* manifold, const btContactSolverInfo& infoGlobal)
+{
+ btSISolverSingleIterationData siData(m_tmpSolverBodyPool,
+ m_tmpSolverContactConstraintPool,
+ m_tmpSolverNonContactConstraintPool,
+ m_tmpSolverContactFrictionConstraintPool,
+ m_tmpSolverContactRollingFrictionConstraintPool,
+ m_orderTmpConstraintPool,
+ m_orderNonContactConstraintPool,
+ m_orderFrictionConstraintPool,
+ m_tmpConstraintSizesPool,
+ m_resolveSingleConstraintRowGeneric,
+ m_resolveSingleConstraintRowLowerLimit,
+ m_resolveSplitPenetrationImpulse,
+ m_kinematicBodyUniqueIdToSolverBodyTable,
+ m_btSeed2,
+ m_fixedBodyId,
+ m_maxOverrideNumSolverIterations);
+
+ btSequentialImpulseConstraintSolver::convertContactInternal(siData, manifold, infoGlobal);
+}
+
void btSequentialImpulseConstraintSolver::convertContacts(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal)
{
int i;
@@ -1181,22 +1545,24 @@ void btSequentialImpulseConstraintSolver::convertContacts(btPersistentManifold**
}
}
-void btSequentialImpulseConstraintSolver::convertJoint(btSolverConstraint* currentConstraintRow,
- btTypedConstraint* constraint,
- const btTypedConstraint::btConstraintInfo1& info1,
- int solverBodyIdA,
- int solverBodyIdB,
- const btContactSolverInfo& infoGlobal)
+void btSequentialImpulseConstraintSolver::convertJointInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool,
+ int& maxOverrideNumSolverIterations,
+ btSolverConstraint* currentConstraintRow,
+ btTypedConstraint* constraint,
+ const btTypedConstraint::btConstraintInfo1& info1,
+ int solverBodyIdA,
+ int solverBodyIdB,
+ const btContactSolverInfo& infoGlobal)
{
const btRigidBody& rbA = constraint->getRigidBodyA();
const btRigidBody& rbB = constraint->getRigidBodyB();
- const btSolverBody* bodyAPtr = &m_tmpSolverBodyPool[solverBodyIdA];
- const btSolverBody* bodyBPtr = &m_tmpSolverBodyPool[solverBodyIdB];
+ const btSolverBody* bodyAPtr = &tmpSolverBodyPool[solverBodyIdA];
+ const btSolverBody* bodyBPtr = &tmpSolverBodyPool[solverBodyIdB];
int overrideNumSolverIterations = constraint->getOverrideNumSolverIterations() > 0 ? constraint->getOverrideNumSolverIterations() : infoGlobal.m_numIterations;
- if (overrideNumSolverIterations > m_maxOverrideNumSolverIterations)
- m_maxOverrideNumSolverIterations = overrideNumSolverIterations;
+ if (overrideNumSolverIterations > maxOverrideNumSolverIterations)
+ maxOverrideNumSolverIterations = overrideNumSolverIterations;
for (int j = 0; j < info1.m_numConstraintRows; j++)
{
@@ -1236,7 +1602,7 @@ void btSequentialImpulseConstraintSolver::convertJoint(btSolverConstraint* curre
info2.m_J2linearAxis = currentConstraintRow->m_contactNormal2;
info2.m_J2angularAxis = currentConstraintRow->m_relpos2CrossNormal;
info2.rowskip = sizeof(btSolverConstraint) / sizeof(btScalar); //check this
- ///the size of btSolverConstraint needs be a multiple of btScalar
+ ///the size of btSolverConstraint needs be a multiple of btScalar
btAssert(info2.rowskip * sizeof(btScalar) == sizeof(btSolverConstraint));
info2.m_constraintError = &currentConstraintRow->m_rhs;
currentConstraintRow->m_cfm = infoGlobal.m_globalCfm;
@@ -1313,7 +1679,16 @@ void btSequentialImpulseConstraintSolver::convertJoint(btSolverConstraint* curre
}
}
-void btSequentialImpulseConstraintSolver::convertJoints(btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal)
+void btSequentialImpulseConstraintSolver::convertJoint(btSolverConstraint* currentConstraintRow,
+ btTypedConstraint* constraint,
+ const btTypedConstraint::btConstraintInfo1& info1,
+ int solverBodyIdA,
+ int solverBodyIdB,
+ const btContactSolverInfo& infoGlobal)
+{
+}
+
+void btSequentialImpulseConstraintSolver::convertJointsInternal(btSISolverSingleIterationData& siData, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal)
{
BT_PROFILE("convertJoints");
for (int j = 0; j < numConstraints; j++)
@@ -1325,11 +1700,11 @@ void btSequentialImpulseConstraintSolver::convertJoints(btTypedConstraint** cons
int totalNumRows = 0;
- m_tmpConstraintSizesPool.resizeNoInitialize(numConstraints);
+ siData.m_tmpConstraintSizesPool.resizeNoInitialize(numConstraints);
//calculate the total number of contraint rows
for (int i = 0; i < numConstraints; i++)
{
- btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i];
+ btTypedConstraint::btConstraintInfo1& info1 = siData.m_tmpConstraintSizesPool[i];
btJointFeedback* fb = constraints[i]->getJointFeedback();
if (fb)
{
@@ -1350,34 +1725,58 @@ void btSequentialImpulseConstraintSolver::convertJoints(btTypedConstraint** cons
}
totalNumRows += info1.m_numConstraintRows;
}
- m_tmpSolverNonContactConstraintPool.resizeNoInitialize(totalNumRows);
+ siData.m_tmpSolverNonContactConstraintPool.resizeNoInitialize(totalNumRows);
///setup the btSolverConstraints
int currentRow = 0;
for (int i = 0; i < numConstraints; i++)
{
- const btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i];
+ const btTypedConstraint::btConstraintInfo1& info1 = siData.m_tmpConstraintSizesPool[i];
if (info1.m_numConstraintRows)
{
btAssert(currentRow < totalNumRows);
- btSolverConstraint* currentConstraintRow = &m_tmpSolverNonContactConstraintPool[currentRow];
+ btSolverConstraint* currentConstraintRow = &siData.m_tmpSolverNonContactConstraintPool[currentRow];
btTypedConstraint* constraint = constraints[i];
btRigidBody& rbA = constraint->getRigidBodyA();
btRigidBody& rbB = constraint->getRigidBodyB();
- int solverBodyIdA = getOrInitSolverBody(rbA, infoGlobal.m_timeStep);
- int solverBodyIdB = getOrInitSolverBody(rbB, infoGlobal.m_timeStep);
+ int solverBodyIdA = siData.getOrInitSolverBody(rbA, infoGlobal.m_timeStep);
+ int solverBodyIdB = siData.getOrInitSolverBody(rbB, infoGlobal.m_timeStep);
- convertJoint(currentConstraintRow, constraint, info1, solverBodyIdA, solverBodyIdB, infoGlobal);
+ convertJointInternal(siData.m_tmpSolverBodyPool, siData.m_maxOverrideNumSolverIterations,
+ currentConstraintRow, constraint, info1, solverBodyIdA, solverBodyIdB, infoGlobal);
}
currentRow += info1.m_numConstraintRows;
}
}
-void btSequentialImpulseConstraintSolver::convertBodies(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal)
+void btSequentialImpulseConstraintSolver::convertJoints(btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal)
+{
+ btSISolverSingleIterationData siData(m_tmpSolverBodyPool,
+ m_tmpSolverContactConstraintPool,
+ m_tmpSolverNonContactConstraintPool,
+ m_tmpSolverContactFrictionConstraintPool,
+ m_tmpSolverContactRollingFrictionConstraintPool,
+ m_orderTmpConstraintPool,
+ m_orderNonContactConstraintPool,
+ m_orderFrictionConstraintPool,
+ m_tmpConstraintSizesPool,
+ m_resolveSingleConstraintRowGeneric,
+ m_resolveSingleConstraintRowLowerLimit,
+ m_resolveSplitPenetrationImpulse,
+ m_kinematicBodyUniqueIdToSolverBodyTable,
+ m_btSeed2,
+ m_fixedBodyId,
+ m_maxOverrideNumSolverIterations);
+
+ convertJointsInternal(siData, constraints, numConstraints, infoGlobal);
+}
+
+
+void btSequentialImpulseConstraintSolver::convertBodiesInternal(btSISolverSingleIterationData& siData, btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal)
{
BT_PROFILE("convertBodies");
for (int i = 0; i < numBodies; i++)
@@ -1385,23 +1784,23 @@ void btSequentialImpulseConstraintSolver::convertBodies(btCollisionObject** bodi
bodies[i]->setCompanionId(-1);
}
#if BT_THREADSAFE
- m_kinematicBodyUniqueIdToSolverBodyTable.resize(0);
+ siData.m_kinematicBodyUniqueIdToSolverBodyTable.resize(0);
#endif // BT_THREADSAFE
- m_tmpSolverBodyPool.reserve(numBodies + 1);
- m_tmpSolverBodyPool.resize(0);
+ siData.m_tmpSolverBodyPool.reserve(numBodies + 1);
+ siData.m_tmpSolverBodyPool.resize(0);
//btSolverBody& fixedBody = m_tmpSolverBodyPool.expand();
//initSolverBody(&fixedBody,0);
for (int i = 0; i < numBodies; i++)
{
- int bodyId = getOrInitSolverBody(*bodies[i], infoGlobal.m_timeStep);
+ int bodyId = siData.getOrInitSolverBody(*bodies[i], infoGlobal.m_timeStep);
btRigidBody* body = btRigidBody::upcast(bodies[i]);
if (body && body->getInvMass())
{
- btSolverBody& solverBody = m_tmpSolverBodyPool[bodyId];
+ btSolverBody& solverBody = siData.m_tmpSolverBodyPool[bodyId];
btVector3 gyroForce(0, 0, 0);
if (body->getFlags() & BT_ENABLE_GYROSCOPIC_FORCE_EXPLICIT)
{
@@ -1422,6 +1821,29 @@ void btSequentialImpulseConstraintSolver::convertBodies(btCollisionObject** bodi
}
}
+
+void btSequentialImpulseConstraintSolver::convertBodies(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal)
+{
+ btSISolverSingleIterationData siData(m_tmpSolverBodyPool,
+ m_tmpSolverContactConstraintPool,
+ m_tmpSolverNonContactConstraintPool,
+ m_tmpSolverContactFrictionConstraintPool,
+ m_tmpSolverContactRollingFrictionConstraintPool,
+ m_orderTmpConstraintPool,
+ m_orderNonContactConstraintPool,
+ m_orderFrictionConstraintPool,
+ m_tmpConstraintSizesPool,
+ m_resolveSingleConstraintRowGeneric,
+ m_resolveSingleConstraintRowLowerLimit,
+ m_resolveSplitPenetrationImpulse,
+ m_kinematicBodyUniqueIdToSolverBodyTable,
+ m_btSeed2,
+ m_fixedBodyId,
+ m_maxOverrideNumSolverIterations);
+
+ convertBodiesInternal(siData, bodies, numBodies, infoGlobal);
+}
+
btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer)
{
m_fixedBodyId = -1;
@@ -1545,14 +1967,14 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol
return 0.f;
}
-btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration, btCollisionObject** /*bodies */, int /*numBodies*/, btPersistentManifold** /*manifoldPtr*/, int /*numManifolds*/, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* /*debugDrawer*/)
+btScalar btSequentialImpulseConstraintSolver::solveSingleIterationInternal(btSISolverSingleIterationData& siData, int iteration, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal)
{
BT_PROFILE("solveSingleIteration");
btScalar leastSquaresResidual = 0.f;
- int numNonContactPool = m_tmpSolverNonContactConstraintPool.size();
- int numConstraintPool = m_tmpSolverContactConstraintPool.size();
- int numFrictionPool = m_tmpSolverContactFrictionConstraintPool.size();
+ int numNonContactPool = siData.m_tmpSolverNonContactConstraintPool.size();
+ int numConstraintPool = siData.m_tmpSolverContactConstraintPool.size();
+ int numFrictionPool = siData.m_tmpSolverContactFrictionConstraintPool.size();
if (infoGlobal.m_solverMode & SOLVER_RANDMIZE_ORDER)
{
@@ -1560,10 +1982,10 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
{
for (int j = 0; j < numNonContactPool; ++j)
{
- int tmp = m_orderNonContactConstraintPool[j];
- int swapi = btRandInt2(j + 1);
- m_orderNonContactConstraintPool[j] = m_orderNonContactConstraintPool[swapi];
- m_orderNonContactConstraintPool[swapi] = tmp;
+ int tmp = siData.m_orderNonContactConstraintPool[j];
+ int swapi = btRandInt2a(j + 1, siData.m_seed);
+ siData.m_orderNonContactConstraintPool[j] = siData.m_orderNonContactConstraintPool[swapi];
+ siData.m_orderNonContactConstraintPool[swapi] = tmp;
}
//contact/friction constraints are not solved more than
@@ -1571,30 +1993,30 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
{
for (int j = 0; j < numConstraintPool; ++j)
{
- int tmp = m_orderTmpConstraintPool[j];
- int swapi = btRandInt2(j + 1);
- m_orderTmpConstraintPool[j] = m_orderTmpConstraintPool[swapi];
- m_orderTmpConstraintPool[swapi] = tmp;
+ int tmp = siData.m_orderTmpConstraintPool[j];
+ int swapi = btRandInt2a(j + 1, siData.m_seed);
+ siData.m_orderTmpConstraintPool[j] = siData.m_orderTmpConstraintPool[swapi];
+ siData.m_orderTmpConstraintPool[swapi] = tmp;
}
for (int j = 0; j < numFrictionPool; ++j)
{
- int tmp = m_orderFrictionConstraintPool[j];
- int swapi = btRandInt2(j + 1);
- m_orderFrictionConstraintPool[j] = m_orderFrictionConstraintPool[swapi];
- m_orderFrictionConstraintPool[swapi] = tmp;
+ int tmp = siData.m_orderFrictionConstraintPool[j];
+ int swapi = btRandInt2a(j + 1, siData.m_seed);
+ siData.m_orderFrictionConstraintPool[j] = siData.m_orderFrictionConstraintPool[swapi];
+ siData.m_orderFrictionConstraintPool[swapi] = tmp;
}
}
}
}
///solve all joint constraints
- for (int j = 0; j < m_tmpSolverNonContactConstraintPool.size(); j++)
+ for (int j = 0; j < siData.m_tmpSolverNonContactConstraintPool.size(); j++)
{
- btSolverConstraint& constraint = m_tmpSolverNonContactConstraintPool[m_orderNonContactConstraintPool[j]];
+ btSolverConstraint& constraint = siData.m_tmpSolverNonContactConstraintPool[siData.m_orderNonContactConstraintPool[j]];
if (iteration < constraint.m_overrideNumSolverIterations)
{
- btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[constraint.m_solverBodyIdA], m_tmpSolverBodyPool[constraint.m_solverBodyIdB], constraint);
+ btScalar residual = siData.m_resolveSingleConstraintRowGeneric(siData.m_tmpSolverBodyPool[constraint.m_solverBodyIdA], siData.m_tmpSolverBodyPool[constraint.m_solverBodyIdB], constraint);
leastSquaresResidual = btMax(leastSquaresResidual, residual * residual);
}
}
@@ -1605,10 +2027,10 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
{
if (constraints[j]->isEnabled())
{
- int bodyAid = getOrInitSolverBody(constraints[j]->getRigidBodyA(), infoGlobal.m_timeStep);
- int bodyBid = getOrInitSolverBody(constraints[j]->getRigidBodyB(), infoGlobal.m_timeStep);
- btSolverBody& bodyA = m_tmpSolverBodyPool[bodyAid];
- btSolverBody& bodyB = m_tmpSolverBodyPool[bodyBid];
+ int bodyAid = siData.getSolverBody(constraints[j]->getRigidBodyA());
+ int bodyBid = siData.getSolverBody(constraints[j]->getRigidBodyB());
+ btSolverBody& bodyA = siData.m_tmpSolverBodyPool[bodyAid];
+ btSolverBody& bodyB = siData.m_tmpSolverBodyPool[bodyBid];
constraints[j]->solveConstraintObsolete(bodyA, bodyB, infoGlobal.m_timeStep);
}
}
@@ -1616,7 +2038,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
///solve all contact constraints
if (infoGlobal.m_solverMode & SOLVER_INTERLEAVE_CONTACT_AND_FRICTION_CONSTRAINTS)
{
- int numPoolConstraints = m_tmpSolverContactConstraintPool.size();
+ int numPoolConstraints = siData.m_tmpSolverContactConstraintPool.size();
int multiplier = (infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS) ? 2 : 1;
for (int c = 0; c < numPoolConstraints; c++)
@@ -1624,8 +2046,8 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
btScalar totalImpulse = 0;
{
- const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[m_orderTmpConstraintPool[c]];
- btScalar residual = resolveSingleConstraintRowLowerLimit(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
+ const btSolverConstraint& solveManifold = siData.m_tmpSolverContactConstraintPool[siData.m_orderTmpConstraintPool[c]];
+ btScalar residual = siData.m_resolveSingleConstraintRowLowerLimit(siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
leastSquaresResidual = btMax(leastSquaresResidual, residual * residual);
totalImpulse = solveManifold.m_appliedImpulse;
@@ -1634,28 +2056,28 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
if (applyFriction)
{
{
- btSolverConstraint& solveManifold = m_tmpSolverContactFrictionConstraintPool[m_orderFrictionConstraintPool[c * multiplier]];
+ btSolverConstraint& solveManifold = siData.m_tmpSolverContactFrictionConstraintPool[siData.m_orderFrictionConstraintPool[c * multiplier]];
if (totalImpulse > btScalar(0))
{
solveManifold.m_lowerLimit = -(solveManifold.m_friction * totalImpulse);
solveManifold.m_upperLimit = solveManifold.m_friction * totalImpulse;
- btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
+ btScalar residual = siData.m_resolveSingleConstraintRowGeneric(siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
leastSquaresResidual = btMax(leastSquaresResidual, residual * residual);
}
}
if (infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)
{
- btSolverConstraint& solveManifold = m_tmpSolverContactFrictionConstraintPool[m_orderFrictionConstraintPool[c * multiplier + 1]];
+ btSolverConstraint& solveManifold = siData.m_tmpSolverContactFrictionConstraintPool[siData.m_orderFrictionConstraintPool[c * multiplier + 1]];
if (totalImpulse > btScalar(0))
{
solveManifold.m_lowerLimit = -(solveManifold.m_friction * totalImpulse);
solveManifold.m_upperLimit = solveManifold.m_friction * totalImpulse;
- btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
+ btScalar residual = siData.m_resolveSingleConstraintRowGeneric(siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
leastSquaresResidual = btMax(leastSquaresResidual, residual * residual);
}
}
@@ -1665,40 +2087,40 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
else //SOLVER_INTERLEAVE_CONTACT_AND_FRICTION_CONSTRAINTS
{
//solve the friction constraints after all contact constraints, don't interleave them
- int numPoolConstraints = m_tmpSolverContactConstraintPool.size();
+ int numPoolConstraints = siData.m_tmpSolverContactConstraintPool.size();
int j;
for (j = 0; j < numPoolConstraints; j++)
{
- const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[m_orderTmpConstraintPool[j]];
- btScalar residual = resolveSingleConstraintRowLowerLimit(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
+ const btSolverConstraint& solveManifold = siData.m_tmpSolverContactConstraintPool[siData.m_orderTmpConstraintPool[j]];
+ btScalar residual = siData.m_resolveSingleConstraintRowLowerLimit(siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
leastSquaresResidual = btMax(leastSquaresResidual, residual * residual);
}
///solve all friction constraints
- int numFrictionPoolConstraints = m_tmpSolverContactFrictionConstraintPool.size();
+ int numFrictionPoolConstraints = siData.m_tmpSolverContactFrictionConstraintPool.size();
for (j = 0; j < numFrictionPoolConstraints; j++)
{
- btSolverConstraint& solveManifold = m_tmpSolverContactFrictionConstraintPool[m_orderFrictionConstraintPool[j]];
- btScalar totalImpulse = m_tmpSolverContactConstraintPool[solveManifold.m_frictionIndex].m_appliedImpulse;
+ btSolverConstraint& solveManifold = siData.m_tmpSolverContactFrictionConstraintPool[siData.m_orderFrictionConstraintPool[j]];
+ btScalar totalImpulse = siData.m_tmpSolverContactConstraintPool[solveManifold.m_frictionIndex].m_appliedImpulse;
if (totalImpulse > btScalar(0))
{
solveManifold.m_lowerLimit = -(solveManifold.m_friction * totalImpulse);
solveManifold.m_upperLimit = solveManifold.m_friction * totalImpulse;
- btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
+ btScalar residual = siData.m_resolveSingleConstraintRowGeneric(siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
leastSquaresResidual = btMax(leastSquaresResidual, residual * residual);
}
}
}
- int numRollingFrictionPoolConstraints = m_tmpSolverContactRollingFrictionConstraintPool.size();
+ int numRollingFrictionPoolConstraints = siData.m_tmpSolverContactRollingFrictionConstraintPool.size();
for (int j = 0; j < numRollingFrictionPoolConstraints; j++)
{
- btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[j];
- btScalar totalImpulse = m_tmpSolverContactConstraintPool[rollingFrictionConstraint.m_frictionIndex].m_appliedImpulse;
+ btSolverConstraint& rollingFrictionConstraint = siData.m_tmpSolverContactRollingFrictionConstraintPool[j];
+ btScalar totalImpulse = siData.m_tmpSolverContactConstraintPool[rollingFrictionConstraint.m_frictionIndex].m_appliedImpulse;
if (totalImpulse > btScalar(0))
{
btScalar rollingFrictionMagnitude = rollingFrictionConstraint.m_friction * totalImpulse;
@@ -1708,7 +2130,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
rollingFrictionConstraint.m_lowerLimit = -rollingFrictionMagnitude;
rollingFrictionConstraint.m_upperLimit = rollingFrictionMagnitude;
- btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdA], m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdB], rollingFrictionConstraint);
+ btScalar residual = siData.m_resolveSingleConstraintRowGeneric(siData.m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdA], siData.m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdB], rollingFrictionConstraint);
leastSquaresResidual = btMax(leastSquaresResidual, residual * residual);
}
}
@@ -1716,8 +2138,56 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration
return leastSquaresResidual;
}
+
+btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration, btCollisionObject** /*bodies */, int /*numBodies*/, btPersistentManifold** /*manifoldPtr*/, int /*numManifolds*/, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* /*debugDrawer*/)
+{
+ btSISolverSingleIterationData siData(m_tmpSolverBodyPool,
+ m_tmpSolverContactConstraintPool,
+ m_tmpSolverNonContactConstraintPool,
+ m_tmpSolverContactFrictionConstraintPool,
+ m_tmpSolverContactRollingFrictionConstraintPool,
+ m_orderTmpConstraintPool,
+ m_orderNonContactConstraintPool,
+ m_orderFrictionConstraintPool,
+ m_tmpConstraintSizesPool,
+ m_resolveSingleConstraintRowGeneric,
+ m_resolveSingleConstraintRowLowerLimit,
+ m_resolveSplitPenetrationImpulse,
+ m_kinematicBodyUniqueIdToSolverBodyTable,
+ m_btSeed2,
+ m_fixedBodyId,
+ m_maxOverrideNumSolverIterations);
+
+ btScalar leastSquaresResidual = btSequentialImpulseConstraintSolver::solveSingleIterationInternal(siData,
+ iteration, constraints, numConstraints, infoGlobal);
+ return leastSquaresResidual;
+}
+
void btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer)
{
+ btSISolverSingleIterationData siData(m_tmpSolverBodyPool,
+ m_tmpSolverContactConstraintPool,
+ m_tmpSolverNonContactConstraintPool,
+ m_tmpSolverContactFrictionConstraintPool,
+ m_tmpSolverContactRollingFrictionConstraintPool,
+ m_orderTmpConstraintPool,
+ m_orderNonContactConstraintPool,
+ m_orderFrictionConstraintPool,
+ m_tmpConstraintSizesPool,
+ m_resolveSingleConstraintRowGeneric,
+ m_resolveSingleConstraintRowLowerLimit,
+ m_resolveSplitPenetrationImpulse,
+ m_kinematicBodyUniqueIdToSolverBodyTable,
+ m_btSeed2,
+ m_fixedBodyId,
+ m_maxOverrideNumSolverIterations);
+
+ solveGroupCacheFriendlySplitImpulseIterationsInternal(siData,
+ bodies, numBodies, manifoldPtr, numManifolds, constraints, numConstraints, infoGlobal, debugDrawer);
+
+}
+void btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySplitImpulseIterationsInternal(btSISolverSingleIterationData& siData, btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer)
+{
BT_PROFILE("solveGroupCacheFriendlySplitImpulseIterations");
int iteration;
if (infoGlobal.m_splitImpulse)
@@ -1727,13 +2197,13 @@ void btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySplitImpulseIte
{
btScalar leastSquaresResidual = 0.f;
{
- int numPoolConstraints = m_tmpSolverContactConstraintPool.size();
+ int numPoolConstraints = siData.m_tmpSolverContactConstraintPool.size();
int j;
for (j = 0; j < numPoolConstraints; j++)
{
- const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[m_orderTmpConstraintPool[j]];
+ const btSolverConstraint& solveManifold = siData.m_tmpSolverContactConstraintPool[siData.m_orderTmpConstraintPool[j]];
- btScalar residual = resolveSplitPenetrationImpulse(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
+ btScalar residual = siData.m_resolveSplitPenetrationImpulse(siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], siData.m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB], solveManifold);
leastSquaresResidual = btMax(leastSquaresResidual, residual * residual);
}
}
@@ -1760,7 +2230,7 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations(
int maxIterations = m_maxOverrideNumSolverIterations > infoGlobal.m_numIterations ? m_maxOverrideNumSolverIterations : infoGlobal.m_numIterations;
for (int iteration = 0; iteration < maxIterations; iteration++)
- //for ( int iteration = maxIterations-1 ; iteration >= 0;iteration--)
+ //for ( int iteration = maxIterations-1 ; iteration >= 0;iteration--)
{
m_leastSquaresResidual = solveSingleIteration(iteration, bodies, numBodies, manifoldPtr, numManifolds, constraints, numConstraints, infoGlobal, debugDrawer);
@@ -1769,6 +2239,14 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations(
#ifdef VERBOSE_RESIDUAL_PRINTF
printf("residual = %f at iteration #%d\n", m_leastSquaresResidual, iteration);
#endif
+ m_analyticsData.m_numSolverCalls++;
+ m_analyticsData.m_numIterationsUsed = iteration+1;
+ m_analyticsData.m_islandId = -2;
+ if (numBodies>0)
+ m_analyticsData.m_islandId = bodies[0]->getCompanionId();
+ m_analyticsData.m_numBodies = numBodies;
+ m_analyticsData.m_numContactManifolds = numManifolds;
+ m_analyticsData.m_remainingLeastSquaresResidual = m_leastSquaresResidual;
break;
}
}
@@ -1776,31 +2254,42 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations(
return 0.f;
}
-void btSequentialImpulseConstraintSolver::writeBackContacts(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
+void btSequentialImpulseConstraintSolver::writeBackContactsInternal(btConstraintArray& tmpSolverContactConstraintPool, btConstraintArray& tmpSolverContactFrictionConstraintPool, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
{
for (int j = iBegin; j < iEnd; j++)
{
- const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[j];
+ const btSolverConstraint& solveManifold = tmpSolverContactConstraintPool[j];
btManifoldPoint* pt = (btManifoldPoint*)solveManifold.m_originalContactPoint;
btAssert(pt);
pt->m_appliedImpulse = solveManifold.m_appliedImpulse;
// float f = m_tmpSolverContactFrictionConstraintPool[solveManifold.m_frictionIndex].m_appliedImpulse;
// printf("pt->m_appliedImpulseLateral1 = %f\n", f);
- pt->m_appliedImpulseLateral1 = m_tmpSolverContactFrictionConstraintPool[solveManifold.m_frictionIndex].m_appliedImpulse;
+ pt->m_appliedImpulseLateral1 = tmpSolverContactFrictionConstraintPool[solveManifold.m_frictionIndex].m_appliedImpulse;
//printf("pt->m_appliedImpulseLateral1 = %f\n", pt->m_appliedImpulseLateral1);
if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS))
{
- pt->m_appliedImpulseLateral2 = m_tmpSolverContactFrictionConstraintPool[solveManifold.m_frictionIndex + 1].m_appliedImpulse;
+ pt->m_appliedImpulseLateral2 = tmpSolverContactFrictionConstraintPool[solveManifold.m_frictionIndex + 1].m_appliedImpulse;
}
//do a callback here?
}
}
+void btSequentialImpulseConstraintSolver::writeBackContacts(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
+{
+ writeBackContactsInternal(m_tmpSolverContactConstraintPool, m_tmpSolverContactFrictionConstraintPool, iBegin, iEnd, infoGlobal);
+
+}
+
void btSequentialImpulseConstraintSolver::writeBackJoints(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
{
+ writeBackJointsInternal(m_tmpSolverNonContactConstraintPool, iBegin, iEnd, infoGlobal);
+}
+
+void btSequentialImpulseConstraintSolver::writeBackJointsInternal(btConstraintArray& tmpSolverNonContactConstraintPool, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
+{
for (int j = iBegin; j < iEnd; j++)
{
- const btSolverConstraint& solverConstr = m_tmpSolverNonContactConstraintPool[j];
+ const btSolverConstraint& solverConstr = tmpSolverNonContactConstraintPool[j];
btTypedConstraint* constr = (btTypedConstraint*)solverConstr.m_originalContactPoint;
btJointFeedback* fb = constr->getJointFeedback();
if (fb)
@@ -1821,53 +2310,79 @@ void btSequentialImpulseConstraintSolver::writeBackJoints(int iBegin, int iEnd,
void btSequentialImpulseConstraintSolver::writeBackBodies(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
{
+ writeBackBodiesInternal(m_tmpSolverBodyPool, iBegin, iEnd, infoGlobal);
+}
+void btSequentialImpulseConstraintSolver::writeBackBodiesInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal)
+{
for (int i = iBegin; i < iEnd; i++)
{
- btRigidBody* body = m_tmpSolverBodyPool[i].m_originalBody;
+ btRigidBody* body = tmpSolverBodyPool[i].m_originalBody;
if (body)
{
if (infoGlobal.m_splitImpulse)
- m_tmpSolverBodyPool[i].writebackVelocityAndTransform(infoGlobal.m_timeStep, infoGlobal.m_splitImpulseTurnErp);
+ tmpSolverBodyPool[i].writebackVelocityAndTransform(infoGlobal.m_timeStep, infoGlobal.m_splitImpulseTurnErp);
else
- m_tmpSolverBodyPool[i].writebackVelocity();
+ tmpSolverBodyPool[i].writebackVelocity();
- m_tmpSolverBodyPool[i].m_originalBody->setLinearVelocity(
- m_tmpSolverBodyPool[i].m_linearVelocity +
- m_tmpSolverBodyPool[i].m_externalForceImpulse);
+ tmpSolverBodyPool[i].m_originalBody->setLinearVelocity(
+ tmpSolverBodyPool[i].m_linearVelocity +
+ tmpSolverBodyPool[i].m_externalForceImpulse);
- m_tmpSolverBodyPool[i].m_originalBody->setAngularVelocity(
- m_tmpSolverBodyPool[i].m_angularVelocity +
- m_tmpSolverBodyPool[i].m_externalTorqueImpulse);
+ tmpSolverBodyPool[i].m_originalBody->setAngularVelocity(
+ tmpSolverBodyPool[i].m_angularVelocity +
+ tmpSolverBodyPool[i].m_externalTorqueImpulse);
if (infoGlobal.m_splitImpulse)
- m_tmpSolverBodyPool[i].m_originalBody->setWorldTransform(m_tmpSolverBodyPool[i].m_worldTransform);
+ tmpSolverBodyPool[i].m_originalBody->setWorldTransform(tmpSolverBodyPool[i].m_worldTransform);
- m_tmpSolverBodyPool[i].m_originalBody->setCompanionId(-1);
+ tmpSolverBodyPool[i].m_originalBody->setCompanionId(-1);
}
}
}
-btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal)
+btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinishInternal(btSISolverSingleIterationData& siData, btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal)
{
BT_PROFILE("solveGroupCacheFriendlyFinish");
if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING)
{
- writeBackContacts(0, m_tmpSolverContactConstraintPool.size(), infoGlobal);
+ writeBackContactsInternal(siData.m_tmpSolverContactConstraintPool, siData.m_tmpSolverContactFrictionConstraintPool, 0, siData.m_tmpSolverContactConstraintPool.size(), infoGlobal);
}
- writeBackJoints(0, m_tmpSolverNonContactConstraintPool.size(), infoGlobal);
- writeBackBodies(0, m_tmpSolverBodyPool.size(), infoGlobal);
+ writeBackJointsInternal(siData.m_tmpSolverNonContactConstraintPool, 0, siData.m_tmpSolverNonContactConstraintPool.size(), infoGlobal);
+ writeBackBodiesInternal(siData.m_tmpSolverBodyPool, 0, siData.m_tmpSolverBodyPool.size(), infoGlobal);
- m_tmpSolverContactConstraintPool.resizeNoInitialize(0);
- m_tmpSolverNonContactConstraintPool.resizeNoInitialize(0);
- m_tmpSolverContactFrictionConstraintPool.resizeNoInitialize(0);
- m_tmpSolverContactRollingFrictionConstraintPool.resizeNoInitialize(0);
+ siData.m_tmpSolverContactConstraintPool.resizeNoInitialize(0);
+ siData.m_tmpSolverNonContactConstraintPool.resizeNoInitialize(0);
+ siData.m_tmpSolverContactFrictionConstraintPool.resizeNoInitialize(0);
+ siData.m_tmpSolverContactRollingFrictionConstraintPool.resizeNoInitialize(0);
- m_tmpSolverBodyPool.resizeNoInitialize(0);
+ siData.m_tmpSolverBodyPool.resizeNoInitialize(0);
return 0.f;
}
+btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal)
+{
+ btSISolverSingleIterationData siData(m_tmpSolverBodyPool,
+ m_tmpSolverContactConstraintPool,
+ m_tmpSolverNonContactConstraintPool,
+ m_tmpSolverContactFrictionConstraintPool,
+ m_tmpSolverContactRollingFrictionConstraintPool,
+ m_orderTmpConstraintPool,
+ m_orderNonContactConstraintPool,
+ m_orderFrictionConstraintPool,
+ m_tmpConstraintSizesPool,
+ m_resolveSingleConstraintRowGeneric,
+ m_resolveSingleConstraintRowLowerLimit,
+ m_resolveSplitPenetrationImpulse,
+ m_kinematicBodyUniqueIdToSolverBodyTable,
+ m_btSeed2,
+ m_fixedBodyId,
+ m_maxOverrideNumSolverIterations);
+
+ return btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinishInternal(siData, bodies, numBodies, infoGlobal);
+}
+
/// btSequentialImpulseConstraintSolver Sequentially applies impulses
btScalar btSequentialImpulseConstraintSolver::solveGroup(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer, btDispatcher* /*dispatcher*/)
{
@@ -1886,4 +2401,4 @@ btScalar btSequentialImpulseConstraintSolver::solveGroup(btCollisionObject** bod
void btSequentialImpulseConstraintSolver::reset()
{
m_btSeed2 = 0;
-}
+} \ No newline at end of file
diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h
index 70db83b063..2b88e25be7 100644
--- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h
+++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h
@@ -29,10 +29,91 @@ class btCollisionObject;
typedef btScalar (*btSingleConstraintRowSolver)(btSolverBody&, btSolverBody&, const btSolverConstraint&);
+struct btSISolverSingleIterationData
+{
+ btAlignedObjectArray<btSolverBody>& m_tmpSolverBodyPool;
+ btConstraintArray& m_tmpSolverContactConstraintPool;
+ btConstraintArray& m_tmpSolverNonContactConstraintPool;
+ btConstraintArray& m_tmpSolverContactFrictionConstraintPool;
+ btConstraintArray& m_tmpSolverContactRollingFrictionConstraintPool;
+
+ btAlignedObjectArray<int>& m_orderTmpConstraintPool;
+ btAlignedObjectArray<int>& m_orderNonContactConstraintPool;
+ btAlignedObjectArray<int>& m_orderFrictionConstraintPool;
+ btAlignedObjectArray<btTypedConstraint::btConstraintInfo1>& m_tmpConstraintSizesPool;
+ unsigned long& m_seed;
+
+ btSingleConstraintRowSolver& m_resolveSingleConstraintRowGeneric;
+ btSingleConstraintRowSolver& m_resolveSingleConstraintRowLowerLimit;
+ btSingleConstraintRowSolver& m_resolveSplitPenetrationImpulse;
+ btAlignedObjectArray<int>& m_kinematicBodyUniqueIdToSolverBodyTable;
+ int& m_fixedBodyId;
+ int& m_maxOverrideNumSolverIterations;
+ int getOrInitSolverBody(btCollisionObject & body, btScalar timeStep);
+ static void initSolverBody(btSolverBody * solverBody, btCollisionObject * collisionObject, btScalar timeStep);
+ int getSolverBody(btCollisionObject& body) const;
+
+
+ btSISolverSingleIterationData(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool,
+ btConstraintArray& tmpSolverContactConstraintPool,
+ btConstraintArray& tmpSolverNonContactConstraintPool,
+ btConstraintArray& tmpSolverContactFrictionConstraintPool,
+ btConstraintArray& tmpSolverContactRollingFrictionConstraintPool,
+ btAlignedObjectArray<int>& orderTmpConstraintPool,
+ btAlignedObjectArray<int>& orderNonContactConstraintPool,
+ btAlignedObjectArray<int>& orderFrictionConstraintPool,
+ btAlignedObjectArray<btTypedConstraint::btConstraintInfo1>& tmpConstraintSizesPool,
+ btSingleConstraintRowSolver& resolveSingleConstraintRowGeneric,
+ btSingleConstraintRowSolver& resolveSingleConstraintRowLowerLimit,
+ btSingleConstraintRowSolver& resolveSplitPenetrationImpulse,
+ btAlignedObjectArray<int>& kinematicBodyUniqueIdToSolverBodyTable,
+ unsigned long& seed,
+ int& fixedBodyId,
+ int& maxOverrideNumSolverIterations
+ )
+ :m_tmpSolverBodyPool(tmpSolverBodyPool),
+ m_tmpSolverContactConstraintPool(tmpSolverContactConstraintPool),
+ m_tmpSolverNonContactConstraintPool(tmpSolverNonContactConstraintPool),
+ m_tmpSolverContactFrictionConstraintPool(tmpSolverContactFrictionConstraintPool),
+ m_tmpSolverContactRollingFrictionConstraintPool(tmpSolverContactRollingFrictionConstraintPool),
+ m_orderTmpConstraintPool(orderTmpConstraintPool),
+ m_orderNonContactConstraintPool(orderNonContactConstraintPool),
+ m_orderFrictionConstraintPool(orderFrictionConstraintPool),
+ m_tmpConstraintSizesPool(tmpConstraintSizesPool),
+ m_seed(seed),
+ m_resolveSingleConstraintRowGeneric(resolveSingleConstraintRowGeneric),
+ m_resolveSingleConstraintRowLowerLimit(resolveSingleConstraintRowLowerLimit),
+ m_resolveSplitPenetrationImpulse(resolveSplitPenetrationImpulse),
+ m_kinematicBodyUniqueIdToSolverBodyTable(kinematicBodyUniqueIdToSolverBodyTable),
+ m_fixedBodyId(fixedBodyId),
+ m_maxOverrideNumSolverIterations(maxOverrideNumSolverIterations)
+ {
+ }
+};
+
+struct btSolverAnalyticsData
+{
+ btSolverAnalyticsData()
+ {
+ m_numSolverCalls = 0;
+ m_numIterationsUsed = -1;
+ m_remainingLeastSquaresResidual = -1;
+ m_islandId = -2;
+ }
+ int m_islandId;
+ int m_numBodies;
+ int m_numContactManifolds;
+ int m_numSolverCalls;
+ int m_numIterationsUsed;
+ double m_remainingLeastSquaresResidual;
+};
+
///The btSequentialImpulseConstraintSolver is a fast SIMD implementation of the Projected Gauss Seidel (iterative LCP) method.
ATTRIBUTE_ALIGNED16(class)
btSequentialImpulseConstraintSolver : public btConstraintSolver
{
+
+
protected:
btAlignedObjectArray<btSolverBody> m_tmpSolverBodyPool;
btConstraintArray m_tmpSolverContactConstraintPool;
@@ -64,26 +145,26 @@ protected:
btScalar m_leastSquaresResidual;
void setupFrictionConstraint(btSolverConstraint & solverConstraint, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB,
- btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2,
- btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation,
- const btContactSolverInfo& infoGlobal,
- btScalar desiredVelocity = 0., btScalar cfmSlip = 0.);
+ btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2,
+ btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation,
+ const btContactSolverInfo& infoGlobal,
+ btScalar desiredVelocity = 0., btScalar cfmSlip = 0.);
void setupTorsionalFrictionConstraint(btSolverConstraint & solverConstraint, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB,
- btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2,
- btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation,
- btScalar desiredVelocity = 0., btScalar cfmSlip = 0.);
+ btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2,
+ btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation,
+ btScalar desiredVelocity = 0., btScalar cfmSlip = 0.);
btSolverConstraint& addFrictionConstraint(const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity = 0., btScalar cfmSlip = 0.);
btSolverConstraint& addTorsionalFrictionConstraint(const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, btManifoldPoint& cp, btScalar torsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, btScalar desiredVelocity = 0, btScalar cfmSlip = 0.f);
void setupContactConstraint(btSolverConstraint & solverConstraint, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp,
- const btContactSolverInfo& infoGlobal, btScalar& relaxation, const btVector3& rel_pos1, const btVector3& rel_pos2);
+ const btContactSolverInfo& infoGlobal, btScalar& relaxation, const btVector3& rel_pos1, const btVector3& rel_pos2);
static void applyAnisotropicFriction(btCollisionObject * colObj, btVector3 & frictionDirection, int frictionMode);
void setFrictionConstraintImpulse(btSolverConstraint & solverConstraint, int solverBodyIdA, int solverBodyIdB,
- btManifoldPoint& cp, const btContactSolverInfo& infoGlobal);
+ btManifoldPoint& cp, const btContactSolverInfo& infoGlobal);
///m_btSeed2 is used for re-arranging the constraint rows. improves convergence/quality of friction
unsigned long m_btSeed2;
@@ -97,6 +178,7 @@ protected:
virtual void convertJoints(btTypedConstraint * *constraints, int numConstraints, const btContactSolverInfo& infoGlobal);
void convertJoint(btSolverConstraint * currentConstraintRow, btTypedConstraint * constraint, const btTypedConstraint::btConstraintInfo1& info1, int solverBodyIdA, int solverBodyIdB, const btContactSolverInfo& infoGlobal);
+
virtual void convertBodies(btCollisionObject * *bodies, int numBodies, const btContactSolverInfo& infoGlobal);
btScalar resolveSplitPenetrationSIMD(btSolverBody & bodyA, btSolverBody & bodyB, const btSolverConstraint& contactConstraint)
@@ -122,7 +204,8 @@ protected:
return m_resolveSplitPenetrationImpulse(bodyA, bodyB, contactConstraint);
}
-protected:
+public:
+
void writeBackContacts(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal);
void writeBackJoints(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal);
void writeBackBodies(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal);
@@ -130,6 +213,7 @@ protected:
virtual btScalar solveGroupCacheFriendlyFinish(btCollisionObject * *bodies, int numBodies, const btContactSolverInfo& infoGlobal);
virtual btScalar solveSingleIteration(int iteration, btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer);
+
virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject * *bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer);
virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject * *bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer);
@@ -141,13 +225,52 @@ public:
virtual btScalar solveGroup(btCollisionObject * *bodies, int numBodies, btPersistentManifold** manifold, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& info, btIDebugDraw* debugDrawer, btDispatcher* dispatcher);
+ static btScalar solveSingleIterationInternal(btSISolverSingleIterationData& siData, int iteration, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal);
+ static void convertBodiesInternal(btSISolverSingleIterationData& siData, btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal);
+ static void convertJointsInternal(btSISolverSingleIterationData& siData, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal);
+ static void convertContactInternal(btSISolverSingleIterationData& siData, btPersistentManifold * manifold, const btContactSolverInfo& infoGlobal);
+ static void setupContactConstraintInternal(btSISolverSingleIterationData& siData, btSolverConstraint& solverConstraint, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, const btContactSolverInfo& infoGlobal, btScalar& relaxation,
+ const btVector3& rel_pos1, const btVector3& rel_pos2);
+ static btScalar restitutionCurveInternal(btScalar rel_vel, btScalar restitution, btScalar velocityThreshold);
+ static btSolverConstraint& addTorsionalFrictionConstraintInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btConstraintArray& tmpSolverContactRollingFrictionConstraintPool, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, btScalar desiredVelocity = 0, btScalar cfmSlip = 0.);
+ static void setupTorsionalFrictionConstraintInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btSolverConstraint& solverConstraint, const btVector3& normalAxis1, int solverBodyIdA, int solverBodyIdB,
+ btManifoldPoint& cp, btScalar combinedTorsionalFriction, const btVector3& rel_pos1, const btVector3& rel_pos2,
+ btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation,
+ btScalar desiredVelocity, btScalar cfmSlip);
+ static void setupFrictionConstraintInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btSolverConstraint& solverConstraint, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip);
+ static btSolverConstraint& addFrictionConstraintInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btConstraintArray& tmpSolverContactFrictionConstraintPool, const btVector3& normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, btManifoldPoint& cp, const btVector3& rel_pos1, const btVector3& rel_pos2, btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity = 0., btScalar cfmSlip = 0.);
+ static void setFrictionConstraintImpulseInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, btConstraintArray& tmpSolverContactFrictionConstraintPool,
+
+ btSolverConstraint& solverConstraint,
+ int solverBodyIdA, int solverBodyIdB,
+ btManifoldPoint& cp, const btContactSolverInfo& infoGlobal);
+ static void convertJointInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool,
+ int& maxOverrideNumSolverIterations,
+ btSolverConstraint* currentConstraintRow,
+ btTypedConstraint* constraint,
+ const btTypedConstraint::btConstraintInfo1& info1,
+ int solverBodyIdA,
+ int solverBodyIdB,
+ const btContactSolverInfo& infoGlobal);
+
+ static btScalar solveGroupCacheFriendlyFinishInternal(btSISolverSingleIterationData& siData, btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal);
+
+ static void writeBackContactsInternal(btConstraintArray& tmpSolverContactConstraintPool, btConstraintArray& tmpSolverContactFrictionConstraintPool, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal);
+
+ static void writeBackJointsInternal(btConstraintArray& tmpSolverNonContactConstraintPool, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal);
+ static void writeBackBodiesInternal(btAlignedObjectArray<btSolverBody>& tmpSolverBodyPool, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal);
+ static void solveGroupCacheFriendlySplitImpulseIterationsInternal(btSISolverSingleIterationData& siData, btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer);
+
+
///clear internal cached data and reset random seed
virtual void reset();
unsigned long btRand2();
-
int btRandInt2(int n);
+ static unsigned long btRand2a(unsigned long& seed);
+ static int btRandInt2a(int n, unsigned long& seed);
+
void setRandSeed(unsigned long seed)
{
m_btSeed2 = seed;
@@ -179,15 +302,22 @@ public:
m_resolveSingleConstraintRowLowerLimit = rowSolver;
}
+
+
///Various implementations of solving a single constraint row using a generic equality constraint, using scalar reference, SSE2 or SSE4
- btSingleConstraintRowSolver getScalarConstraintRowSolverGeneric();
- btSingleConstraintRowSolver getSSE2ConstraintRowSolverGeneric();
- btSingleConstraintRowSolver getSSE4_1ConstraintRowSolverGeneric();
+ static btSingleConstraintRowSolver getScalarConstraintRowSolverGeneric();
+ static btSingleConstraintRowSolver getSSE2ConstraintRowSolverGeneric();
+ static btSingleConstraintRowSolver getSSE4_1ConstraintRowSolverGeneric();
///Various implementations of solving a single constraint row using an inequality (lower limit) constraint, using scalar reference, SSE2 or SSE4
- btSingleConstraintRowSolver getScalarConstraintRowSolverLowerLimit();
- btSingleConstraintRowSolver getSSE2ConstraintRowSolverLowerLimit();
- btSingleConstraintRowSolver getSSE4_1ConstraintRowSolverLowerLimit();
+ static btSingleConstraintRowSolver getScalarConstraintRowSolverLowerLimit();
+ static btSingleConstraintRowSolver getSSE2ConstraintRowSolverLowerLimit();
+ static btSingleConstraintRowSolver getSSE4_1ConstraintRowSolverLowerLimit();
+
+ static btSingleConstraintRowSolver getScalarSplitPenetrationImpulseGeneric();
+ static btSingleConstraintRowSolver getSSE2SplitPenetrationImpulseGeneric();
+
+ btSolverAnalyticsData m_analyticsData;
};
#endif //BT_SEQUENTIAL_IMPULSE_CONSTRAINT_SOLVER_H
diff --git a/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp b/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp
index 17287aa82a..5353fe009e 100644
--- a/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp
+++ b/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp
@@ -65,7 +65,7 @@ inline int getIslandId(const btPersistentManifold* lhs)
return islandId;
}
-SIMD_FORCE_INLINE int btGetConstraintIslandId(const btTypedConstraint* lhs)
+SIMD_FORCE_INLINE int btGetConstraintIslandId1(const btTypedConstraint* lhs)
{
const btCollisionObject& rcolObj0 = lhs->getRigidBodyA();
const btCollisionObject& rcolObj1 = lhs->getRigidBodyB();
@@ -452,7 +452,7 @@ void btSimulationIslandManagerMt::addConstraintsToIslands(btAlignedObjectArray<b
btTypedConstraint* constraint = constraints[i];
if (constraint->isEnabled())
{
- int islandId = btGetConstraintIslandId(constraint);
+ int islandId = btGetConstraintIslandId1(constraint);
// if island is not sleeping,
if (Island* island = getIsland(islandId))
{
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp
index 53fc48d4b9..3e210d7520 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp
@@ -106,6 +106,7 @@ btMultiBody::btMultiBody(int n_links,
m_fixedBase(fixedBase),
m_awake(true),
m_canSleep(canSleep),
+ m_canWakeup(true),
m_sleepTimer(0),
m_userObjectPointer(0),
m_userIndex2(-1),
@@ -343,6 +344,7 @@ void btMultiBody::finalizeMultiDof()
m_deltaV.resize(6 + m_dofCount);
m_realBuf.resize(6 + m_dofCount + m_dofCount * m_dofCount + 6 + m_dofCount); //m_dofCount for joint-space vels + m_dofCount^2 for "D" matrices + delta-pos vector (6 base "vels" + joint "vels")
m_vectorBuf.resize(2 * m_dofCount); //two 3-vectors (i.e. one six-vector) for each system dof ("h" matrices)
+ m_matrixBuf.resize(m_links.size() + 1);
for (int i = 0; i < m_vectorBuf.size(); i++)
{
m_vectorBuf[i].setValue(0, 0, 0);
@@ -350,9 +352,9 @@ void btMultiBody::finalizeMultiDof()
updateLinksDofOffsets();
}
-int btMultiBody::getParent(int i) const
+int btMultiBody::getParent(int link_num) const
{
- return m_links[i].m_parent;
+ return m_links[link_num].m_parent;
}
btScalar btMultiBody::getLinkMass(int i) const
@@ -1882,6 +1884,8 @@ void btMultiBody::checkMotionAndSleepIfRequired(btScalar timestep)
return;
}
+
+
// motion is computed as omega^2 + v^2 + (sum of squares of joint velocities)
btScalar motion = 0;
{
@@ -1900,8 +1904,11 @@ void btMultiBody::checkMotionAndSleepIfRequired(btScalar timestep)
else
{
m_sleepTimer = 0;
- if (!m_awake)
- wakeUp();
+ if (m_canWakeup)
+ {
+ if (!m_awake)
+ wakeUp();
+ }
}
}
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h
index e5c0f1806b..c0b0d003be 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h
@@ -65,7 +65,7 @@ public:
virtual ~btMultiBody();
//note: fixed link collision with parent is always disabled
- void setupFixed(int linkIndex,
+ void setupFixed(int i, //linkIndex
btScalar mass,
const btVector3 &inertia,
int parent,
@@ -83,7 +83,7 @@ public:
const btVector3 &thisPivotToThisComOffset,
bool disableParentCollision);
- void setupRevolute(int linkIndex, // 0 to num_links-1
+ void setupRevolute(int i, // 0 to num_links-1
btScalar mass,
const btVector3 &inertia,
int parentIndex,
@@ -93,7 +93,7 @@ public:
const btVector3 &thisPivotToThisComOffset, // vector from joint axis to my COM, in MY frame
bool disableParentCollision = false);
- void setupSpherical(int linkIndex, // 0 to num_links-1
+ void setupSpherical(int i, // linkIndex, 0 to num_links-1
btScalar mass,
const btVector3 &inertia,
int parent,
@@ -182,7 +182,10 @@ public:
// get/set pos/vel/rot/omega for the base link
//
- const btVector3 &getBasePos() const { return m_basePos; } // in world frame
+ const btVector3 &getBasePos() const
+ {
+ return m_basePos;
+ } // in world frame
const btVector3 getBaseVel() const
{
return btVector3(m_realBuf[3], m_realBuf[4], m_realBuf[5]);
@@ -274,15 +277,15 @@ public:
//
// transform vectors in local frame of link i to world frame (or vice versa)
//
- btVector3 localPosToWorld(int i, const btVector3 &vec) const;
- btVector3 localDirToWorld(int i, const btVector3 &vec) const;
- btVector3 worldPosToLocal(int i, const btVector3 &vec) const;
- btVector3 worldDirToLocal(int i, const btVector3 &vec) const;
+ btVector3 localPosToWorld(int i, const btVector3 &local_pos) const;
+ btVector3 localDirToWorld(int i, const btVector3 &local_dir) const;
+ btVector3 worldPosToLocal(int i, const btVector3 &world_pos) const;
+ btVector3 worldDirToLocal(int i, const btVector3 &world_dir) const;
//
// transform a frame in local coordinate to a frame in world coordinate
//
- btMatrix3x3 localFrameToWorld(int i, const btMatrix3x3 &mat) const;
+ btMatrix3x3 localFrameToWorld(int i, const btMatrix3x3 &local_frame) const;
//
// calculate kinetic energy and angular momentum
@@ -451,7 +454,10 @@ public:
//
void setCanSleep(bool canSleep)
{
- m_canSleep = canSleep;
+ if (m_canWakeup)
+ {
+ m_canSleep = canSleep;
+ }
}
bool getCanSleep() const
@@ -459,6 +465,15 @@ public:
return m_canSleep;
}
+ bool getCanWakeup() const
+ {
+ return m_canWakeup;
+ }
+
+ void setCanWakeup(bool canWakeup)
+ {
+ m_canWakeup = canWakeup;
+ }
bool isAwake() const { return m_awake; }
void wakeUp();
void goToSleep();
@@ -469,6 +484,11 @@ public:
return m_fixedBase;
}
+ void setFixedBase(bool fixedBase)
+ {
+ m_fixedBase = fixedBase;
+ }
+
int getCompanionId() const
{
return m_companionId;
@@ -556,11 +576,11 @@ public:
{
return m_internalNeedsJointFeedback;
}
- void forwardKinematics(btAlignedObjectArray<btQuaternion> & scratch_q, btAlignedObjectArray<btVector3> & scratch_m);
+ void forwardKinematics(btAlignedObjectArray<btQuaternion>& world_to_local, btAlignedObjectArray<btVector3> & local_origin);
void compTreeLinkVelocities(btVector3 * omega, btVector3 * vel) const;
- void updateCollisionObjectWorldTransforms(btAlignedObjectArray<btQuaternion> & scratch_q, btAlignedObjectArray<btVector3> & scratch_m);
+ void updateCollisionObjectWorldTransforms(btAlignedObjectArray<btQuaternion> & world_to_local, btAlignedObjectArray<btVector3> & local_origin);
virtual int calculateSerializeBufferSize() const;
@@ -688,6 +708,7 @@ private:
// Sleep parameters.
bool m_awake;
bool m_canSleep;
+ bool m_canWakeup;
btScalar m_sleepTimer;
void *m_userObjectPointer;
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp
index e97bd71cc4..23e163f0e8 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp
@@ -70,6 +70,30 @@ btScalar btMultiBodyConstraintSolver::solveSingleIteration(int iteration, btColl
//solve featherstone frictional contact
if (infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS && ((infoGlobal.m_solverMode & SOLVER_DISABLE_IMPLICIT_CONE_FRICTION) == 0))
{
+ for (int j1 = 0; j1 < this->m_multiBodySpinningFrictionContactConstraints.size(); j1++)
+ {
+ if (iteration < infoGlobal.m_numIterations)
+ {
+ int index = j1;
+
+ btMultiBodySolverConstraint& frictionConstraint = m_multiBodySpinningFrictionContactConstraints[index];
+ btScalar totalImpulse = m_multiBodyNormalContactConstraints[frictionConstraint.m_frictionIndex].m_appliedImpulse;
+ //adjust friction limits here
+ if (totalImpulse > btScalar(0))
+ {
+ frictionConstraint.m_lowerLimit = -(frictionConstraint.m_friction * totalImpulse);
+ frictionConstraint.m_upperLimit = frictionConstraint.m_friction * totalImpulse;
+ btScalar residual = resolveSingleConstraintRowGeneric(frictionConstraint);
+ leastSquaredResidual = btMax(leastSquaredResidual, residual * residual);
+
+ if (frictionConstraint.m_multiBodyA)
+ frictionConstraint.m_multiBodyA->setPosUpdated(false);
+ if (frictionConstraint.m_multiBodyB)
+ frictionConstraint.m_multiBodyB->setPosUpdated(false);
+ }
+ }
+ }
+
for (int j1 = 0; j1 < this->m_multiBodyTorsionalFrictionContactConstraints.size(); j1++)
{
if (iteration < infoGlobal.m_numIterations)
@@ -78,18 +102,29 @@ btScalar btMultiBodyConstraintSolver::solveSingleIteration(int iteration, btColl
btMultiBodySolverConstraint& frictionConstraint = m_multiBodyTorsionalFrictionContactConstraints[index];
btScalar totalImpulse = m_multiBodyNormalContactConstraints[frictionConstraint.m_frictionIndex].m_appliedImpulse;
+ j1++;
+ int index2 = j1;
+ btMultiBodySolverConstraint& frictionConstraintB = m_multiBodyTorsionalFrictionContactConstraints[index2];
//adjust friction limits here
- if (totalImpulse > btScalar(0))
+ if (totalImpulse > btScalar(0) && frictionConstraint.m_frictionIndex == frictionConstraintB.m_frictionIndex)
{
frictionConstraint.m_lowerLimit = -(frictionConstraint.m_friction * totalImpulse);
frictionConstraint.m_upperLimit = frictionConstraint.m_friction * totalImpulse;
- btScalar residual = resolveSingleConstraintRowGeneric(frictionConstraint);
+ frictionConstraintB.m_lowerLimit = -(frictionConstraintB.m_friction * totalImpulse);
+ frictionConstraintB.m_upperLimit = frictionConstraintB.m_friction * totalImpulse;
+
+ btScalar residual = resolveConeFrictionConstraintRows(frictionConstraint, frictionConstraintB);
leastSquaredResidual = btMax(leastSquaredResidual, residual * residual);
if (frictionConstraint.m_multiBodyA)
frictionConstraint.m_multiBodyA->setPosUpdated(false);
if (frictionConstraint.m_multiBodyB)
frictionConstraint.m_multiBodyB->setPosUpdated(false);
+
+ if (frictionConstraintB.m_multiBodyA)
+ frictionConstraintB.m_multiBodyA->setPosUpdated(false);
+ if (frictionConstraintB.m_multiBodyB)
+ frictionConstraintB.m_multiBodyB->setPosUpdated(false);
}
}
}
@@ -164,6 +199,7 @@ btScalar btMultiBodyConstraintSolver::solveGroupCacheFriendlySetup(btCollisionOb
m_multiBodyNormalContactConstraints.resize(0);
m_multiBodyFrictionContactConstraints.resize(0);
m_multiBodyTorsionalFrictionContactConstraints.resize(0);
+ m_multiBodySpinningFrictionContactConstraints.resize(0);
m_data.m_jacobians.resize(0);
m_data.m_deltaVelocitiesUnitImpulse.resize(0);
@@ -1169,6 +1205,43 @@ btMultiBodySolverConstraint& btMultiBodyConstraintSolver::addMultiBodyTorsionalF
return solverConstraint;
}
+btMultiBodySolverConstraint& btMultiBodyConstraintSolver::addMultiBodySpinningFrictionConstraint(const btVector3& normalAxis, btPersistentManifold* manifold, int frictionIndex, btManifoldPoint& cp,
+ btScalar combinedTorsionalFriction,
+ btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip)
+{
+ BT_PROFILE("addMultiBodyRollingFrictionConstraint");
+
+ btMultiBodySolverConstraint& solverConstraint = m_multiBodySpinningFrictionContactConstraints.expandNonInitializing();
+ solverConstraint.m_orgConstraint = 0;
+ solverConstraint.m_orgDofIndex = -1;
+
+ solverConstraint.m_frictionIndex = frictionIndex;
+ bool isFriction = true;
+
+ const btMultiBodyLinkCollider* fcA = btMultiBodyLinkCollider::upcast(manifold->getBody0());
+ const btMultiBodyLinkCollider* fcB = btMultiBodyLinkCollider::upcast(manifold->getBody1());
+
+ btMultiBody* mbA = fcA ? fcA->m_multiBody : 0;
+ btMultiBody* mbB = fcB ? fcB->m_multiBody : 0;
+
+ int solverBodyIdA = mbA ? -1 : getOrInitSolverBody(*colObj0, infoGlobal.m_timeStep);
+ int solverBodyIdB = mbB ? -1 : getOrInitSolverBody(*colObj1, infoGlobal.m_timeStep);
+
+ solverConstraint.m_solverBodyIdA = solverBodyIdA;
+ solverConstraint.m_solverBodyIdB = solverBodyIdB;
+ solverConstraint.m_multiBodyA = mbA;
+ if (mbA)
+ solverConstraint.m_linkA = fcA->m_link;
+
+ solverConstraint.m_multiBodyB = mbB;
+ if (mbB)
+ solverConstraint.m_linkB = fcB->m_link;
+
+ solverConstraint.m_originalContactPoint = &cp;
+
+ setupMultiBodyTorsionalFrictionConstraint(solverConstraint, normalAxis, cp, combinedTorsionalFriction, infoGlobal, relaxation, isFriction, desiredVelocity, cfmSlip);
+ return solverConstraint;
+}
void btMultiBodyConstraintSolver::convertMultiBodyContact(btPersistentManifold* manifold, const btContactSolverInfo& infoGlobal)
{
const btMultiBodyLinkCollider* fcA = btMultiBodyLinkCollider::upcast(manifold->getBody0());
@@ -1258,7 +1331,7 @@ void btMultiBodyConstraintSolver::convertMultiBodyContact(btPersistentManifold*
{
if (cp.m_combinedSpinningFriction > 0)
{
- addMultiBodyTorsionalFrictionConstraint(cp.m_normalWorldOnB, manifold, frictionIndex, cp, cp.m_combinedSpinningFriction, colObj0, colObj1, relaxation, infoGlobal);
+ addMultiBodySpinningFrictionConstraint(cp.m_normalWorldOnB, manifold, frictionIndex, cp, cp.m_combinedSpinningFriction, colObj0, colObj1, relaxation, infoGlobal);
}
if (cp.m_combinedRollingFriction > 0)
{
@@ -1267,11 +1340,8 @@ void btMultiBodyConstraintSolver::convertMultiBodyContact(btPersistentManifold*
applyAnisotropicFriction(colObj0, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
applyAnisotropicFriction(colObj1, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION);
- if (cp.m_lateralFrictionDir1.length() > 0.001)
- addMultiBodyTorsionalFrictionConstraint(cp.m_lateralFrictionDir1, manifold, frictionIndex, cp, cp.m_combinedRollingFriction, colObj0, colObj1, relaxation, infoGlobal);
-
- if (cp.m_lateralFrictionDir2.length() > 0.001)
- addMultiBodyTorsionalFrictionConstraint(cp.m_lateralFrictionDir2, manifold, frictionIndex, cp, cp.m_combinedRollingFriction, colObj0, colObj1, relaxation, infoGlobal);
+ addMultiBodyTorsionalFrictionConstraint(cp.m_lateralFrictionDir1, manifold, frictionIndex, cp, cp.m_combinedRollingFriction, colObj0, colObj1, relaxation, infoGlobal);
+ addMultiBodyTorsionalFrictionConstraint(cp.m_lateralFrictionDir2, manifold, frictionIndex, cp, cp.m_combinedRollingFriction, colObj0, colObj1, relaxation, infoGlobal);
}
rollingFriction--;
}
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h
index f39f2879fc..abf5718839 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h
@@ -34,6 +34,7 @@ protected:
btMultiBodyConstraintArray m_multiBodyNormalContactConstraints;
btMultiBodyConstraintArray m_multiBodyFrictionContactConstraints;
btMultiBodyConstraintArray m_multiBodyTorsionalFrictionContactConstraints;
+ btMultiBodyConstraintArray m_multiBodySpinningFrictionContactConstraints;
btMultiBodyJacobianData m_data;
@@ -54,6 +55,10 @@ protected:
btScalar combinedTorsionalFriction,
btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity = 0, btScalar cfmSlip = 0);
+ btMultiBodySolverConstraint& addMultiBodySpinningFrictionConstraint(const btVector3& normalAxis, btPersistentManifold* manifold, int frictionIndex, btManifoldPoint& cp,
+ btScalar combinedTorsionalFriction,
+ btCollisionObject* colObj0, btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity = 0, btScalar cfmSlip = 0);
+
void setupMultiBodyJointLimitConstraint(btMultiBodySolverConstraint & constraintRow,
btScalar * jacA, btScalar * jacB,
btScalar penetration, btScalar combinedFrictionCoeff, btScalar combinedRestitutionCoeff,
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp
index 1557987bc3..1131e5378c 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp
@@ -207,6 +207,7 @@ public:
}
};
+
struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::IslandCallback
{
btContactSolverInfo* m_solverInfo;
@@ -224,6 +225,8 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::
btAlignedObjectArray<btTypedConstraint*> m_constraints;
btAlignedObjectArray<btMultiBodyConstraint*> m_multiBodyConstraints;
+ btAlignedObjectArray<btSolverAnalyticsData> m_islandAnalyticsData;
+
MultiBodyInplaceSolverIslandCallback(btMultiBodyConstraintSolver* solver,
btDispatcher* dispatcher)
: m_solverInfo(NULL),
@@ -235,7 +238,7 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::
{
}
- MultiBodyInplaceSolverIslandCallback& operator=(MultiBodyInplaceSolverIslandCallback& other)
+ MultiBodyInplaceSolverIslandCallback& operator=(const MultiBodyInplaceSolverIslandCallback& other)
{
btAssert(0);
(void)other;
@@ -244,6 +247,7 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::
SIMD_FORCE_INLINE void setup(btContactSolverInfo* solverInfo, btTypedConstraint** sortedConstraints, int numConstraints, btMultiBodyConstraint** sortedMultiBodyConstraints, int numMultiBodyConstraints, btIDebugDraw* debugDrawer)
{
+ m_islandAnalyticsData.clear();
btAssert(solverInfo);
m_solverInfo = solverInfo;
@@ -270,6 +274,11 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::
{
///we don't split islands, so all constraints/contact manifolds/bodies are passed into the solver regardless the island id
m_solver->solveMultiBodyGroup(bodies, numBodies, manifolds, numManifolds, m_sortedConstraints, m_numConstraints, &m_multiBodySortedConstraints[0], m_numConstraints, *m_solverInfo, m_debugDrawer, m_dispatcher);
+ if (m_solverInfo->m_reportSolverAnalytics&1)
+ {
+ m_solver->m_analyticsData.m_islandId = islandId;
+ m_islandAnalyticsData.push_back(m_solver->m_analyticsData);
+ }
}
else
{
@@ -335,7 +344,7 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::
if ((m_multiBodyConstraints.size() + m_constraints.size() + m_manifolds.size()) > m_solverInfo->m_minimumSolverBatchSize)
{
- processConstraints();
+ processConstraints(islandId);
}
else
{
@@ -344,7 +353,7 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::
}
}
}
- void processConstraints()
+ void processConstraints(int islandId=-1)
{
btCollisionObject** bodies = m_bodies.size() ? &m_bodies[0] : 0;
btPersistentManifold** manifold = m_manifolds.size() ? &m_manifolds[0] : 0;
@@ -354,6 +363,11 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::
//printf("mb contacts = %d, mb constraints = %d\n", mbContacts, m_multiBodyConstraints.size());
m_solver->solveMultiBodyGroup(bodies, m_bodies.size(), manifold, m_manifolds.size(), constraints, m_constraints.size(), multiBodyConstraints, m_multiBodyConstraints.size(), *m_solverInfo, m_debugDrawer, m_dispatcher);
+ if (m_bodies.size() && (m_solverInfo->m_reportSolverAnalytics&1))
+ {
+ m_solver->m_analyticsData.m_islandId = islandId;
+ m_islandAnalyticsData.push_back(m_solver->m_analyticsData);
+ }
m_bodies.resize(0);
m_manifolds.resize(0);
m_constraints.resize(0);
@@ -361,6 +375,11 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::
}
};
+void btMultiBodyDynamicsWorld::getAnalyticsData(btAlignedObjectArray<btSolverAnalyticsData>& islandAnalyticsData) const
+{
+ islandAnalyticsData = m_solverMultiBodyIslandCallback->m_islandAnalyticsData;
+}
+
btMultiBodyDynamicsWorld::btMultiBodyDynamicsWorld(btDispatcher* dispatcher, btBroadphaseInterface* pairCache, btMultiBodyConstraintSolver* constraintSolver, btCollisionConfiguration* collisionConfiguration)
: btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration),
m_multiBodyConstraintSolver(constraintSolver)
@@ -717,13 +736,17 @@ void btMultiBodyDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo)
m_scratch_v.resize(bod->getNumLinks() + 1);
m_scratch_m.resize(bod->getNumLinks() + 1);
+ if (bod->internalNeedsJointFeedback())
{
if (!bod->isUsingRK4Integration())
{
- bool isConstraintPass = true;
- bod->computeAccelerationsArticulatedBodyAlgorithmMultiDof(solverInfo.m_timeStep, m_scratch_r, m_scratch_v, m_scratch_m, isConstraintPass,
- getSolverInfo().m_jointFeedbackInWorldSpace,
- getSolverInfo().m_jointFeedbackInJointFrame);
+ if (bod->internalNeedsJointFeedback())
+ {
+ bool isConstraintPass = true;
+ bod->computeAccelerationsArticulatedBodyAlgorithmMultiDof(solverInfo.m_timeStep, m_scratch_r, m_scratch_v, m_scratch_m, isConstraintPass,
+ getSolverInfo().m_jointFeedbackInWorldSpace,
+ getSolverInfo().m_jointFeedbackInJointFrame);
+ }
}
}
}
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h
index 641238f3bb..e36c2f7aad 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h
@@ -109,5 +109,7 @@ public:
virtual void serialize(btSerializer* serializer);
virtual void setMultiBodyConstraintSolver(btMultiBodyConstraintSolver* solver);
virtual void setConstraintSolver(btConstraintSolver* solver);
+ virtual void getAnalyticsData(btAlignedObjectArray<struct btSolverAnalyticsData>& m_islandAnalyticsData) const;
+
};
#endif //BT_MULTIBODY_DYNAMICS_WORLD_H
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h
index f91c001f12..bc909990c2 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h
@@ -36,6 +36,10 @@ public:
btMultiBody* m_multiBody;
int m_link;
+ virtual ~btMultiBodyLinkCollider()
+ {
+
+ }
btMultiBodyLinkCollider(btMultiBody* multiBody, int link)
: m_multiBody(multiBody),
m_link(link)
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp
index 338e8af0ab..f2186a93e9 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp
@@ -22,9 +22,9 @@ subject to the following restrictions:
#define DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS
-static bool interleaveContactAndFriction = false;
+static bool interleaveContactAndFriction1 = false;
-struct btJointNode
+struct btJointNode1
{
int jointIndex; // pointer to enclosing dxJoint object
int otherBodyIndex; // *other* body this joint is connected to
@@ -241,7 +241,7 @@ void btMultiBodyMLCPConstraintSolver::createMLCPFast(const btContactSolverInfo&
void btMultiBodyMLCPConstraintSolver::createMLCPFastRigidBody(const btContactSolverInfo& infoGlobal)
{
- int numContactRows = interleaveContactAndFriction ? 3 : 1;
+ int numContactRows = interleaveContactAndFriction1 ? 3 : 1;
int numConstraintRows = m_allConstraintPtrArray.size();
@@ -301,7 +301,7 @@ void btMultiBodyMLCPConstraintSolver::createMLCPFastRigidBody(const btContactSol
BT_PROFILE("bodyJointNodeArray.resize");
bodyJointNodeArray.resize(numBodies, -1);
}
- btAlignedObjectArray<btJointNode> jointNodeArray;
+ btAlignedObjectArray<btJointNode1> jointNodeArray;
{
BT_PROFILE("jointNodeArray.reserve");
jointNodeArray.reserve(2 * m_allConstraintPtrArray.size());
@@ -729,7 +729,7 @@ btScalar btMultiBodyMLCPConstraintSolver::solveGroupCacheFriendlySetup(
int firstContactConstraintOffset = dindex;
// The btSequentialImpulseConstraintSolver moves all friction constraints at the very end, we can also interleave them instead
- if (interleaveContactAndFriction)
+ if (interleaveContactAndFriction1)
{
for (int i = 0; i < m_tmpSolverContactConstraintPool.size(); i++)
{
@@ -785,7 +785,7 @@ btScalar btMultiBodyMLCPConstraintSolver::solveGroupCacheFriendlySetup(
firstContactConstraintOffset = dindex;
// The btSequentialImpulseConstraintSolver moves all friction constraints at the very end, we can also interleave them instead
- if (interleaveContactAndFriction)
+ if (interleaveContactAndFriction1)
{
for (int i = 0; i < m_multiBodyNormalContactConstraints.size(); ++i)
{
diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h
index 6be36ba142..77fdb86bb9 100644
--- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h
+++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h
@@ -156,7 +156,7 @@ protected:
btTypedConstraint** constraints,
int numConstraints,
const btContactSolverInfo& infoGlobal,
- btIDebugDraw* debugDrawer) BT_OVERRIDE;
+ btIDebugDraw* debugDrawer) ;
public:
BT_DECLARE_ALIGNED_ALLOCATOR()
diff --git a/thirdparty/bullet/BulletDynamics/MLCPSolvers/btLemkeSolver.h b/thirdparty/bullet/BulletDynamics/MLCPSolvers/btLemkeSolver.h
index 3f215e56bb..ac2fc46ab0 100644
--- a/thirdparty/bullet/BulletDynamics/MLCPSolvers/btLemkeSolver.h
+++ b/thirdparty/bullet/BulletDynamics/MLCPSolvers/btLemkeSolver.h
@@ -20,7 +20,7 @@ subject to the following restrictions:
#include "btMLCPSolverInterface.h"
#include "btLemkeAlgorithm.h"
-///The btLemkeSolver is based on "Fast Implementation of Lemke’s Algorithm for Rigid Body Contact Simulation (John E. Lloyd) "
+///The btLemkeSolver is based on "Fast Implementation of Lemke's Algorithm for Rigid Body Contact Simulation (John E. Lloyd) "
///It is a slower but more accurate solver. Increase the m_maxLoops for better convergence, at the cost of more CPU time.
///The original implementation of the btLemkeAlgorithm was done by Kilian Grundl from the MBSim team
class btLemkeSolver : public btMLCPSolverInterface
@@ -67,7 +67,7 @@ public:
btMatrixXu A1;
btMatrixXu B(n, n);
{
- BT_PROFILE("inverse(slow)");
+ //BT_PROFILE("inverse(slow)");
A1.resize(A.rows(), A.cols());
for (int row = 0; row < A.rows(); row++)
{
@@ -174,7 +174,7 @@ public:
y1.resize(n, 1);
btLemkeAlgorithm lemke(M, qq, m_debugLevel);
{
- BT_PROFILE("lemke.solve");
+ //BT_PROFILE("lemke.solve");
lemke.setSystem(M, qq);
z1 = lemke.solve(m_maxLoops);
}
diff --git a/thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp b/thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp
index f150b5ae4c..9326b0d098 100644
--- a/thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp
+++ b/thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp
@@ -349,7 +349,7 @@ int MultiBodyTree::finalize()
const int &num_bodies = m_init_cache->numBodies();
const int &num_dofs = m_init_cache->numDoFs();
- if (num_dofs <= 0)
+ if (num_dofs < 0)
{
bt_id_error_message("Need num_dofs>=1, but num_dofs= %d\n", num_dofs);
//return -1;
diff --git a/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp b/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp
index befbc2e2a4..ec9a562295 100644
--- a/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp
+++ b/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp
@@ -479,9 +479,17 @@ int MultiBodyTree::MultiBodyImpl::calculateKinematics(const vecx &q, const vecx
//todo: review
RigidBody &body = m_body_list[m_body_spherical_list[i]];
- body.m_body_T_parent = transformZ(q(body.m_q_index + 2)) *
- transformY(q(body.m_q_index + 1)) *
- transformX(q(body.m_q_index));
+ mat33 T;
+
+ T = transformX(q(body.m_q_index)) *
+ transformY(q(body.m_q_index + 1)) *
+ transformZ(q(body.m_q_index + 2));
+ body.m_body_T_parent = T * body.m_body_T_parent_ref;
+
+ body.m_parent_pos_parent_body(0)=0;
+ body.m_parent_pos_parent_body(1)=0;
+ body.m_parent_pos_parent_body(2)=0;
+
body.m_parent_pos_parent_body = body.m_body_T_parent * body.m_parent_pos_parent_body;
if (type >= POSITION_VELOCITY)
@@ -832,6 +840,25 @@ int MultiBodyTree::MultiBodyImpl::calculateMassMatrix(const vecx &q, const bool
body.m_parent_pos_parent_body = body.m_body_T_parent * body.m_parent_pos_parent_body;
}
+
+ for (idArrayIdx i = 0; i < m_body_spherical_list.size(); i++)
+ {
+ //todo: review
+ RigidBody &body = m_body_list[m_body_spherical_list[i]];
+
+ mat33 T;
+
+ T = transformX(q(body.m_q_index)) *
+ transformY(q(body.m_q_index + 1)) *
+ transformZ(q(body.m_q_index + 2));
+ body.m_body_T_parent = T * body.m_body_T_parent_ref;
+
+ body.m_parent_pos_parent_body(0)=0;
+ body.m_parent_pos_parent_body(1)=0;
+ body.m_parent_pos_parent_body(2)=0;
+
+ body.m_parent_pos_parent_body = body.m_body_T_parent * body.m_parent_pos_parent_body;
+ }
}
for (int i = m_body_list.size() - 1; i >= 0; i--)
{
diff --git a/thirdparty/bullet/BulletSoftBody/btSoftBody.cpp b/thirdparty/bullet/BulletSoftBody/btSoftBody.cpp
index 58796a88d0..7463bdc019 100644
--- a/thirdparty/bullet/BulletSoftBody/btSoftBody.cpp
+++ b/thirdparty/bullet/BulletSoftBody/btSoftBody.cpp
@@ -518,7 +518,7 @@ void btSoftBody::addAeroForceToNode(const btVector3& windVelocity, int nodeIndex
fDrag = 0.5f * kDG * medium.m_density * rel_v2 * tri_area * n_dot_v * (-rel_v_nrm);
// Check angle of attack
- // cos(10º) = 0.98480
+ // cos(10°) = 0.98480
if (0 < n_dot_v && n_dot_v < 0.98480f)
fLift = 0.5f * kLF * medium.m_density * rel_v_len * tri_area * btSqrt(1.0f - n_dot_v * n_dot_v) * (nrm.cross(rel_v_nrm).cross(rel_v_nrm));
@@ -604,7 +604,7 @@ void btSoftBody::addAeroForceToFace(const btVector3& windVelocity, int faceIndex
fDrag = 0.5f * kDG * medium.m_density * rel_v2 * tri_area * n_dot_v * (-rel_v_nrm);
// Check angle of attack
- // cos(10º) = 0.98480
+ // cos(10°) = 0.98480
if (0 < n_dot_v && n_dot_v < 0.98480f)
fLift = 0.5f * kLF * medium.m_density * rel_v_len * tri_area * btSqrt(1.0f - n_dot_v * n_dot_v) * (nrm.cross(rel_v_nrm).cross(rel_v_nrm));
diff --git a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportPosix.cpp b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportPosix.cpp
index 02f4ed1631..a03f6dc570 100644
--- a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportPosix.cpp
+++ b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportPosix.cpp
@@ -45,14 +45,14 @@ subject to the following restrictions:
int btGetNumHardwareThreads()
{
- return btMin<int>(BT_MAX_THREAD_COUNT, std::thread::hardware_concurrency());
+ return btMax(1u, btMin(BT_MAX_THREAD_COUNT, std::thread::hardware_concurrency()));
}
#else
int btGetNumHardwareThreads()
{
- return btMin<int>(BT_MAX_THREAD_COUNT, sysconf(_SC_NPROCESSORS_ONLN));
+ return btMax(1, btMin<int>(BT_MAX_THREAD_COUNT, sysconf(_SC_NPROCESSORS_ONLN)));
}
#endif
@@ -304,8 +304,8 @@ void btThreadSupportPosix::stopThreads()
checkPThreadFunction(sem_post(threadStatus.startSemaphore));
checkPThreadFunction(sem_wait(m_mainSemaphore));
- destroySem(threadStatus.startSemaphore);
checkPThreadFunction(pthread_join(threadStatus.thread, 0));
+ destroySem(threadStatus.startSemaphore);
}
destroySem(m_mainSemaphore);
m_activeThreadStatus.clear();
diff --git a/thirdparty/bullet/LinearMath/btAlignedObjectArray.h b/thirdparty/bullet/LinearMath/btAlignedObjectArray.h
index b4671bc19f..b3d5d64b58 100644
--- a/thirdparty/bullet/LinearMath/btAlignedObjectArray.h
+++ b/thirdparty/bullet/LinearMath/btAlignedObjectArray.h
@@ -38,13 +38,6 @@ subject to the following restrictions:
#include <new> //for placement new
#endif //BT_USE_PLACEMENT_NEW
-// The register keyword is deprecated in C++11 so don't use it.
-#if __cplusplus > 199711L
-#define BT_REGISTER
-#else
-#define BT_REGISTER register
-#endif
-
///The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods
///It is developed to replace stl::vector to avoid portability issues, including STL alignment issues to add SIMD/SSE data
template <typename T>
@@ -209,7 +202,7 @@ public:
SIMD_FORCE_INLINE void resize(int newsize, const T& fillData = T())
{
- const BT_REGISTER int curSize = size();
+ const int curSize = size();
if (newsize < curSize)
{
@@ -236,7 +229,7 @@ public:
}
SIMD_FORCE_INLINE T& expandNonInitializing()
{
- const BT_REGISTER int sz = size();
+ const int sz = size();
if (sz == capacity())
{
reserve(allocSize(size()));
@@ -248,7 +241,7 @@ public:
SIMD_FORCE_INLINE T& expand(const T& fillValue = T())
{
- const BT_REGISTER int sz = size();
+ const int sz = size();
if (sz == capacity())
{
reserve(allocSize(size()));
@@ -263,7 +256,7 @@ public:
SIMD_FORCE_INLINE void push_back(const T& _Val)
{
- const BT_REGISTER int sz = size();
+ const int sz = size();
if (sz == capacity())
{
reserve(allocSize(size()));
diff --git a/thirdparty/bullet/LinearMath/btMatrixX.h b/thirdparty/bullet/LinearMath/btMatrixX.h
index 9df9e49469..388c57c2d7 100644
--- a/thirdparty/bullet/LinearMath/btMatrixX.h
+++ b/thirdparty/bullet/LinearMath/btMatrixX.h
@@ -263,7 +263,10 @@ struct btMatrixX
{
{
BT_PROFILE("storage=0");
- btSetZero(&m_storage[0], m_storage.size());
+ if (m_storage.size())
+ {
+ btSetZero(&m_storage[0], m_storage.size());
+ }
//memset(&m_storage[0],0,sizeof(T)*m_storage.size());
//for (int i=0;i<m_storage.size();i++)
// m_storage[i]=0;
@@ -281,7 +284,7 @@ struct btMatrixX
}
}
- void printMatrix(const char* msg)
+ void printMatrix(const char* msg) const
{
printf("%s ---------------------\n", msg);
for (int i = 0; i < rows(); i++)
diff --git a/thirdparty/bullet/LinearMath/btScalar.h b/thirdparty/bullet/LinearMath/btScalar.h
index c198bd4b35..ba49d6700b 100644
--- a/thirdparty/bullet/LinearMath/btScalar.h
+++ b/thirdparty/bullet/LinearMath/btScalar.h
@@ -124,7 +124,7 @@ inline int btGetVersion()
#ifdef BT_DEBUG
#ifdef _MSC_VER
#include <stdio.h>
- #define btAssert(x) { if(!(x)){printf("Assert "__FILE__ ":%u (%s)\n", __LINE__, #x);__debugbreak(); }}
+ #define btAssert(x) { if(!(x)){printf("Assert " __FILE__ ":%u (%s)\n", __LINE__, #x);__debugbreak(); }}
#else//_MSC_VER
#include <assert.h>
#define btAssert assert
@@ -152,7 +152,7 @@ inline int btGetVersion()
#ifdef __SPU__
#include <spu_printf.h>
#define printf spu_printf
- #define btAssert(x) {if(!(x)){printf("Assert "__FILE__ ":%u ("#x")\n", __LINE__);spu_hcmpeq(0,0);}}
+ #define btAssert(x) {if(!(x)){printf("Assert " __FILE__ ":%u ("#x")\n", __LINE__);spu_hcmpeq(0,0);}}
#else
#define btAssert assert
#endif
diff --git a/thirdparty/bullet/LinearMath/btVector3.h b/thirdparty/bullet/LinearMath/btVector3.h
index 61fd8d1e46..d65ed9808d 100644
--- a/thirdparty/bullet/LinearMath/btVector3.h
+++ b/thirdparty/bullet/LinearMath/btVector3.h
@@ -36,7 +36,7 @@ subject to the following restrictions:
#pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255'
#endif
-#define BT_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x))
+#define BT_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff)
//#define bt_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) )
#define bt_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask))
#define bt_splat3_ps(_a, _i) bt_pshufd_ps((_a), BT_SHUFFLE(_i, _i, _i, 3))
diff --git a/thirdparty/bullet/btBulletCollisionAll.cpp b/thirdparty/bullet/btBulletCollisionAll.cpp
new file mode 100644
index 0000000000..2851fb3b73
--- /dev/null
+++ b/thirdparty/bullet/btBulletCollisionAll.cpp
@@ -0,0 +1,96 @@
+#include "BulletCollision/BroadphaseCollision/btAxisSweep3.cpp"
+#include "BulletCollision/BroadphaseCollision/btDbvt.cpp"
+#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp"
+#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp"
+#include "BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp"
+#include "BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp"
+#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp"
+#include "BulletCollision/BroadphaseCollision/btDispatcher.cpp"
+#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp"
+#include "BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp"
+#include "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp"
+#include "BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp"
+#include "BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btManifoldResult.cpp"
+#include "BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp"
+#include "BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp"
+#include "BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp"
+#include "BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btCollisionObject.cpp"
+#include "BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp"
+#include "BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btCollisionWorld.cpp"
+#include "BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp"
+#include "BulletCollision/CollisionDispatch/btUnionFind.cpp"
+#include "BulletCollision/CollisionDispatch/btCollisionWorldImporter.cpp"
+#include "BulletCollision/CollisionDispatch/btGhostObject.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btConvexCast.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp"
+#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp"
+#include "BulletCollision/CollisionShapes/btBox2dShape.cpp"
+#include "BulletCollision/CollisionShapes/btConvexPolyhedron.cpp"
+#include "BulletCollision/CollisionShapes/btShapeHull.cpp"
+#include "BulletCollision/CollisionShapes/btBoxShape.cpp"
+#include "BulletCollision/CollisionShapes/btConvexShape.cpp"
+#include "BulletCollision/CollisionShapes/btSphereShape.cpp"
+#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp"
+#include "BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp"
+#include "BulletCollision/CollisionShapes/btStaticPlaneShape.cpp"
+#include "BulletCollision/CollisionShapes/btCapsuleShape.cpp"
+#include "BulletCollision/CollisionShapes/btCylinderShape.cpp"
+#include "BulletCollision/CollisionShapes/btStridingMeshInterface.cpp"
+#include "BulletCollision/CollisionShapes/btCollisionShape.cpp"
+#include "BulletCollision/CollisionShapes/btEmptyShape.cpp"
+#include "BulletCollision/CollisionShapes/btTetrahedronShape.cpp"
+#include "BulletCollision/CollisionShapes/btCompoundShape.cpp"
+#include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp"
+#include "BulletCollision/CollisionShapes/btTriangleBuffer.cpp"
+#include "BulletCollision/CollisionShapes/btConcaveShape.cpp"
+#include "BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp"
+#include "BulletCollision/CollisionShapes/btTriangleCallback.cpp"
+#include "BulletCollision/CollisionShapes/btConeShape.cpp"
+#include "BulletCollision/CollisionShapes/btMultiSphereShape.cpp"
+#include "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp"
+#include "BulletCollision/CollisionShapes/btConvex2dShape.cpp"
+#include "BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp"
+#include "BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp"
+#include "BulletCollision/CollisionShapes/btConvexHullShape.cpp"
+#include "BulletCollision/CollisionShapes/btOptimizedBvh.cpp"
+#include "BulletCollision/CollisionShapes/btTriangleMesh.cpp"
+#include "BulletCollision/CollisionShapes/btConvexInternalShape.cpp"
+#include "BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp"
+#include "BulletCollision/CollisionShapes/btTriangleMeshShape.cpp"
+#include "BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp"
+#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp"
+#include "BulletCollision/CollisionShapes/btSdfCollisionShape.cpp"
+#include "BulletCollision/CollisionShapes/btMiniSDF.cpp"
+#include "BulletCollision/CollisionShapes/btUniformScalingShape.cpp"
+#include "BulletCollision/Gimpact/btContactProcessing.cpp"
+#include "BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp"
+#include "BulletCollision/Gimpact/btTriangleShapeEx.cpp"
+#include "BulletCollision/Gimpact/gim_memory.cpp"
+#include "BulletCollision/Gimpact/btGImpactBvh.cpp"
+#include "BulletCollision/Gimpact/btGImpactShape.cpp"
+#include "BulletCollision/Gimpact/gim_box_set.cpp"
+#include "BulletCollision/Gimpact/gim_tri_collision.cpp"
+#include "BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp"
+#include "BulletCollision/Gimpact/btGenericPoolAllocator.cpp"
+#include "BulletCollision/Gimpact/gim_contact.cpp"
diff --git a/thirdparty/bullet/btBulletDynamicsAll.cpp b/thirdparty/bullet/btBulletDynamicsAll.cpp
new file mode 100644
index 0000000000..a8069e30ae
--- /dev/null
+++ b/thirdparty/bullet/btBulletDynamicsAll.cpp
@@ -0,0 +1,42 @@
+#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp"
+#include "BulletDynamics/Dynamics/btRigidBody.cpp"
+#include "BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp"
+#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.cpp"
+#include "BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp"
+#include "BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp"
+#include "BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btSliderConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btContactConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btFixedConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btHingeConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btTypedConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btGearConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btNNCGConstraintSolver.cpp"
+#include "BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp"
+#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp"
+#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.cpp"
+#include "BulletDynamics/MLCPSolvers/btDantzigLCP.cpp"
+#include "BulletDynamics/MLCPSolvers/btLemkeAlgorithm.cpp"
+#include "BulletDynamics/MLCPSolvers/btMLCPSolver.cpp"
+#include "BulletDynamics/Featherstone/btMultiBody.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyGearConstraint.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyConstraint.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyFixedConstraint.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp"
+#include "BulletDynamics/Featherstone/btMultiBodySphericalJointMotor.cpp"
+#include "BulletDynamics/Vehicle/btRaycastVehicle.cpp"
+#include "BulletDynamics/Vehicle/btWheelInfo.cpp"
+#include "BulletDynamics/Character/btKinematicCharacterController.cpp"
+
diff --git a/thirdparty/bullet/btLinearMathAll.cpp b/thirdparty/bullet/btLinearMathAll.cpp
new file mode 100644
index 0000000000..808f412803
--- /dev/null
+++ b/thirdparty/bullet/btLinearMathAll.cpp
@@ -0,0 +1,14 @@
+#include "LinearMath/btAlignedAllocator.cpp"
+#include "LinearMath/btGeometryUtil.cpp"
+#include "LinearMath/btSerializer.cpp"
+#include "LinearMath/btVector3.cpp"
+#include "LinearMath/btConvexHull.cpp"
+#include "LinearMath/btPolarDecomposition.cpp"
+#include "LinearMath/btSerializer64.cpp"
+#include "LinearMath/btConvexHullComputer.cpp"
+#include "LinearMath/btQuickprof.cpp"
+#include "LinearMath/btThreads.cpp"
+#include "LinearMath/TaskScheduler/btTaskScheduler.cpp"
+#include "LinearMath/TaskScheduler/btThreadSupportPosix.cpp"
+#include "LinearMath/TaskScheduler/btThreadSupportWin32.cpp"
+